Skip to content

Commit

Permalink
feat(common-sdk): add schema and table enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
DominicGBauer committed Feb 29, 2024
1 parent 2780270 commit 5bca7a0
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 60 deletions.
19 changes: 6 additions & 13 deletions packages/kysely-driver/tests/setup/db.ts
Original file line number Diff line number Diff line change
@@ -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({
Expand Down
12 changes: 4 additions & 8 deletions packages/kysely-driver/tests/setup/types.ts
Original file line number Diff line number Diff line change
@@ -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<string, string, never>;
name: string;
}
export type UsersTable = Database['users'];

export type Users = Selectable<UsersTable>;
export type NewUsers = Insertable<UsersTable>;
Expand Down
2 changes: 1 addition & 1 deletion packages/kysely-driver/tests/sqlite/db.test.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
34 changes: 18 additions & 16 deletions packages/powersync-sdk-common/src/db/schema/Schema.ts
Original file line number Diff line number Diff line change
@@ -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<string, Table<any>>;
type SchemaType = Record<string, TableV2<any>>;

export type SchemaTableType<S extends SchemaType> = {
type SchemaTableType<S extends SchemaType> = {
[K in keyof S]: RowType<S[K]>;
};

export class Schema<S extends SchemaType> {
/**
* A schema is a collection of tables. It is used to define the structure of a database.
*/
export class Schema<S extends SchemaType = SchemaType> {
/*
Only available when constructing with mapped typed definition columns
*/
readonly types: SchemaTableType<S>;
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);
});
}
}
10 changes: 9 additions & 1 deletion packages/powersync-sdk-common/src/db/schema/Table.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import _ from 'lodash';
import { Column } from '../Column';
import type { Index } from './Index';
import { TableV2 } from './TableV2';

export interface TableOptions {
/**
Expand All @@ -20,7 +21,7 @@ export const DEFAULT_TABLE_OPTIONS: Partial<TableOptions> = {
localOnly: false
};

export const InvalidSQLCharacters = /[\"\'%,\.#\s\[\]]/;
export const InvalidSQLCharacters = /["'%,.#\s[\]]/;

export class Table {
protected options: TableOptions;
Expand All @@ -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 };
}
Expand Down
32 changes: 12 additions & 20 deletions packages/powersync-sdk-common/src/db/schema/TableV2.ts
Original file line number Diff line number Diff line change
@@ -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<T extends number | string | null> = {
type: ColumnType;
Expand Down Expand Up @@ -31,7 +30,7 @@ export const column = {

export type ColumnsType = Record<string, BaseColumnType<any>>;

export type RowType<T extends Table<any>> = {
export type RowType<T extends TableV2<any>> = {
id: string;
} & {
[K in keyof T['columns']]: T['columns'][K]['_template'];
Expand All @@ -42,24 +41,17 @@ export type IndexShorthand = Record<string, string[]>;
/*
Generate a new table from the columns and indexes
*/
export class Table<Columns extends ColumnsType> {
columns: Columns;
indexes: Index[];
export class TableV2<Columns extends ColumnsType = ColumnsType> {
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]) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/powersync-sdk-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

0 comments on commit 5bca7a0

Please sign in to comment.