From d6d3d0d16f3f111c0cf45e124ee10a4eca9fa714 Mon Sep 17 00:00:00 2001 From: Adelin Niculae <43118261+adelinn@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:52:33 +0200 Subject: [PATCH 1/2] add functions for forcing auto increment on postgres --- src/schemaExporter.ts | 2 + src/utils.test.ts | 120 +++++++++++++++++++++++++++++++++++++++++- src/utils.ts | 12 +++++ src/vendorSpecific.ts | 30 +++++++++++ 4 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 src/vendorSpecific.ts diff --git a/src/schemaExporter.ts b/src/schemaExporter.ts index bfb4c32..1cf7947 100644 --- a/src/schemaExporter.ts +++ b/src/schemaExporter.ts @@ -3,6 +3,7 @@ import { readFile, writeFile } from 'fs/promises'; import { condenseAction } from './condenseAction.js'; import type { IExporter } from './types'; import { ExportHelper } from './utils.js'; +import { exportHook } from './vendorSpecific.js' export class SchemaExporter implements IExporter { private _filePath: string; @@ -46,6 +47,7 @@ export class SchemaExporter implements IExporter { private createAndSaveSnapshot = async () => { const svc = this._getSchemaService(); let snapshot = await svc.snapshot(); + snapshot = exportHook(snapshot); let hash = svc.getHashedSnapshot(snapshot).hash; let json = JSON.stringify({ snapshot, hash }, null, 2); await writeFile(this._filePath, json); diff --git a/src/utils.test.ts b/src/utils.test.ts index cf03892..6bf515f 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert"; import { describe, it } from "node:test"; -import { deepEqual, getDiff, sortObject } from "./utils.js"; +import { deepEqual, getDiff, sortObject, postgresSequenceToSerialType } from "./utils.js"; describe('sortObject', () => { it('should sort object keys alphabetically', () => { @@ -101,4 +101,122 @@ describe('deepEqual', () => { assert.strictEqual(deepEqual('hello', 'world'), false); assert.strictEqual(deepEqual(null, undefined), false); }); +}); + +describe('postgresSequenceToSerialType', () => { + it('should remove nextval default value and set has_auto_increment to true', () => { + const obj1 = { + "fields": [ + { + "collection": "test_collection", + "field": "id", + "type": "integer", + "meta": null, + "schema": { + "name": "id", + "table": "test_collection", + "data_type": "integer", + "default_value": "nextval('test_collection_id_seq'::regclass)", + "max_length": null, + "numeric_precision": 32, + "numeric_scale": 0, + "is_nullable": false, + "is_unique": true, + "is_primary_key": true, + "is_generated": false, + "generation_expression": null, + "has_auto_increment": false, + "foreign_key_table": null, + "foreign_key_column": null + } + } + ] + }; + const obj2 = { + "fields": [ + { + "collection": "test_collection", + "field": "id", + "type": "integer", + "meta": null, + "schema": { + "name": "id", + "table": "test_collection", + "data_type": "integer", + "default_value": null, + "max_length": null, + "numeric_precision": 32, + "numeric_scale": 0, + "is_nullable": false, + "is_unique": true, + "is_primary_key": true, + "is_generated": false, + "generation_expression": null, + "has_auto_increment": true, + "foreign_key_table": null, + "foreign_key_column": null + } + } + ] + }; + assert.deepEqual(postgresSequenceToSerialType(obj1), obj2); + }); + + it('should return same snapshot if serial type is already used everywhere', () => { + const obj1 = { + "fields": [ + { + "collection": "test_collection", + "field": "id", + "type": "string", + "meta": null, + "schema": { + "name": "id", + "table": "test_collection", + "data_type": "integer", + "default_value": "test", + "max_length": null, + "numeric_precision": 32, + "numeric_scale": 0, + "is_nullable": false, + "is_unique": true, + "is_primary_key": true, + "is_generated": false, + "generation_expression": null, + "has_auto_increment": false, + "foreign_key_table": null, + "foreign_key_column": null + } + } + ] + }; + const obj2 = { + "fields": [ + { + "collection": "test_collection", + "field": "id", + "type": "string", + "meta": null, + "schema": { + "name": "id", + "table": "test_collection", + "data_type": "integer", + "default_value": "test", + "max_length": null, + "numeric_precision": 32, + "numeric_scale": 0, + "is_nullable": false, + "is_unique": true, + "is_primary_key": true, + "is_generated": false, + "generation_expression": null, + "has_auto_increment": false, + "foreign_key_table": null, + "foreign_key_column": null + } + } + ] + }; + assert.deepEqual(postgresSequenceToSerialType(obj1), obj2); + }); }); \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index d0c9084..f9d3d1d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -126,4 +126,16 @@ export function sortObject | T[]>(obj: T): T { sortedObj[key] = sortObject((obj as Record)[key]); }); return sortedObj as T; +} + +export function postgresSequenceToSerialType>(snapshot: T): T { + snapshot.fields + .map( (field: any) => { + if (field.schema?.default_value=="nextval('"+field.schema?.table+"_"+field.schema?.name+"_seq'::regclass)") { + field.schema.default_value = null; + field.schema.has_auto_increment = true; + } + return field; + }) as T; + return snapshot; } \ No newline at end of file diff --git a/src/vendorSpecific.ts b/src/vendorSpecific.ts new file mode 100644 index 0000000..8316a53 --- /dev/null +++ b/src/vendorSpecific.ts @@ -0,0 +1,30 @@ +import { postgresSequenceToSerialType } from './utils'; + +const modifiers: modifiersType = { + import: { + }, + export: { + postgres: [postgresSequenceToSerialType] + } +} + +export function importHook(snapshot: any) { + if (modifiers.import[snapshot.vendor]?.length) + return modifiers.import[snapshot.vendor]?.reduce((_snapshot, modifier) => { + return modifier(_snapshot); + }, snapshot) + return snapshot; +}; + +export function exportHook(snapshot: any) { + if (modifiers.export[snapshot.vendor]?.length) + return modifiers.export[snapshot.vendor]!.reduce((_snapshot, modifier) => { + return modifier(_snapshot); + }, snapshot) + return snapshot; +}; + +type modifiersType = { + import: Record + export: Record +} \ No newline at end of file From 468a225017e66890d4327ae6631a090d98c925b8 Mon Sep 17 00:00:00 2001 From: Adelin Niculae <43118261+adelinn@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:07:12 +0200 Subject: [PATCH 2/2] remove importHook and relocate code --- src/dialects/postgres/utils.test.ts | 121 ++++++++++++++++++++++++++++ src/dialects/postgres/utils.ts | 11 +++ src/schemaExporter.ts | 2 +- src/schemaExporterHooks.ts | 23 ++++++ src/utils.test.ts | 120 +-------------------------- src/utils.ts | 12 --- src/vendorSpecific.ts | 30 ------- tsconfig.test.json | 2 +- 8 files changed, 158 insertions(+), 163 deletions(-) create mode 100644 src/dialects/postgres/utils.test.ts create mode 100644 src/dialects/postgres/utils.ts create mode 100644 src/schemaExporterHooks.ts delete mode 100644 src/vendorSpecific.ts diff --git a/src/dialects/postgres/utils.test.ts b/src/dialects/postgres/utils.test.ts new file mode 100644 index 0000000..647f969 --- /dev/null +++ b/src/dialects/postgres/utils.test.ts @@ -0,0 +1,121 @@ +import assert from "node:assert"; +import { describe, it } from "node:test"; +import { sequenceToSerialType } from "./utils.js"; + +describe('sequenceToSerialType', () => { + it('should remove nextval default value and set has_auto_increment to true', () => { + const obj1 = { + "fields": [ + { + "collection": "test_collection", + "field": "id", + "type": "integer", + "meta": null, + "schema": { + "name": "id", + "table": "test_collection", + "data_type": "integer", + "default_value": "nextval('test_collection_id_seq'::regclass)", + "max_length": null, + "numeric_precision": 32, + "numeric_scale": 0, + "is_nullable": false, + "is_unique": true, + "is_primary_key": true, + "is_generated": false, + "generation_expression": null, + "has_auto_increment": false, + "foreign_key_table": null, + "foreign_key_column": null + } + } + ] + }; + const obj2 = { + "fields": [ + { + "collection": "test_collection", + "field": "id", + "type": "integer", + "meta": null, + "schema": { + "name": "id", + "table": "test_collection", + "data_type": "integer", + "default_value": null, + "max_length": null, + "numeric_precision": 32, + "numeric_scale": 0, + "is_nullable": false, + "is_unique": true, + "is_primary_key": true, + "is_generated": false, + "generation_expression": null, + "has_auto_increment": true, + "foreign_key_table": null, + "foreign_key_column": null + } + } + ] + }; + assert.deepEqual(sequenceToSerialType(obj1), obj2); + }); + + it('should return same snapshot if serial type is already used everywhere', () => { + const obj1 = { + "fields": [ + { + "collection": "test_collection", + "field": "id", + "type": "string", + "meta": null, + "schema": { + "name": "id", + "table": "test_collection", + "data_type": "integer", + "default_value": "test", + "max_length": null, + "numeric_precision": 32, + "numeric_scale": 0, + "is_nullable": false, + "is_unique": true, + "is_primary_key": true, + "is_generated": false, + "generation_expression": null, + "has_auto_increment": false, + "foreign_key_table": null, + "foreign_key_column": null + } + } + ] + }; + const obj2 = { + "fields": [ + { + "collection": "test_collection", + "field": "id", + "type": "string", + "meta": null, + "schema": { + "name": "id", + "table": "test_collection", + "data_type": "integer", + "default_value": "test", + "max_length": null, + "numeric_precision": 32, + "numeric_scale": 0, + "is_nullable": false, + "is_unique": true, + "is_primary_key": true, + "is_generated": false, + "generation_expression": null, + "has_auto_increment": false, + "foreign_key_table": null, + "foreign_key_column": null + } + } + ] + }; + assert.deepEqual(sequenceToSerialType(obj1), obj2); + }); + }); \ No newline at end of file diff --git a/src/dialects/postgres/utils.ts b/src/dialects/postgres/utils.ts new file mode 100644 index 0000000..a3c7201 --- /dev/null +++ b/src/dialects/postgres/utils.ts @@ -0,0 +1,11 @@ +export function sequenceToSerialType>(snapshot: T): T { + snapshot.fields + .map( (field: any) => { + if (field.schema?.default_value=="nextval('"+field.schema?.table+"_"+field.schema?.name+"_seq'::regclass)") { + field.schema.default_value = null; + field.schema.has_auto_increment = true; + } + return field; + }) as T; + return snapshot; +} \ No newline at end of file diff --git a/src/schemaExporter.ts b/src/schemaExporter.ts index 1cf7947..b10e212 100644 --- a/src/schemaExporter.ts +++ b/src/schemaExporter.ts @@ -3,7 +3,7 @@ import { readFile, writeFile } from 'fs/promises'; import { condenseAction } from './condenseAction.js'; import type { IExporter } from './types'; import { ExportHelper } from './utils.js'; -import { exportHook } from './vendorSpecific.js' +import { exportHook } from './schemaExporterHooks.js' export class SchemaExporter implements IExporter { private _filePath: string; diff --git a/src/schemaExporterHooks.ts b/src/schemaExporterHooks.ts new file mode 100644 index 0000000..ef369fb --- /dev/null +++ b/src/schemaExporterHooks.ts @@ -0,0 +1,23 @@ +import * as pgUtils from './dialects/postgres/utils.js'; + +const modifiers: modifiersType = { + postgres: [pgUtils.sequenceToSerialType] +} + +export function exportHook>(snapshot: T) { + if (modifiers[snapshot.vendor]?.length) + return modifiers[snapshot.vendor]!.reduce((_snapshot, modifier) => { + return modifier(_snapshot); + }, snapshot) + return snapshot; +}; + +type modifiersType = Record + +type snapshotWithHashType = { + snapshot: Record, + hash: string +} + +type snapshotWithHashFunctionType = (snapshotWithHash: T) => T +type snapshotFunctionType = >(snapshotWithHash: T) => T \ No newline at end of file diff --git a/src/utils.test.ts b/src/utils.test.ts index 6bf515f..cf03892 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert"; import { describe, it } from "node:test"; -import { deepEqual, getDiff, sortObject, postgresSequenceToSerialType } from "./utils.js"; +import { deepEqual, getDiff, sortObject } from "./utils.js"; describe('sortObject', () => { it('should sort object keys alphabetically', () => { @@ -101,122 +101,4 @@ describe('deepEqual', () => { assert.strictEqual(deepEqual('hello', 'world'), false); assert.strictEqual(deepEqual(null, undefined), false); }); -}); - -describe('postgresSequenceToSerialType', () => { - it('should remove nextval default value and set has_auto_increment to true', () => { - const obj1 = { - "fields": [ - { - "collection": "test_collection", - "field": "id", - "type": "integer", - "meta": null, - "schema": { - "name": "id", - "table": "test_collection", - "data_type": "integer", - "default_value": "nextval('test_collection_id_seq'::regclass)", - "max_length": null, - "numeric_precision": 32, - "numeric_scale": 0, - "is_nullable": false, - "is_unique": true, - "is_primary_key": true, - "is_generated": false, - "generation_expression": null, - "has_auto_increment": false, - "foreign_key_table": null, - "foreign_key_column": null - } - } - ] - }; - const obj2 = { - "fields": [ - { - "collection": "test_collection", - "field": "id", - "type": "integer", - "meta": null, - "schema": { - "name": "id", - "table": "test_collection", - "data_type": "integer", - "default_value": null, - "max_length": null, - "numeric_precision": 32, - "numeric_scale": 0, - "is_nullable": false, - "is_unique": true, - "is_primary_key": true, - "is_generated": false, - "generation_expression": null, - "has_auto_increment": true, - "foreign_key_table": null, - "foreign_key_column": null - } - } - ] - }; - assert.deepEqual(postgresSequenceToSerialType(obj1), obj2); - }); - - it('should return same snapshot if serial type is already used everywhere', () => { - const obj1 = { - "fields": [ - { - "collection": "test_collection", - "field": "id", - "type": "string", - "meta": null, - "schema": { - "name": "id", - "table": "test_collection", - "data_type": "integer", - "default_value": "test", - "max_length": null, - "numeric_precision": 32, - "numeric_scale": 0, - "is_nullable": false, - "is_unique": true, - "is_primary_key": true, - "is_generated": false, - "generation_expression": null, - "has_auto_increment": false, - "foreign_key_table": null, - "foreign_key_column": null - } - } - ] - }; - const obj2 = { - "fields": [ - { - "collection": "test_collection", - "field": "id", - "type": "string", - "meta": null, - "schema": { - "name": "id", - "table": "test_collection", - "data_type": "integer", - "default_value": "test", - "max_length": null, - "numeric_precision": 32, - "numeric_scale": 0, - "is_nullable": false, - "is_unique": true, - "is_primary_key": true, - "is_generated": false, - "generation_expression": null, - "has_auto_increment": false, - "foreign_key_table": null, - "foreign_key_column": null - } - } - ] - }; - assert.deepEqual(postgresSequenceToSerialType(obj1), obj2); - }); }); \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index f9d3d1d..d0c9084 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -126,16 +126,4 @@ export function sortObject | T[]>(obj: T): T { sortedObj[key] = sortObject((obj as Record)[key]); }); return sortedObj as T; -} - -export function postgresSequenceToSerialType>(snapshot: T): T { - snapshot.fields - .map( (field: any) => { - if (field.schema?.default_value=="nextval('"+field.schema?.table+"_"+field.schema?.name+"_seq'::regclass)") { - field.schema.default_value = null; - field.schema.has_auto_increment = true; - } - return field; - }) as T; - return snapshot; } \ No newline at end of file diff --git a/src/vendorSpecific.ts b/src/vendorSpecific.ts deleted file mode 100644 index 8316a53..0000000 --- a/src/vendorSpecific.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { postgresSequenceToSerialType } from './utils'; - -const modifiers: modifiersType = { - import: { - }, - export: { - postgres: [postgresSequenceToSerialType] - } -} - -export function importHook(snapshot: any) { - if (modifiers.import[snapshot.vendor]?.length) - return modifiers.import[snapshot.vendor]?.reduce((_snapshot, modifier) => { - return modifier(_snapshot); - }, snapshot) - return snapshot; -}; - -export function exportHook(snapshot: any) { - if (modifiers.export[snapshot.vendor]?.length) - return modifiers.export[snapshot.vendor]!.reduce((_snapshot, modifier) => { - return modifier(_snapshot); - }, snapshot) - return snapshot; -}; - -type modifiersType = { - import: Record - export: Record -} \ No newline at end of file diff --git a/tsconfig.test.json b/tsconfig.test.json index 38e550a..228c446 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -26,5 +26,5 @@ "rootDir": "./src", "outDir": "./dist-test", }, - "include": ["src/utils.ts", "src/utils.test.ts"] + "include": ["src/utils.ts", "src/utils.test.ts", "src/dialects/postgres/utils.ts", "src/dialects/postgres/utils.test.ts"] } \ No newline at end of file