diff --git a/common/data/package.json b/common/data/package.json index 00f767586..44a8199d3 100644 --- a/common/data/package.json +++ b/common/data/package.json @@ -6,8 +6,8 @@ "build": "echo '@casimir/data build not specified. Disregard this warning and any listed errors above if @casimir/types is not needed for the current project build.' && exit 0", "clean": "ts-node --transpile-only scripts/clean.ts", "configure:python": "poetry install && poetry run ipython kernel install --user --name=casimir-data", - "dev": "ts-node --transpile-only scripts/postgres.ts --clean \"$npm_config_clean\" --tables \"$npm_config_tables\"", - "watch": "ts-node-dev --watch src --respawn --transpile-only scripts/postgres.ts --clean \"$npm_config_clean\" --tables \"$npm_config_tables\"", + "dev": "ts-node --transpile-only scripts/postgres.ts --tables \"$npm_config_tables\"", + "watch": "ts-node-dev --watch src --respawn --transpile-only scripts/postgres.ts --tables \"$npm_config_tables\"", "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { diff --git a/common/data/scripts/clean.ts b/common/data/scripts/clean.ts index 55c357024..afc0c7d8b 100644 --- a/common/data/scripts/clean.ts +++ b/common/data/scripts/clean.ts @@ -1,13 +1,25 @@ import { run } from '@casimir/helpers' /** Resource path from package caller */ -const resources = './scripts' +const resourcePath = './scripts' /** - * Clean local Docker Postgres environment, sql, and pgdata. + * Clean Docker containers, Postgres data, and SQL schema files. */ void async function () { - console.log(`Cleaning Docker services, Postgres data, and SQL schema files from ${resources}`) - await run('docker compose -p casimir-data down -v') - await run(`npx rimraf ${resources}/.out`) + console.log(`Cleaning up Docker containers, Postgres data, and SQL schema files from ${resourcePath}/.out`) + + /** Stop postgres database */ + const stackName = 'casimir-data' + const containerName = `${stackName}-postgres-1` + const container = await run(`docker ps -q --filter name=${containerName}`) + if (container) { + await run(`docker compose -p ${stackName} -f ${resourcePath}/docker-compose.yaml down`) + } + + /** Clear output directory for pgdata and sql */ + const outDir = `${resourcePath}/.out` + await run(`npx rimraf ${outDir}`) + + console.log('🐘 Database resources cleaned') }() \ No newline at end of file diff --git a/common/data/scripts/docker-compose.yaml b/common/data/scripts/docker-compose.yaml index cb52db9db..4f6f04653 100644 --- a/common/data/scripts/docker-compose.yaml +++ b/common/data/scripts/docker-compose.yaml @@ -1,9 +1,8 @@ version: '3' services: - pg: + postgres: image: postgres:latest - restart: always ports: - 5432:5432 environment: diff --git a/common/data/scripts/postgres.ts b/common/data/scripts/postgres.ts index b40e67929..73009c2fb 100644 --- a/common/data/scripts/postgres.ts +++ b/common/data/scripts/postgres.ts @@ -4,7 +4,7 @@ import { run } from '@casimir/helpers' import { JsonSchema, Schema, accountSchema, nonceSchema, userSchema } from '@casimir/data' /** Resource path from package caller */ -const resources = './scripts' +const resourcePath = './scripts' /** All table schemas */ const tableSchemas = { @@ -17,7 +17,6 @@ const tableSchemas = { * Run a local postgres database with the given tables. * * Arguments: - * --clean: clean the database before starting (optional, i.e., --clean) * --tables: tables to deploy (optional, i.e., --tables=accounts,users) */ void async function () { @@ -25,29 +24,33 @@ void async function () { /** Parse command line arguments */ const argv = minimist(process.argv.slice(2)) - /** Default to clean services and data */ - const clean = argv.clean !== 'false' || argv.clean !== false - /** Default to all tables */ const tables = argv.tables ? argv.tables.split(',') : ['accounts', 'nonces', 'users'] - - /** Write to sql file in ${resources}/sql */ - if (clean) { - await run('npm run clean --workspace @casimir/data') - } - const sqlDir = `${resources}/.out/sql` - if (!fs.existsSync(sqlDir)) fs.mkdirSync(sqlDir, { recursive: true }) + let sqlSchema = '-- Generated by @casimir/data/scripts/postgres.ts\n\n' for (const table of tables) { const tableSchema = tableSchemas[table] as JsonSchema const schema = new Schema(tableSchema) const postgresTable = schema.getPostgresTable() - - console.log(`${schema.getTitle()} JSON schema parsed to SQL:`) - console.log(postgresTable) + console.log(`${schema.getTitle()} JSON schema parsed to SQL`) + sqlSchema += `DROP TABLE IF EXISTS ${table};\n` + sqlSchema += `${postgresTable}\n\n` + } - fs.writeFileSync(`${sqlDir}/${table}.sql`, postgresTable) + /** Write to sql file in ${resourcePath}/sql */ + const sqlDir = `${resourcePath}/.out/sql` + if (!fs.existsSync(sqlDir)) fs.mkdirSync(sqlDir, { recursive: true }) + fs.writeFileSync(`${sqlDir}/schema.sql`, sqlSchema) + + /** Start or sync database with latest schema */ + const stackName = 'casimir-data' + const containerName = `${stackName}-postgres-1` + const container = await run(`docker ps -q --filter name=${containerName}`) + if (!container) { + /** Start local database */ + await run(`docker compose -p ${stackName} -f ${resourcePath}/docker-compose.yaml up -d`) + console.log('🐘 Database started') + } else { + await run(`docker exec ${containerName} psql -U postgres -d postgres -f /docker-entrypoint-initdb.d/schema.sql`) + console.log('🐘 Database synced') } - - /** Start local database */ - await run(`docker compose -p casimir-data -f ${resources}/docker-compose.yaml up -d`) }() \ No newline at end of file diff --git a/common/data/src/schemas/account.schema.json b/common/data/src/schemas/account.schema.json index fc4b632f6..dabd7d995 100644 --- a/common/data/src/schemas/account.schema.json +++ b/common/data/src/schemas/account.schema.json @@ -41,6 +41,10 @@ "type": "array", "description": "The account transactions" }, + "updated_at": { + "type": "string", + "description": "The account last update date in ISO 8601 format" + }, "wallet": { "type": "string", "description": "The account wallet provider" diff --git a/common/helpers/src/index.ts b/common/helpers/src/index.ts index 4ed3c8715..c2db95e08 100644 --- a/common/helpers/src/index.ts +++ b/common/helpers/src/index.ts @@ -99,10 +99,15 @@ export function kebabCase(string: string): string { */ export async function run(fullCommand: string) { const [command, ...args] = fullCommand.split(' ') - const child = spawn(command, args, { stdio: 'inherit' }) + const child = spawn(command, args) + let data = '' return new Promise((resolve, reject) => { child.on('error', reject) - child.on('exit', resolve) + child.stdout.on('data', chunk => { + process.stdout.write(chunk.toString()) + data += chunk.toString() + }) + child.on('exit', () => resolve(data)) }) } diff --git a/scripts/local/dev.ts b/scripts/local/dev.ts index ea241b26a..1063b283a 100644 --- a/scripts/local/dev.ts +++ b/scripts/local/dev.ts @@ -64,10 +64,14 @@ void async function () { const { chains, services, tables } = apps[app as keyof typeof apps] if (mock) { + + if (clean) { + /** Clean postgres database */ + await $`npm run clean --workspace @casimir/data` + } + /** Mock postgres database */ - $`npm run watch --clean=${clean} --tables=${tables.join(',')} --workspace @casimir/data` - /** Comment out line above and uncomment line below when schemas are stable */ - // $`npm run dev --clean=${clean} --tables=${tables.join(',')} --workspace @casimir/data` + $`npm run watch --tables=${tables.join(',')} --workspace @casimir/data` /** Mock services */ let port = 4000