Skip to content

Commit

Permalink
add SchemaBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
zfben committed Feb 2, 2025
1 parent 14d286f commit ddafb6a
Show file tree
Hide file tree
Showing 5 changed files with 487 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/__tests__/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('client', () => {
})

it('query', () => {
expect(client.query('users')).toBeInstanceOf(QueryBuilder)
expect(client.query('query')).toBeInstanceOf(QueryBuilder)
})

it('quit', async () => {
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './client'
export * from './types'
export type { SchemaBuilder } from './schema-builder'
133 changes: 133 additions & 0 deletions src/schema-builder/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { type Client, createClient } from '../../client'
import { createTestingPostgres } from '../../__tests__/utils'
import { SchemaBuilder } from '..'

describe('SchemaBuilder', () => {
let client: Client

beforeAll(async () => {
client = createClient(createTestingPostgres())
})

afterAll(async () => {
await client.quit()
})

it('createTable', async () => {
const builder = new SchemaBuilder(client)

builder.createTable('creators', table => {
table.string('string')
table.number('number')
table.boolean('boolean')
table.date('date')
table.json('json')
table.jsonb('jsonb')
table.timestamp('timestamp')
table.timestamptz('timestamptz')
table.timestamps()
table.specificType('uuid_list', 'uuid[]')

table.index('string')
})

await builder.run()

const tables = await client.raw(
'SELECT * FROM information_schema.tables WHERE table_name = ?',
['creators']
)
expect(tables[0]).toMatchObject({ table_name: 'creators' })

const columns = await client.raw(
'SELECT * FROM information_schema.columns WHERE table_name = ?',
['creators']
)

for (const column of [
{ column_name: 'string', data_type: 'character varying' },
{ column_name: 'number', data_type: 'integer' },
{ column_name: 'boolean', data_type: 'boolean' },
{ column_name: 'date', data_type: 'date' },
{ column_name: 'json', data_type: 'json' },
{ column_name: 'jsonb', data_type: 'jsonb' },
{ column_name: 'timestamp', data_type: 'timestamp without time zone' },
{ column_name: 'timestamptz', data_type: 'timestamp with time zone' },
{
column_name: 'created_at',
data_type: 'timestamp without time zone',
column_default: 'now()',
},
{
column_name: 'updated_at',
data_type: 'timestamp without time zone',
column_default: 'now()',
},
{ column_name: 'uuid_list', data_type: 'ARRAY', udt_name: '_uuid' },
]) {
expect(
columns.find(c => c.column_name === column.column_name)
).toMatchObject(column)
}

const indices = await client.raw(
'SELECT * FROM pg_indexes WHERE tablename = ?',
['creators']
)

expect(
indices.find(i => i.indexname.includes('idx_creators_string'))
).toMatchObject({
schemaname: 'public',
tablename: 'creators',
indexname: 'idx_creators_string',
tablespace: null,
indexdef:
'CREATE INDEX idx_creators_string ON public.creators USING btree (string)',
})

await client.raw('DROP TABLE creators')
})

it('alterTable', async () => {
const builder = new SchemaBuilder(client)

await client.raw('DROP TABLE IF EXISTS alters')

builder.createTable('alters', table => {
table.string('string')
table.timestamps()
})

await builder.run()

builder.alterTable('alters', table => {
table.renameColumn('string', 'new_string')
table.number('number')
table.dropColumn('created_at')
table.alterColumn('updated_at', {
type: 'date',
defaultValue: 'NULL'
})
})

await builder.run()

const columns = await client.raw(
'SELECT * FROM information_schema.columns WHERE table_name = ?',
['alters']
)

for (const column of [
{ column_name: 'new_string', data_type: 'character varying' },
{ column_name: 'number', data_type: 'integer' },
]) {
expect(
columns.find(c => c.column_name === column.column_name)
).toMatchObject(column)
}

await client.raw('DROP TABLE alters')
})
})
45 changes: 45 additions & 0 deletions src/schema-builder/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Client } from "../client"
import { TableBuilder } from "./table-builder"

export class SchemaBuilder {
private client: Client
private tables: Map<string, TableBuilder> = new Map()

constructor(client: Client) {
this.client = client
}

createTable(tableName: string, callback: (table: TableBuilder) => void) {
const builder = new TableBuilder(tableName, 'create')
callback(builder)
this.tables.set(tableName, builder)
return this
}

alterTable(tableName: string, callback: (table: TableBuilder) => void) {
const builder = this.tables.get(tableName) || new TableBuilder(tableName, 'alter')
callback(builder)
this.tables.set(tableName, builder)
return this
}

dropTable(tableName: string) {
this.tables.delete(tableName)
return this
}

toSQL(): string[] {
const statements: string[] = []
for (const builder of this.tables.values()) {
statements.push(builder.toSQL())
}
return statements
}

async run() {
for (const statement of this.toSQL()) {
await this.client.raw(statement)
}
this.tables.clear()
}
}
Loading

0 comments on commit ddafb6a

Please sign in to comment.