Skip to content

Commit

Permalink
Extracted SQL builder from execution on collection to enable unit tes…
Browse files Browse the repository at this point in the history
…ting SQL generation in the future
  • Loading branch information
oskardudycz committed Jul 7, 2024
1 parent 4b33197 commit c2bb851
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 111 deletions.
113 changes: 2 additions & 111 deletions src/packages/pongo/src/postgres/client.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
import pg from 'pg';
import { v4 as uuid } from 'uuid';
import {
type DbClient,
type PongoCollection,
type PongoDeleteResult,
type PongoFilter,
type PongoInsertOneResult,
type PongoUpdate,
type PongoUpdateResult,
} from '../main';
import { executeSQL } from './execute';
import { constructFilterQuery } from './filter';
import { type DbClient } from '../main';
import { endPool, getPool } from './pool';
import { sql } from './sql';
import { buildUpdateQuery } from './update';
import { postgresCollection } from './postgresCollection';

export const postgresClient = (
connectionString: string,
Expand All @@ -27,99 +14,3 @@ export const postgresClient = (
collection: <T>(name: string) => postgresCollection<T>(name, pool),
};
};

export const postgresCollection = <T>(
collectionName: string,
pool: pg.Pool,
): PongoCollection<T> => {
const createCollection = executeSQL(
pool,
sql(
'CREATE TABLE IF NOT EXISTS %I (_id UUID PRIMARY KEY, data JSONB)',
collectionName,
),
);
return {
createCollection: async () => {
await createCollection;
},
insertOne: async (document: T): Promise<PongoInsertOneResult> => {
await createCollection;

const id = uuid();

const result = await executeSQL(
pool,
sql(
'INSERT INTO %I (_id, data) VALUES (%L, %L)',
collectionName,
id,
JSON.stringify({ ...document, _id: id }),
),
);

return result.rowCount
? { insertedId: id, acknowledged: true }
: { insertedId: null, acknowledged: false };
},
updateOne: async (
filter: PongoFilter<T>,
update: PongoUpdate<T>,
): Promise<PongoUpdateResult> => {
await createCollection;

const filterQuery = constructFilterQuery(filter);
const updateQuery = buildUpdateQuery(update);

const result = await executeSQL(
pool,
sql(
'UPDATE %I SET data = %s WHERE %s',
collectionName,
updateQuery,
filterQuery,
),
);
return result.rowCount
? { acknowledged: true, modifiedCount: result.rowCount }
: { acknowledged: false, modifiedCount: 0 };
},
deleteOne: async (filter: PongoFilter<T>): Promise<PongoDeleteResult> => {
await createCollection;

const filterQuery = constructFilterQuery(filter);
const result = await executeSQL(
pool,
sql('DELETE FROM %I WHERE %s', collectionName, filterQuery),
);
return result.rowCount
? { acknowledged: true, deletedCount: result.rowCount }
: { acknowledged: false, deletedCount: 0 };
},
findOne: async (filter: PongoFilter<T>): Promise<T | null> => {
await createCollection;

const filterQuery = constructFilterQuery(filter);
const result = await executeSQL(
pool,
sql(
'SELECT data FROM %I WHERE %s LIMIT 1',
collectionName,
filterQuery,
),
);
return (result.rows[0]?.data ?? null) as T | null;
},
find: async (filter: PongoFilter<T>): Promise<T[]> => {
await createCollection;

const filterQuery = constructFilterQuery(filter);
const result = await executeSQL(
pool,
sql('SELECT data FROM %I WHERE %s', collectionName, filterQuery),
);

return result.rows.map((row) => row.data as T);
},
};
};
132 changes: 132 additions & 0 deletions src/packages/pongo/src/postgres/postgresCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import pg from 'pg';
import { v4 as uuid } from 'uuid';
import {
type PongoCollection,
type PongoDeleteResult,
type PongoFilter,
type PongoInsertOneResult,
type PongoUpdate,
type PongoUpdateResult,
} from '../main';
import { executeSQL } from './execute';
import { constructFilterQuery } from './filter';
import { sql, type SQL } from './sql';
import { buildUpdateQuery } from './update';

export const postgresCollection = <T>(
collectionName: string,
pool: pg.Pool,
): PongoCollection<T> => {
const createCollection = executeSQL(
pool,
SQLBuilder.createCollection(collectionName),
);
return {
createCollection: async () => {
await createCollection;
},
insertOne: async (document: T): Promise<PongoInsertOneResult> => {
await createCollection;

const id = uuid();

const result = await executeSQL(
pool,
SQLBuilder.insertOne(collectionName, id, document),
);

return result.rowCount
? { insertedId: id, acknowledged: true }
: { insertedId: null, acknowledged: false };
},
updateOne: async (
filter: PongoFilter<T>,
update: PongoUpdate<T>,
): Promise<PongoUpdateResult> => {
await createCollection;

const result = await executeSQL(
pool,
SQLBuilder.updateOne(collectionName, filter, update),
);
return result.rowCount
? { acknowledged: true, modifiedCount: result.rowCount }
: { acknowledged: false, modifiedCount: 0 };
},
deleteOne: async (filter: PongoFilter<T>): Promise<PongoDeleteResult> => {
await createCollection;

const result = await executeSQL(
pool,
SQLBuilder.deleteOne(collectionName, filter),
);
return result.rowCount
? { acknowledged: true, deletedCount: result.rowCount }
: { acknowledged: false, deletedCount: 0 };
},
findOne: async (filter: PongoFilter<T>): Promise<T | null> => {
await createCollection;

const result = await executeSQL(
pool,
SQLBuilder.findOne(collectionName, filter),
);
return (result.rows[0]?.data ?? null) as T | null;
},
find: async (filter: PongoFilter<T>): Promise<T[]> => {
await createCollection;

const result = await executeSQL(
pool,
SQLBuilder.find(collectionName, filter),
);
return result.rows.map((row) => row.data as T);
},
};
};

export const SQLBuilder = {
createCollection: (collectionName: string): SQL =>
sql(
'CREATE TABLE IF NOT EXISTS %I (_id UUID PRIMARY KEY, data JSONB)',
collectionName,
),
insertOne: <T>(collectionName: string, id: string, document: T): SQL =>
sql(
'INSERT INTO %I (_id, data) VALUES (%L, %L)',
collectionName,
id,
JSON.stringify({ ...document, _id: id }),
),
updateOne: <T>(
collectionName: string,
filter: PongoFilter<T>,
update: PongoUpdate<T>,
): SQL => {
const filterQuery = constructFilterQuery(filter);
const updateQuery = buildUpdateQuery(update);

return sql(
'UPDATE %I SET data = %s WHERE %s',
collectionName,
updateQuery,
filterQuery,
);
},
deleteOne: <T>(collectionName: string, filter: PongoFilter<T>): SQL => {
const filterQuery = constructFilterQuery(filter);
return sql('DELETE FROM %I WHERE %s', collectionName, filterQuery);
},
findOne: <T>(collectionName: string, filter: PongoFilter<T>): SQL => {
const filterQuery = constructFilterQuery(filter);
return sql(
'SELECT data FROM %I WHERE %s LIMIT 1',
collectionName,
filterQuery,
);
},
find: <T>(collectionName: string, filter: PongoFilter<T>): SQL => {
const filterQuery = constructFilterQuery(filter);
return sql('SELECT data FROM %I WHERE %s', collectionName, filterQuery);
},
};

0 comments on commit c2bb851

Please sign in to comment.