Skip to content

Commit

Permalink
Usability improvements for Pongo shell
Browse files Browse the repository at this point in the history
Added:
- PostgreSQL connection check,
- an option to set the prettifying and log level through shell params
  • Loading branch information
oskardudycz committed Oct 8, 2024
1 parent 5c95c90 commit 00e6dc2
Show file tree
Hide file tree
Showing 11 changed files with 282 additions and 88 deletions.
10 changes: 5 additions & 5 deletions src/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@event-driven-io/pongo-core",
"version": "0.15.3",
"version": "0.16.0-alpha.5",
"description": "Pongo - Mongo with strong consistency on top of Postgres",
"type": "module",
"engines": {
Expand Down
2 changes: 1 addition & 1 deletion src/packages/dumbo/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@event-driven-io/dumbo",
"version": "0.11.1",
"version": "0.12.0-alpha.5",
"description": "Dumbo - tools for dealing with PostgreSQL",
"type": "module",
"scripts": {
Expand Down
6 changes: 5 additions & 1 deletion src/packages/dumbo/src/core/schema/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
rawSql,
singleOrNull,
sql,
tracer,
type SchemaComponent,
type SQLExecutor,
} from '..';
Expand Down Expand Up @@ -101,7 +102,10 @@ const runSQLMigration = async (
await recordMigration(execute, newMigration);
// console.log(`Migration "${newMigration.name}" applied successfully.`);
} catch (error) {
console.error(`Failed to apply migration "${migration.name}":`, error);
tracer.error('migration-error', {
migationName: migration.name,
error: error,
});
throw error;
}
};
Expand Down
21 changes: 14 additions & 7 deletions src/packages/dumbo/src/core/tracing/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { JSONSerializer } from '../serializer';
import { prettyPrintJson } from './printing';
import { prettyJson } from './printing';

export const tracer = () => {};

export type LogLevel = 'DISABLED' | 'INFO' | 'LOG' | 'WARN' | 'ERROR';

export type LogType = 'CONSOLE';

export type LogStyle = 'RAW' | 'PRETTY';

