Skip to content

Commit

Permalink
Merge pull request #53 from brandon-schabel/improved-sqlite
Browse files Browse the repository at this point in the history
improved sqlite table generation config and improved unit testing
  • Loading branch information
brandon-schabel authored Nov 5, 2023
2 parents c6aabdd + 4259ea0 commit ad0e947
Show file tree
Hide file tree
Showing 23 changed files with 755 additions and 256 deletions.
1 change: 0 additions & 1 deletion jwt-tokens.json

This file was deleted.

2 changes: 0 additions & 2 deletions modules/server/server-example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,11 @@ const middleware = {
const routes = {
"/": {
GET: (req, mid) => {
console.log({ mid });
return new Response(`Hello World! ${mid?.time?.timestamp}`);
},
},
"/random": {
GET: (req, mid) => {
console.log({ mid });
return new Response(`Hello World! ${mid?.time?.timestamp}`);
},
},
Expand Down
6 changes: 3 additions & 3 deletions modules/sqlite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ Next, define your schema and use `sqliteTableFactory` to generate your table.

```javascript
const userSchema = {
id: "TEXT",
name: "TEXT",
email: "TEXT",
id: { type: "TEXT" },
name: { type: "TEXT" },
email: { type: "TEXT" },
}

const db = new Database({filename: "./mydb.sqlite"})
Expand Down
2 changes: 1 addition & 1 deletion modules/sqlite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export {
readItems,
updateItem
} from "./sqlite-utils/crud-fn-utils";
export { createTableQuery } from "./sqlite-utils/table-query-string";
export { createTableQuery } from "./sqlite-utils/table-query-gen";

14 changes: 7 additions & 7 deletions modules/sqlite/sqlite-factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { SchemaMap, createSqliteFactory } from "./sqlite-factory";
let db = new Database(":memory:");

const noteSchema = {
id: "TEXT",
text: "TEXT",
id: { type: "TEXT" },
text: { type: "TEXT" },
} satisfies SchemaMap;

describe("createSqliteFactory", () => {
Expand All @@ -33,7 +33,7 @@ describe("createSqliteFactory", () => {
text: "some text",
});

const notes = notesTable.read();
const notes = notesTable.readAll();

expect(notes).toEqual([{ id: "1", text: "some text" }]);
});
Expand All @@ -51,15 +51,15 @@ describe("createSqliteFactory", () => {
text: "some text",
});

const notes = notesTable.read();
const notes = notesTable.readAll();

expect(notes).toEqual([{ id: "1", text: "some text" }]);

notesTable.update("1", {
text: "some text updated",
});

const updatedNotes = notesTable.read();
const updatedNotes = notesTable.readAll();

expect(updatedNotes).toEqual([{ id: "1", text: "some text updated" }]);
});
Expand All @@ -77,13 +77,13 @@ describe("createSqliteFactory", () => {
text: "some text",
});

const notes = notesTable.read();
const notes = notesTable.readAll();

expect(notes).toEqual([{ id: "1", text: "some text" }]);

notesTable.deleteById("1");

const updatedNotes = notesTable.read();
const updatedNotes = notesTable.readAll();

expect(updatedNotes).toEqual([]);
});
Expand Down
88 changes: 42 additions & 46 deletions modules/sqlite/sqlite-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,6 @@ import {
sqliteTableFactory,
} from "./sqlite-table-factory";

export type CreateSqliteFactory<Schema extends SchemaMap> = {
create: (item: SQLiteSchemaToTypeScript<Schema>) => Promise<void>;
read: () => Promise<SQLiteSchemaToTypeScript<Schema>[]>;
update: (
id: number,
item: Partial<SQLiteSchemaToTypeScript<Schema>>
) => Promise<void>;
deleteById: (id: number) => Promise<void>;
};

type CreateSqliteFactoryParams = {
db: Database;
debug?: boolean;
Expand All @@ -25,7 +15,7 @@ type DBTableFactoryParams<Schema extends SchemaMap> = Omit<
SqliteTableFactoryParams<Schema>,
"db"
> & {
debug: boolean;
debug?: boolean;
};

// Mapping of SQLite types to TypeScript types.
Expand All @@ -38,51 +28,36 @@ export type SQLiteToTypeScriptTypes = {
DATE: Date;
};

export type SchemaKeys = keyof SQLiteToTypeScriptTypes;
export type SQLiteDataTypes = keyof SQLiteToTypeScriptTypes;

export type SchemaMap = Partial<Record<string, SchemaKeys>>;

export const createTableSchema = <Schema extends SchemaMap>(
schema: Schema
): string => {
return undefined as any as string;
export type FieldDefinition = {
type: SQLiteDataTypes;
primaryKey?: boolean;
unique?: boolean;
foreignKey?: string;
required?: boolean;
defaultValue?: string | number;
};

// Mapped type that takes a schema with SQLite types and returns a schema with TypeScript types.
export type SQLiteSchemaToTypeScript<T extends SchemaMap> = {
[K in keyof T]: T[K] extends SchemaKeys
? SQLiteToTypeScriptTypes[T[K]]
export type SQLiteSchemaInfer<T extends SchemaMap> = {
[K in keyof T]: T[K] extends FieldDefinition
? SQLiteToTypeScriptTypes[T[K]["type"]]
: never;
};

// Example usage.
const sqlitePersonTableSchema = {
id: "TEXT",
age: "INTEGER",
name: "TEXT",
createdAt: "DATE",
} satisfies SchemaMap;
export type SchemaMap = Partial<Record<string, FieldDefinition>>;

export const getType = <T extends SchemaMap>(
schema: T
): SQLiteSchemaToTypeScript<T> => {
return undefined as any as SQLiteSchemaToTypeScript<T>;
export const createTableSchema = <Schema extends SchemaMap>(
schema: Schema
): string => {
return undefined as any as string;
};

type Person = SQLiteSchemaToTypeScript<typeof sqlitePersonTableSchema>;
// => schema;

// TODO implement into the sqlite factory
type PersonTableSchema = SQLiteSchemaToTypeScript<
typeof sqlitePersonTableSchema
>;

// This should now have the correct types.
let person: PersonTableSchema = {
id: "some-id",
age: 42,
name: "some-name",
createdAt: new Date(),
export const getType = <T extends SchemaMap>(
schema: T
): SQLiteSchemaInfer<T> => {
return undefined as any as SQLiteSchemaInfer<T>;
};

export function createSqliteFactory({
Expand Down Expand Up @@ -118,3 +93,24 @@ export function createSqliteFactory({

return { dbTableFactory };
}

// This should now have the correct types.
// let person: PersonTableSchema = {
// id: "some-id",
// age: 42,
// name: "some-name",
// createdAt: new Date(),
// };

// // example
// const sqlitePersonTableSchema = {
// id: { type: "TEXT" },
// age: { type: "INTEGER" },
// name: { type: "TEXT" },
// createdAt: { type: "DATE" },
// } satisfies SchemaMap;

// type Person = SQLiteSchemaInfer<typeof sqlitePersonTableSchema>;

// // TODO implement into the sqlite factory
// type PersonTableSchema = SQLiteSchemaInfer<typeof sqlitePersonTableSchema>;
14 changes: 7 additions & 7 deletions modules/sqlite/sqlite-table-factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { sqliteTableFactory } from "./sqlite-table-factory";

const mockDb = new Database(":memory:");
const testSchema = {
id: "TEXT",
name: "TEXT",
age: "INTEGER",
id: { type: "TEXT" },
name: { type: "TEXT" },
age: { type: "INTEGER" },
} satisfies SchemaMap;

const factoryOptions = {
Expand All @@ -32,7 +32,7 @@ describe("sqliteTableFactory", () => {
test("should insert an item into the database using the factory", () => {
const item = { id: "1", name: "John", age: 30 };
factory.create(item);
const items = factory.read();
const items = factory.readAll();
expect(items.length).toBe(1);
expect(items[0]).toEqual(item);
});
Expand All @@ -42,7 +42,7 @@ describe("sqliteTableFactory", () => {
mockDb
.query("INSERT INTO test (id, name, age) VALUES (?, ?, ?)")
.run(item.id, item.name, item.age);
const items = factory.read();
const items = factory.readAll();
expect(items.length).toBe(1);
expect(items[0]).toEqual(item);
});
Expand All @@ -54,7 +54,7 @@ describe("sqliteTableFactory", () => {
.run(item.id, item.name, item.age);
const updatedName = "John Doe";
factory.update(item.id, { name: updatedName });
const items = factory.read();
const items = factory.readAll();
expect(items.length).toBe(1);
expect(items[0]).toEqual({ ...item, name: updatedName });
});
Expand All @@ -64,7 +64,7 @@ describe("sqliteTableFactory", () => {
.query("INSERT INTO test (id, name, age) VALUES (?, ?, ?)")
.run(item.id, item.name, item.age);
factory.deleteById(item.id);
const items = factory.read();
const items = factory.readAll();
expect(items.length).toBe(0);
});
});
66 changes: 28 additions & 38 deletions modules/sqlite/sqlite-table-factory.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import Database from "bun:sqlite";
import {
CreateSqliteFactory,
SQLiteSchemaToTypeScript,
SchemaMap,
} from "./sqlite-factory";
import { SQLiteSchemaInfer, SchemaMap } from "./sqlite-factory";
import {
createItem,
deleteItemById,
readItemById,
readItems,
readItemsWhere,
updateItem,
} from "./sqlite-utils/crud-fn-utils";
import { createTableQuery } from "./sqlite-utils/table-query-string";
import { createTableQuery } from "./sqlite-utils/table-query-gen";

export type ForeignKeysT<Schema> =
| { column: keyof Schema; references: string }[]
Expand Down Expand Up @@ -39,47 +37,39 @@ function logger(debug: boolean) {

export function sqliteTableFactory<
Schema extends SchemaMap,
TranslatedSchema extends SQLiteSchemaToTypeScript<Schema> = SQLiteSchemaToTypeScript<Schema>
TranslatedSchema extends SQLiteSchemaInfer<Schema> = SQLiteSchemaInfer<Schema>
>(
params: SqliteTableFactoryParams<Schema>,
options: SqliteTableOptions<Schema> = {}
) {
const { db, schema, tableName } = params;
const { debug = false, foreignKeys = null } = options;
const { debug = false } = options;

const log = logger(debug);

db.query(createTableQuery({ tableName, schema, foreignKeys, debug })).run();

// Pass necessary context to external CRUD functions
function create(item: TranslatedSchema) {
return createItem<Schema>(db, tableName, log, item);
}

function read(): TranslatedSchema[] {
return readItems<Schema>(db, tableName, log) as TranslatedSchema[];
}

function update(
id: string | number,
item: Partial<Omit<TranslatedSchema, "id">>
) {
updateItem<Schema>(db, tableName, log, id, item);
}

function deleteById(id: number| string) {
deleteItemById(db, tableName, log, id);
}

function infer(): CreateSqliteFactory<Schema> {
return undefined as any as CreateSqliteFactory<Schema>;
}
db.query(createTableQuery({ tableName, schema, debug })).run();

return {
create,
read,
update,
deleteById,
infer,
readItemsWhere(where: Partial<TranslatedSchema>) {
return readItemsWhere<Schema>(db, tableName, log, where);
},
create(item: TranslatedSchema) {
return createItem<Schema>(db, tableName, log, item);
},
readAll(): TranslatedSchema[] {
return readItems<Schema>(db, tableName, log) as TranslatedSchema[];
},
readById(id: string | number) {
return readItemById<Schema>(db, tableName, log, id);
},
update(id: string | number, item: Partial<Omit<TranslatedSchema, "id">>) {
return updateItem<Schema>(db, tableName, log, id, item);
},
deleteById(id: number | string) {
return deleteItemById(db, tableName, log, id);
},
infer(): TranslatedSchema {
return undefined as any as TranslatedSchema;
},
};
}
Empty file added modules/sqlite/sqlite-types.ts
Empty file.
Loading

0 comments on commit ad0e947

Please sign in to comment.