Skip to content

Commit

Permalink
update schema and cli
Browse files Browse the repository at this point in the history
  • Loading branch information
zfben committed Feb 2, 2025
1 parent d3ad589 commit 8447ed4
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 132 deletions.
5 changes: 4 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
},
"peerDependencies": {
"postgres": "*",
"tsx": "*"
"tsx": "*",
"@faasjs/logger": "*"
},
"engines": {
"node": ">=22.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest'
import { escapeIdentifier, escapeValue } from '../escape'
import { escapeIdentifier, escapeValue } from '../utils'

describe('escapeIdentifier', () => {
it('escapes double quotes', () => {
Expand Down
64 changes: 60 additions & 4 deletions src/cli/__tests__/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,66 @@
import { describe, expect, it } from 'vitest'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { main } from '../main'
import { DATABASE_URL } from '../../__tests__/utils'

describe('cli', () => {
it('DATABASE_URL is required', async () => {
delete process.env.DATABASE_URL
beforeEach(() => {
vi.spyOn(console, 'error').mockImplementation((...args) => {
process.stderr.write(`${args.join(' ')}\n`)
})
vi.spyOn(console, 'log').mockImplementation((...args) => {
process.stdout.write(`${args.join(' ')}\n`)
})
})

it('should throw error on failed database connection', async () => {
process.env.DATABASE_URL = 'postgres://invalid'

await main()

expect(console.error).toHaveBeenCalledWith(
expect.stringContaining(
'Error connecting to database, please check your DATABASE_URL'
)
)

expect(console.error).toHaveBeenCalledWith(
expect.stringContaining(
'getaddrinfo ENOTFOUND invalid'
)
)
})

it('should log success on successful connection', async () => {
process.env.DATABASE_URL = DATABASE_URL

await main()

expect(console.log).toHaveBeenCalledWith(
expect.stringContaining(
'Connected to database successfully'
)
)
})

it('status', async () => {
process.env.DATABASE_URL = DATABASE_URL

await main('status')

await expect(async () => main()).rejects.toThrow('DATABASE_URL not set, please run `DATABASE_URL=postgres://<your pg url> typed-pg`')
expect(console.log).toHaveBeenCalledWith(
expect.stringContaining(
'Status:'
)
)
expect(console.log).toHaveBeenCalledWith(
expect.stringContaining(
'Lock:'
)
)
expect(console.log).toHaveBeenCalledWith(
expect.stringContaining(
'Migrations:'
)
)
})
})
2 changes: 1 addition & 1 deletion src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

import { main } from './main'

main()
main().finally(() => process.exit(0))
47 changes: 39 additions & 8 deletions src/cli/main.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,50 @@
import postgres from "postgres"
import postgres, { type Sql } from "postgres"
import { Logger } from '@faasjs/logger'

export async function main() {
export async function main(operation = process.argv[2] as string) {
const logger = new Logger('TypedPg')
const connection = process.env.DATABASE_URL as string

if (!connection)
throw Error('DATABASE_URL not set, please run `DATABASE_URL=postgres://<your pg url> typed-pg`')
if (!connection) {
logger.error('DATABASE_URL not set, please run `DATABASE_URL=postgres://<your pg url> typed-pg`')
return
}

const sql = postgres(connection)
let sql: Sql

try {
sql = postgres(connection)
await sql`SELECT 1`
logger.info('Connected to database successfully')
} catch (error) {
console.error(error)
throw Error('Error connecting to database, please check your DATABASE_URL')
logger.error('Error connecting to database, please check your DATABASE_URL\n', error)
return
}

if (!operation) {
logger.error('Please provide a operation to run: `typed-pg <operation>`, e.g. `typed-pg status`')
return
}

console.log('Connected to database')
switch (operation) {
case 'status': {
await sql`CREATE TABLE IF NOT EXISTS typed_pg_migrations_lock (
"index" serial4 NOT NULL,is_locked int4 NULL,CONSTRAINT typed_pg_migrations_lock_pkey PRIMARY KEY (index)
)`
await sql`CREATE TABLE IF NOT EXISTS typed_pg_migrations (
id serial4 NOT NULL,
"name" varchar(255) NULL,
batch int4 NULL,
migration_time timestamptz NULL,
CONSTRAINT typed_pg_migrations_pkey PRIMARY KEY (id)
)`
const lock = await sql`SELECT * FROM typed_pg_migrations_lock`
const migrations = await sql`SELECT * FROM typed_pg_migrations`

logger.info('Status:')
logger.info('Lock:', lock)
logger.info('Migrations:', migrations)
break
}
}
}
2 changes: 1 addition & 1 deletion src/query-builder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Client } from './client'
import type { ColumnName, ColumnValue, TableName, TableType } from './types'
import { escapeIdentifier } from './escape'
import { escapeIdentifier } from './utils'

const NormalOperators = ['=', '!=', '<', '<=', '>', '>='] as const
const ArrayOperators = ['IN', 'NOT IN'] as const
Expand Down
180 changes: 90 additions & 90 deletions src/schema-builder/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,105 +8,104 @@ describe('SchemaBuilder', () => {

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

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

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

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

builder.createTable('creators', table => {
table.string('string').primary().unique()
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('creators_pkey'))
).toMatchObject({
schemaname: 'public',
tablename: 'creators',
indexname: 'creators_pkey',
tablespace: null,
indexdef: 'CREATE UNIQUE INDEX creators_pkey ON public.creators USING btree (string)',
})

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('createTable', async () => {
// const builder = new SchemaBuilder(client)

// builder.createTable('creators', table => {
// table.string('string').primary().unique()
// 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('creators_pkey'))
// ).toMatchObject({
// schemaname: 'public',
// tablename: 'creators',
// indexname: 'creators_pkey',
// tablespace: null,
// indexdef: 'CREATE UNIQUE INDEX creators_pkey ON public.creators USING btree (string)',
// })

// 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)',
// })
// })

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

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

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

Expand All @@ -116,6 +115,9 @@ describe('SchemaBuilder', () => {
table.renameColumn('string', 'new_string')
table.number('number')
table.dropColumn('created_at')
table.alterColumn('string', {
primary: false,
})
table.alterColumn('updated_at', {
type: 'date',
defaultValue: 'NULL',
Expand Down Expand Up @@ -156,7 +158,5 @@ describe('SchemaBuilder', () => {
tablespace: null,
indexdef: 'CREATE UNIQUE INDEX alters_pkey ON public.alters USING btree (updated_at)',
})

await client.raw('DROP TABLE alters')
})
})
Loading

0 comments on commit 8447ed4

Please sign in to comment.