export const LogLevel = {
DISABLED: 'DISABLED' as LogLevel,
INFO: 'INFO' as LogLevel,
Expand All @@ -17,6 +13,15 @@ export const LogLevel = {
ERROR: 'ERROR' as LogLevel,
};

export type LogType = 'CONSOLE';

export type LogStyle = 'RAW' | 'PRETTY';

export const LogStyle = {
RAW: 'RAW' as LogStyle,
PRETTY: 'PRETTY' as LogStyle,
};

const shouldLog = (logLevel: LogLevel): boolean => {
const definedLogLevel = process.env.DUMBO_LOG_LEVEL ?? LogLevel.DISABLED;

Expand All @@ -37,7 +42,9 @@ const shouldLog = (logLevel: LogLevel): boolean => {

if (
definedLogLevel === LogLevel.INFO &&
[LogLevel.ERROR, LogLevel.WARN, LogLevel.INFO].includes(logLevel)
[LogLevel.ERROR, LogLevel.WARN, LogLevel.LOG, LogLevel.INFO].includes(
logLevel,
)
)
return true;

Expand All @@ -59,7 +66,7 @@ const getTraceEventFormatter =
case 'RAW':
return JSONSerializer.serialize(event);
case 'PRETTY':
return prettyPrintJson(event, true);
return prettyJson(event, { handleMultiline: true });
}
};

Expand Down
57 changes: 41 additions & 16 deletions src/packages/dumbo/src/core/tracing/printing/pretty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ const TWO_SPACES = ' ';

const COLOR_STRING = chalk.hex('#98c379'); // Soft green for strings
const COLOR_KEY = chalk.hex('#61afef'); // Muted cyan for keys
const COLOR_NUMBER = chalk.hex('#d19a66'); // Light orange for numbers
const COLOR_NUMBER_OR_DATE = chalk.hex('#d19a66'); // Light orange for numbers
const COLOR_BOOLEAN = chalk.hex('#c678dd'); // Light purple for booleans
const COLOR_NULL = chalk.hex('#c678dd'); // Light purple for null
const COLOR_NULL_OR_UNDEFINED = chalk.hex('#c678dd'); // Light purple for null
const COLOR_BRACKETS = chalk.hex('#abb2bf'); // Soft white for object and array brackets

const processString = (
Expand All @@ -31,7 +31,10 @@ const processString = (
return COLOR_STRING(`"${str}"`);
};

// Function to format and colorize JSON by traversing it
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const shouldPrint = (obj: any): boolean =>
typeof obj !== 'function' && typeof obj !== 'symbol';

const formatJson = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
obj: any,
Expand All @@ -40,13 +43,34 @@ const formatJson = (
): string => {
const indent = TWO_SPACES.repeat(indentLevel);

if (obj === null) return COLOR_NULL('null');
if (obj === null) return COLOR_NULL_OR_UNDEFINED('null');

if (obj === undefined) return COLOR_NULL_OR_UNDEFINED('undefined');

if (typeof obj === 'string')
return processString(obj, indent, handleMultiline);
if (typeof obj === 'number') return COLOR_NUMBER(String(obj));
if (typeof obj === 'number' || typeof obj === 'bigint' || obj instanceof Date)
return COLOR_NUMBER_OR_DATE(String(obj));
if (typeof obj === 'boolean') return COLOR_BOOLEAN(String(obj));

// Handle arrays
if (obj instanceof Error) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const errorObj: Record<string, any> = {};

const propNames = Object.getOwnPropertyNames(obj);

propNames.forEach((key) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
errorObj[key] = (obj as any)[key];
});

return formatJson(errorObj, indentLevel, handleMultiline);
}

if (obj instanceof Promise) {
return COLOR_STRING('Promise {pending}');
}

if (Array.isArray(obj)) {
const arrayItems = obj.map((item) =>
formatJson(item, indentLevel + 1, handleMultiline),
Expand All @@ -57,20 +81,21 @@ const formatJson = (
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const entries = Object.entries(obj).map(
([key, value]) =>
`${COLOR_KEY(`"${key}"`)}: ${formatJson(
value,
indentLevel + 1,
handleMultiline,
)}`,
const entries = Object.entries(obj).map(([key, value]) =>
shouldPrint(value)
? `${COLOR_KEY(`"${key}"`)}: ${formatJson(
value,
indentLevel + 1,
handleMultiline,
)}`
: '',
);
return `${COLOR_BRACKETS('{')}\n${indent} ${entries.join(
`,\n${indent} `,
)}\n${indent}${COLOR_BRACKETS('}')}`;
};

export const prettyPrintJson = (
export const prettyJson = (
obj: unknown,
handleMultiline: boolean = false,
): string => formatJson(obj, 0, handleMultiline);
options?: { handleMultiline?: boolean },
): string => formatJson(obj, 0, options?.handleMultiline);
16 changes: 7 additions & 9 deletions src/packages/dumbo/src/core/tracing/printing/pretty.unit.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import assert from 'assert';
import chalk from 'chalk';
import { describe, it } from 'node:test';
import { prettyPrintJson } from './pretty';
import { prettyJson } from './pretty';

// Define a basic test suite
void describe('prettyPrintJson', () => {
// Turn off chalk colorization during tests for easy comparison
chalk.level = 0;
Expand All @@ -19,7 +18,7 @@ void describe('prettyPrintJson', () => {
"age": 30
}`;

const output = prettyPrintJson(input, false); // Multiline handling off
const output = prettyJson(input, { handleMultiline: false });
assert.strictEqual(output, expectedOutput);
});

Expand All @@ -37,7 +36,7 @@ void describe('prettyPrintJson', () => {
"
}`;

const output = prettyPrintJson(input, true); // Multiline handling on
const output = prettyJson(input, { handleMultiline: true });
assert.strictEqual(output, expectedOutput);
});

Expand All @@ -64,7 +63,7 @@ void describe('prettyPrintJson', () => {
}
}`;

const output = prettyPrintJson(input, false); // Multiline handling off
const output = prettyJson(input, { handleMultiline: false });
assert.strictEqual(output, expectedOutput);
});

Expand All @@ -85,7 +84,7 @@ void describe('prettyPrintJson', () => {
"active": true
}`;

const output = prettyPrintJson(input, false); // Multiline handling off
const output = prettyJson(input, { handleMultiline: false });
assert.strictEqual(output, expectedOutput);
});

Expand All @@ -102,7 +101,7 @@ void describe('prettyPrintJson', () => {
"tags": null
}`;

const output = prettyPrintJson(input, false); // Multiline handling off
const output = prettyJson(input, { handleMultiline: false });
assert.strictEqual(output, expectedOutput);
});

Expand All @@ -121,8 +120,7 @@ void describe('prettyPrintJson', () => {
"
}`;

const output = prettyPrintJson(input, true); // Multiline handling on
console.log(output);
const output = prettyJson(input, { handleMultiline: true });
assert.strictEqual(output, expectedOutput);
});
});
44 changes: 44 additions & 0 deletions src/packages/dumbo/src/postgres/pg/connections/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,47 @@ export function nodePostgresConnection(
? nodePostgresClientConnection(options)
: nodePostgresPoolClientConnection(options);
}

