From 5bca7a0cd6e859112d442d2f265247b14cc961d3 Mon Sep 17 00:00:00 2001 From: DominicGBauer Date: Thu, 29 Feb 2024 11:17:57 +0200 Subject: [PATCH] feat(common-sdk): add schema and table enhancements --- packages/kysely-driver/tests/setup/db.ts | 19 ++++------- packages/kysely-driver/tests/setup/types.ts | 12 +++---- .../kysely-driver/tests/sqlite/db.test.ts | 2 +- .../src/db/schema/Schema.ts | 34 ++++++++++--------- .../src/db/schema/Table.ts | 10 +++++- .../src/db/schema/TableV2.ts | 32 +++++++---------- packages/powersync-sdk-common/src/index.ts | 2 +- 7 files changed, 51 insertions(+), 60 deletions(-) diff --git a/packages/kysely-driver/tests/setup/db.ts b/packages/kysely-driver/tests/setup/db.ts index 05eb7cc7..91fb84bb 100644 --- a/packages/kysely-driver/tests/setup/db.ts +++ b/packages/kysely-driver/tests/setup/db.ts @@ -1,19 +1,12 @@ -import { - Column, - ColumnType, - Schema, - Table, - WASQLitePowerSyncDatabaseOpenFactory -} from '@journeyapps/powersync-sdk-web'; +import { Schema, TableV2, WASQLitePowerSyncDatabaseOpenFactory, column } from '@journeyapps/powersync-sdk-web'; import { wrapPowerSyncWithKysely } from '../../src/sqlite/db'; import { Database } from './types'; -const TestSchema = new Schema([ - new Table({ - name: 'users', - columns: [new Column({ name: 'name', type: ColumnType.TEXT })] - }) -]); +const users = new TableV2({ + name: column.text +}); + +export const TestSchema = new Schema({ users }); export const getPowerSyncDb = () => { const factory = new WASQLitePowerSyncDatabaseOpenFactory({ diff --git a/packages/kysely-driver/tests/setup/types.ts b/packages/kysely-driver/tests/setup/types.ts index c6c88b4e..17f5cbea 100644 --- a/packages/kysely-driver/tests/setup/types.ts +++ b/packages/kysely-driver/tests/setup/types.ts @@ -1,13 +1,9 @@ -import { ColumnType, Insertable, Selectable, Updateable } from 'kysely'; +import { Insertable, Selectable, Updateable } from 'kysely'; +import { TestSchema } from './db'; -export interface Database { - users: UsersTable; -} +export type Database = (typeof TestSchema)['types']; -export interface UsersTable { - id: ColumnType; - name: string; -} +export type UsersTable = Database['users']; export type Users = Selectable; export type NewUsers = Insertable; diff --git a/packages/kysely-driver/tests/sqlite/db.test.ts b/packages/kysely-driver/tests/sqlite/db.test.ts index cec661b1..c900e7a1 100644 --- a/packages/kysely-driver/tests/sqlite/db.test.ts +++ b/packages/kysely-driver/tests/sqlite/db.test.ts @@ -1,9 +1,9 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import * as SUT from '../../src/sqlite/db'; import { Kysely } from 'kysely'; -import { Database } from '../setup/types'; import { getPowerSyncDb } from '../setup/db'; import { AbstractPowerSyncDatabase } from '@journeyapps/powersync-sdk-common'; +import { Database } from '../setup/types'; describe('CRUD operations', () => { let powerSyncDb: AbstractPowerSyncDatabase; diff --git a/packages/powersync-sdk-common/src/db/schema/Schema.ts b/packages/powersync-sdk-common/src/db/schema/Schema.ts index 91ce46fe..5eda7c76 100644 --- a/packages/powersync-sdk-common/src/db/schema/Schema.ts +++ b/packages/powersync-sdk-common/src/db/schema/Schema.ts @@ -1,43 +1,45 @@ -import type { Table as OldTable } from './Table'; -import { RowType, Table } from './TableV2'; +import { Table as ClassicTable } from './Table'; +import { RowType, TableV2 } from './TableV2'; -export type SchemaType = Record>; +type SchemaType = Record>; -export type SchemaTableType = { +type SchemaTableType = { [K in keyof S]: RowType; }; -export class Schema { +/** + * A schema is a collection of tables. It is used to define the structure of a database. + */ +export class Schema { + /* + Only available when constructing with mapped typed definition columns + */ readonly types: SchemaTableType; readonly props: S; - constructor(public tables: OldTable[] | S) { + constructor(public tables: ClassicTable[] | S) { if (Array.isArray(tables)) { this.tables = tables; } else { this.props = tables as S; - this.tables = this.convertToTables(this.props); + this.tables = this.convertToClassicTables(this.props); } } - build() { - return new Schema(this.tables); - } - validate() { - for (const table of this.tables as OldTable[]) { + for (const table of this.tables as ClassicTable[]) { table.validate(); } } toJSON() { return { - tables: (this.tables as OldTable[]).map((t) => t.toJSON()) + tables: (this.tables as ClassicTable[]).map((t) => t.toJSON()) }; } - private convertToTables(props: S) { - return Object.entries(props).map(([name, type]) => { - return type.table(name); + private convertToClassicTables(props: S) { + return Object.entries(props).map(([name, table]) => { + return ClassicTable.createTable(name, table); }); } } diff --git a/packages/powersync-sdk-common/src/db/schema/Table.ts b/packages/powersync-sdk-common/src/db/schema/Table.ts index a9cd8715..2543337e 100644 --- a/packages/powersync-sdk-common/src/db/schema/Table.ts +++ b/packages/powersync-sdk-common/src/db/schema/Table.ts @@ -1,6 +1,7 @@ import _ from 'lodash'; import { Column } from '../Column'; import type { Index } from './Index'; +import { TableV2 } from './TableV2'; export interface TableOptions { /** @@ -20,7 +21,7 @@ export const DEFAULT_TABLE_OPTIONS: Partial = { localOnly: false }; -export const InvalidSQLCharacters = /[\"\'%,\.#\s\[\]]/; +export const InvalidSQLCharacters = /["'%,.#\s[\]]/; export class Table { protected options: TableOptions; @@ -33,6 +34,13 @@ export class Table { return new Table({ ...options, localOnly: false, insertOnly: true }); } + static createTable(name: string, table: TableV2) { + return new Table({ + name, + columns: Object.entries(table.columns).map(([name, col]) => new Column({ name, type: col.type })) + }); + } + constructor(options: TableOptions) { this.options = { ...DEFAULT_TABLE_OPTIONS, ...options }; } diff --git a/packages/powersync-sdk-common/src/db/schema/TableV2.ts b/packages/powersync-sdk-common/src/db/schema/TableV2.ts index b4d20959..2921b696 100644 --- a/packages/powersync-sdk-common/src/db/schema/TableV2.ts +++ b/packages/powersync-sdk-common/src/db/schema/TableV2.ts @@ -1,7 +1,6 @@ -import { Column, ColumnType } from '../Column'; +import { ColumnType } from '../Column'; import { Index } from './Index'; import { IndexedColumn } from './IndexedColumn'; -import { Table as OldTable } from './Table'; export type BaseColumnType = { type: ColumnType; @@ -31,7 +30,7 @@ export const column = { export type ColumnsType = Record>; -export type RowType> = { +export type RowType> = { id: string; } & { [K in keyof T['columns']]: T['columns'][K]['_template']; @@ -42,24 +41,17 @@ export type IndexShorthand = Record; /* Generate a new table from the columns and indexes */ -export class Table { - columns: Columns; - indexes: Index[]; +export class TableV2 { + constructor( + public columns: Columns, + public indexes: Index[] = [] + ) {} - constructor(columns: Columns, indexes: Index[] = []) { - this.columns = columns; - this.indexes = indexes; - } - - table(name: string) { - return new OldTable({ - name, - columns: Object.entries(this.columns).map(([name, col]) => new Column({ name: name, type: col.type })) - }); - } - - indexed(indexes: IndexShorthand) { - return new Table( + /** + * Add indexes to the table by creating a new table with the indexes added + */ + addIndexes(indexes: IndexShorthand) { + return new TableV2( this.columns, this.indexes.concat( Object.entries(indexes).map(([name, columns]) => { diff --git a/packages/powersync-sdk-common/src/index.ts b/packages/powersync-sdk-common/src/index.ts index 5ff00db9..84c1dd33 100644 --- a/packages/powersync-sdk-common/src/index.ts +++ b/packages/powersync-sdk-common/src/index.ts @@ -25,7 +25,7 @@ export * from './db/crud/SyncStatus'; export * from './db/crud/UploadQueueStatus'; export * from './db/DBAdapter'; export * from './db/Column'; -export { Table as TableV2 } from './db/schema/TableV2'; +export * from './db/schema/TableV2'; export * from './utils/BaseObserver'; export * from './utils/strings';