export type ConnectionCheckResult =
| { successful: true }
| {
successful: false;
code: string | undefined;
errorType: 'ConnectionRefused' | 'Authentication' | 'Unknown';
error: unknown;
};

export const checkConnection = async (
connectionString: string,
): Promise<ConnectionCheckResult> => {
const client = new pg.Client({
connectionString: connectionString,
});

try {
await client.connect();
return { successful: true };
} catch (error) {
const code =
error instanceof Error &&
'code' in error &&
typeof error.code === 'string'
? error.code
: undefined;

return {
successful: false,
errorType:
code === 'ECONNREFUSED'
? 'ConnectionRefused'
: code === '28P01'
? 'Authentication'
: 'Unknown',
code,
error,
};
} finally {
// Ensure the client is closed properly if connected
await client.end();
}
};
6 changes: 3 additions & 3 deletions src/packages/dumbo/src/postgres/pg/connections/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ import pg from 'pg';
import {
createConnectionPool,
JSONSerializer,
tracer,
type ConnectionPool,
} from '../../../core';
import {
defaultPostgreSqlDatabase,
getDatabaseNameOrDefault,
} from '../../core';
import { setNodePostgresTypeParser } from '../serialization';
import {
nodePostgresConnection,
NodePostgresConnectorType,
type NodePostgresClientConnection,
type NodePostgresConnector,
type NodePostgresPoolClientConnection,
} from './connection';
import { setNodePostgresTypeParser } from '../serialization';

export type NodePostgresNativePool =
ConnectionPool<NodePostgresPoolClientConnection>;
Expand Down Expand Up @@ -291,8 +292,7 @@ export const onEndPool = async (lookupKey: string, pool: pg.Pool) => {
try {
await pool.end();
} catch (error) {
console.log(`Error while closing the connection pool: ${lookupKey}`);
console.log(error);
tracer.error('connection-closing-error', { lookupKey, error });
}
pools.delete(lookupKey);
};
Expand Down
4 changes: 2 additions & 2 deletions src/packages/pongo/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@event-driven-io/pongo",
"version": "0.15.3",
"version": "0.16.0-alpha.5",
"description": "Pongo - Mongo with strong consistency on top of Postgres",
"type": "module",
"scripts": {
Expand Down Expand Up @@ -87,7 +87,7 @@
"pongo": "./dist/cli.js"
},
"peerDependencies": {
"@event-driven-io/dumbo": "0.11.1",
"@event-driven-io/dumbo": "0.12.0-alpha.5",
"@types/mongodb": "^4.0.7",
"@types/pg": "^8.11.6",
"@types/uuid": "^10.0.0",
Expand Down
Loading

0 comments on commit 00e6dc2

Please sign in to comment.