New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@contember/schema-migrations

Package Overview
Dependencies
Maintainers
5
Versions
266
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@contember/schema-migrations - npm Package Compare versions

Comparing version 1.2.0-alpha.18 to 1.2.0-alpha.19

dist/src/modifications/settings/index.d.ts

2

dist/src/Migration.d.ts

@@ -0,1 +1,2 @@

import { SchemaValidatorSkippedErrors } from '@contember/schema-utils';
export interface MigrationInfo {

@@ -5,2 +6,3 @@ readonly version: string;

readonly formatVersion: number;
readonly skippedErrors?: SchemaValidatorSkippedErrors[];
}

@@ -7,0 +9,0 @@ interface Migration extends MigrationInfo {

4

dist/src/Migration.js

@@ -7,6 +7,6 @@ "use strict";

exports.calculateMigrationChecksum = void 0;
const crypto_1 = __importDefault(require("crypto"));
const node_crypto_1 = __importDefault(require("node:crypto"));
const calculateMigrationChecksum = (migration) => {
const canonicalMigration = JSON.stringify(migration.modifications);
return crypto_1.default //
return node_crypto_1.default //
.createHash('md5')

@@ -13,0 +13,0 @@ .update(canonicalMigration)

@@ -9,3 +9,5 @@ import { MigrationFilesManager } from './MigrationFilesManager';

constructor(migrationFilesManager: MigrationFilesManager, schemaDiffer: SchemaDiffer);
prepareMigration(initialSchema: Schema, newSchema: Schema, migrationName: string): Promise<{
prepareMigration(initialSchema: Schema, newSchema: Schema, migrationName: string, { skipInitialSchemaValidation }?: {
skipInitialSchemaValidation?: boolean;
}): Promise<{
migration: Migration;

@@ -12,0 +14,0 @@ initialSchema: Schema;

@@ -11,5 +11,5 @@ "use strict";

}
async prepareMigration(initialSchema, newSchema, migrationName) {
async prepareMigration(initialSchema, newSchema, migrationName, { skipInitialSchemaValidation = false } = {}) {
await this.migrationFilesManager.createDirIfNotExist();
const modifications = this.schemaDiffer.diffSchemas(initialSchema, newSchema);
const modifications = this.schemaDiffer.diffSchemas(initialSchema, newSchema, { skipInitialSchemaValidation });
if (modifications.length === 0) {

@@ -16,0 +16,0 @@ return null;

@@ -28,13 +28,4 @@ "use strict";

const MigrationVersionHelper_1 = require("./MigrationVersionHelper");
const fs = __importStar(require("fs"));
const util_1 = require("util");
const path = __importStar(require("path"));
const readFile = (0, util_1.promisify)(fs.readFile);
const fsWrite = (0, util_1.promisify)(fs.writeFile);
const fsRemove = (0, util_1.promisify)(fs.unlink);
const fsRealpath = (0, util_1.promisify)(fs.realpath);
const mkdir = (0, util_1.promisify)(fs.mkdir);
const lstatFile = (0, util_1.promisify)(fs.lstat);
const readDir = (0, util_1.promisify)(fs.readdir);
const mvFile = (0, util_1.promisify)(fs.rename);
const fs = __importStar(require("node:fs/promises"));
const path = __importStar(require("node:path"));
class MigrationFilesManager {

@@ -46,15 +37,15 @@ constructor(directory) {

const path = this.formatPath(name);
await fsWrite(path, content, { encoding: 'utf8' });
return await fsRealpath(path);
await fs.writeFile(path, content, { encoding: 'utf8' });
return await fs.realpath(path);
}
async removeFile(name) {
const path = this.formatPath(name);
await fsRemove(path);
await fs.unlink(path);
}
async moveFile(oldName, newName) {
await mvFile(this.formatPath(oldName), this.formatPath(newName));
await fs.rename(this.formatPath(oldName), this.formatPath(newName));
}
async createDirIfNotExist() {
try {
await mkdir(this.directory);
await fs.mkdir(this.directory);
}

@@ -72,3 +63,3 @@ catch (e) {

.filter(async (file) => {
return (await lstatFile(`${this.directory}/${file}`)).isFile();
return (await fs.lstat(`${this.directory}/${file}`)).isFile();
}));

@@ -79,3 +70,3 @@ return filteredFiles.sort();

try {
return await readDir(this.directory);
return await fs.readdir(this.directory);
}

@@ -97,3 +88,3 @@ catch (e) {

path: `${this.directory}/${filename}`,
content: await readFile(`${this.directory}/${filename}`, { encoding: 'utf8' }),
content: await fs.readFile(`${this.directory}/${filename}`, { encoding: 'utf8' }),
}));

@@ -100,0 +91,0 @@ return await Promise.all(filesWithContent);

@@ -15,2 +15,3 @@ "use strict";

return (await this.migrationFilesManager.readFiles()).map(({ filename, content }) => {
var _a;
const parsed = JSON.parse(content);

@@ -22,2 +23,3 @@ return {

modifications: parsed.modifications,
skippedErrors: (_a = parsed.skippedErrors) !== null && _a !== void 0 ? _a : [],
};

@@ -24,0 +26,0 @@ });

@@ -14,2 +14,3 @@ "use strict";

const indexes_1 = require("./indexes");
const settings_1 = require("./settings");
class ModificationHandlerFactory {

@@ -29,2 +30,3 @@ constructor(map) {

const handlers = [
settings_1.updateSettingsModification,
acl_1.updateAclSchemaModification,

@@ -31,0 +33,0 @@ acl_1.patchAclSchemaModification,

@@ -164,3 +164,10 @@ "use strict";

const { [field.name]: removed, ...fields } = entity.fields;
return { ...entity, fields };
const indexes = Object.entries(entity.indexes).filter(([, index]) => !index.fields.includes(field.name));
const unique = Object.entries(entity.unique).filter(([, index]) => !index.fields.includes(field.name));
return {
...entity,
fields,
indexes: Object.fromEntries(indexes),
unique: Object.fromEntries(unique),
};
}), (0, schema_utils_1.isRelation)(field) && (0, schema_utils_1.isInverseRelation)(field)

@@ -167,0 +174,0 @@ ? (0, exports.updateEntity)(field.target, (0, exports.updateField)(field.ownedBy, ({ field: { inversedBy, ...field } }) => field))

@@ -5,6 +5,10 @@ import { Schema } from '@contember/schema';

import { Migration } from './Migration';
declare type DiffOptions = {
skipRecreateValidation?: boolean;
skipInitialSchemaValidation?: boolean;
};
export declare class SchemaDiffer {
private readonly schemaMigrator;
constructor(schemaMigrator: SchemaMigrator);
diffSchemas(originalSchema: Schema, updatedSchema: Schema, checkRecreate?: boolean): Migration.Modification[];
diffSchemas(originalSchema: Schema, updatedSchema: Schema, { skipInitialSchemaValidation, skipRecreateValidation, }?: DiffOptions): Migration.Modification[];
}

@@ -15,2 +19,3 @@ export declare class InvalidSchemaException extends Error {

}
export {};
//# sourceMappingURL=SchemaDiffer.d.ts.map

@@ -9,2 +9,3 @@ "use strict";

const indexes_1 = require("./modifications/indexes");
const settings_1 = require("./modifications/settings");
class SchemaDiffer {

@@ -14,6 +15,8 @@ constructor(schemaMigrator) {

}
diffSchemas(originalSchema, updatedSchema, checkRecreate = true) {
const originalErrors = schema_utils_1.SchemaValidator.validate(originalSchema);
if (originalErrors.length > 0) {
throw new InvalidSchemaException('original schema is not valid', originalErrors);
diffSchemas(originalSchema, updatedSchema, { skipInitialSchemaValidation = false, skipRecreateValidation = false, } = {}) {
if (!skipInitialSchemaValidation) {
const originalErrors = schema_utils_1.SchemaValidator.validate(originalSchema);
if (originalErrors.length > 0) {
throw new InvalidSchemaException('original schema is not valid', originalErrors);
}
}

@@ -25,2 +28,3 @@ const updatedErrors = schema_utils_1.SchemaValidator.validate(updatedSchema);

const differs = [
new settings_1.UpdateSettingsDiffer(),
new modifications_1.ConvertOneToManyRelationDiffer(),

@@ -51,4 +55,4 @@ new modifications_1.ConvertOneHasManyToManyHasManyRelationDiffer(),

new modifications_1.CreateColumnDiffer(),
new modifications_1.CreateRelationDiffer(),
new modifications_1.CreateViewDiffer(),
new modifications_1.CreateRelationDiffer(),
new modifications_1.CreateRelationInverseSideDiffer(),

@@ -68,3 +72,3 @@ new modifications_1.CreateUniqueConstraintDiffer(),

}
if (checkRecreate) {
if (!skipRecreateValidation) {
const { meta, ...appliedDiffsSchema2 } = appliedDiffsSchema;

@@ -71,0 +75,0 @@ const errors = (0, schema_utils_1.deepCompare)(updatedSchema, appliedDiffsSchema2, []);

@@ -103,2 +103,82 @@ "use strict";

});
var ViewAddRelationOriginalSchema;
(function (ViewAddRelationOriginalSchema) {
class Article {
constructor() {
this.title = schema_definition_1.SchemaDefinition.stringColumn();
}
}
ViewAddRelationOriginalSchema.Article = Article;
class Category {
constructor() {
this.name = schema_definition_1.SchemaDefinition.stringColumn();
}
}
ViewAddRelationOriginalSchema.Category = Category;
})(ViewAddRelationOriginalSchema || (ViewAddRelationOriginalSchema = {}));
var ViewAddRelationUpdateSchema;
(function (ViewAddRelationUpdateSchema) {
class Article {
constructor() {
this.title = schema_definition_1.SchemaDefinition.stringColumn();
this.category = schema_definition_1.SchemaDefinition.manyHasOne(Category);
this.stats = schema_definition_1.SchemaDefinition.oneHasOneInverse(ArticleStats, 'article');
}
}
ViewAddRelationUpdateSchema.Article = Article;
class Category {
constructor() {
this.name = schema_definition_1.SchemaDefinition.stringColumn();
}
}
ViewAddRelationUpdateSchema.Category = Category;
let ArticleStats = class ArticleStats {
constructor() {
this.article = schema_definition_1.SchemaDefinition.oneHasOne(Article, 'stats');
this.visitCount = schema_definition_1.SchemaDefinition.intColumn();
}
};
ArticleStats = __decorate([
schema_definition_1.SchemaDefinition.View('SELECT 1')
], ArticleStats);
ViewAddRelationUpdateSchema.ArticleStats = ArticleStats;
})(ViewAddRelationUpdateSchema || (ViewAddRelationUpdateSchema = {}));
(0, tests_js_1.testMigrations)('create a relation and a view', {
originalSchema: schema_definition_1.SchemaDefinition.createModel(ViewAddRelationOriginalSchema), updatedSchema: schema_definition_1.SchemaDefinition.createModel(ViewAddRelationUpdateSchema), diff: [
{
modification: 'createRelation',
entityName: 'Article',
owningSide: { name: 'category', nullable: true, type: 'ManyHasOne', target: 'Category', joiningColumn: { columnName: 'category_id', onDelete: 'restrict' } },
},
{
modification: 'createView',
entity: { name: 'ArticleStats',
primary: 'id',
primaryColumn: 'id',
unique: {},
indexes: {},
fields: { id: { name: 'id', columnName: 'id', nullable: false, type: 'Uuid', columnType: 'uuid' },
article: { name: 'article', inversedBy: 'stats', nullable: true, type: 'OneHasOne', target: 'Article', joiningColumn: { columnName: 'article_id', onDelete: 'restrict' } },
visitCount: { name: 'visitCount', columnName: 'visit_count', nullable: true, type: 'Integer', columnType: 'integer' } },
tableName: 'article_stats',
eventLog: { enabled: true },
view: { sql: 'SELECT 1' } },
},
{
modification: 'createRelationInverseSide',
entityName: 'Article',
relation: {
name: 'stats',
ownedBy: 'article',
target: 'ArticleStats',
type: 'OneHasOne',
nullable: true,
},
},
],
sql: (0, tags_js_1.SQL) `ALTER TABLE "article" ADD "category_id" uuid;
ALTER TABLE "article" ADD CONSTRAINT "fk_article_category_id_703b8b" FOREIGN KEY ("category_id") REFERENCES "category"("id") ON DELETE NO ACTION DEFERRABLE INITIALLY IMMEDIATE;
CREATE INDEX "article_category_id_index" ON "article" ("category_id");
CREATE VIEW "article_stats" AS SELECT 1;`,
});
//# sourceMappingURL=createView.test.js.map
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -7,2 +13,3 @@ const tests_1 = require("../../src/tests");

const tags_1 = require("../../src/tags");
const schema_definition_2 = require("@contember/schema-definition");
(0, tests_1.testMigrations)('remove relation (many has one)', {

@@ -115,2 +122,67 @@ originalSchema: new schema_definition_1.SchemaBuilder()

});
var DropIndexOrigSchema;
(function (DropIndexOrigSchema) {
let Article = class Article {
constructor() {
this.title = schema_definition_2.SchemaDefinition.stringColumn();
this.author = schema_definition_2.SchemaDefinition.manyHasOne(Author);
}
};
Article = __decorate([
schema_definition_2.SchemaDefinition.Unique('title', 'author'),
schema_definition_2.SchemaDefinition.Index('title', 'author')
], Article);
DropIndexOrigSchema.Article = Article;
class Author {
constructor() {
this.name = schema_definition_2.SchemaDefinition.stringColumn();
}
}
DropIndexOrigSchema.Author = Author;
})(DropIndexOrigSchema || (DropIndexOrigSchema = {}));
var DropIndexUpSchema;
(function (DropIndexUpSchema) {
let Article = class Article {
constructor() {
this.title = schema_definition_2.SchemaDefinition.stringColumn();
this.author = schema_definition_2.SchemaDefinition.stringColumn();
}
};
Article = __decorate([
schema_definition_2.SchemaDefinition.Unique('title', 'author'),
schema_definition_2.SchemaDefinition.Index('title', 'author')
], Article);
DropIndexUpSchema.Article = Article;
class Author {
constructor() {
this.name = schema_definition_2.SchemaDefinition.stringColumn();
}
}
DropIndexUpSchema.Author = Author;
})(DropIndexUpSchema || (DropIndexUpSchema = {}));
(0, tests_1.testMigrations)('test drop index / unique when removing a field', {
originalSchema: schema_definition_2.SchemaDefinition.createModel(DropIndexOrigSchema),
updatedSchema: schema_definition_2.SchemaDefinition.createModel(DropIndexUpSchema),
diff: [{
modification: 'removeField',
entityName: 'Article',
fieldName: 'author',
}, {
modification: 'createColumn',
entityName: 'Article',
field: { name: 'author', columnName: 'author', nullable: true, type: 'String', columnType: 'text' },
}, {
modification: 'createUniqueConstraint',
entityName: 'Article',
unique: { name: 'unique_Article_title_author_7157ea', fields: ['title', 'author'] },
}, {
modification: 'createIndex',
entityName: 'Article',
index: { name: 'idx_Article_title_author_7157ea', fields: ['title', 'author'] },
}],
sql: (0, tags_1.SQL) `ALTER TABLE "article" DROP "author_id";
ALTER TABLE "article" ADD "author" text;
ALTER TABLE "article" ADD CONSTRAINT "unique_Article_title_author_7157ea" UNIQUE ("title", "author");
CREATE INDEX "idx_Article_title_author_7157ea" ON "article" ("title", "author");`,
});
//# sourceMappingURL=removeField.test.js.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const node_path_1 = require("node:path");
const src_1 = require("../../src");

@@ -8,4 +8,4 @@ const schema_utils_1 = require("@contember/schema-utils");

// eslint-disable-next-line no-console
console.log((0, path_1.relative)(process.cwd(), process.argv[2]));
const migrationsResolver = new src_1.MigrationsResolver(new src_1.MigrationFilesManager((0, path_1.relative)(process.cwd(), process.argv[2])));
console.log((0, node_path_1.relative)(process.cwd(), process.argv[2]));
const migrationsResolver = new src_1.MigrationsResolver(new src_1.MigrationFilesManager((0, node_path_1.relative)(process.cwd(), process.argv[2])));
const modificationHandlerFactory = new src_1.ModificationHandlerFactory(src_1.ModificationHandlerFactory.defaultFactoryMap);

@@ -18,3 +18,4 @@ const differ = new src_1.SchemaDiffer(new src_1.SchemaMigrator(modificationHandlerFactory));

(0, schema_utils_1.schemaType)(nextSchema);
differ.diffSchemas(schema, nextSchema);
const { meta, ...nextSchemaWithoutMeta } = nextSchema;
differ.diffSchemas(schema, nextSchemaWithoutMeta);
schema = nextSchema;

@@ -21,0 +22,0 @@ }

@@ -7,2 +7,3 @@ "use strict";

const vitest_1 = require("vitest");
const schema_utils_1 = require("@contember/schema-utils");
const modificationFactory = new src_1.ModificationHandlerFactory(src_1.ModificationHandlerFactory.defaultFactoryMap);

@@ -13,6 +14,14 @@ const schemaMigrator = new src_1.SchemaMigrator(modificationFactory);

function testDiffSchemas(originalModel, updatedModel, expectedDiff, originalAcl = emptyAcl, updatedAcl = emptyAcl) {
const actualDiff = schemaDiffer.diffSchemas({ model: originalModel, acl: originalAcl, validation: {} }, { model: updatedModel, acl: updatedAcl, validation: {} }, false);
vitest_1.assert.deepStrictEqual(actualDiff, expectedDiff);
const { meta, ...schema } = schemaMigrator.applyModifications({ model: originalModel, acl: originalAcl, validation: {} }, actualDiff, src_1.VERSION_LATEST);
const actualDiff = schemaDiffer.diffSchemas({ ...schema_utils_1.emptySchema, model: originalModel, acl: originalAcl, validation: {} }, { ...schema_utils_1.emptySchema, model: updatedModel, acl: updatedAcl, validation: {} }, { skipRecreateValidation: true });
try {
vitest_1.assert.deepStrictEqual(actualDiff, expectedDiff);
}
catch (e) {
// eslint-disable-next-line no-console
console.log(JSON.stringify(actualDiff));
throw e;
}
const { meta, ...schema } = schemaMigrator.applyModifications({ ...schema_utils_1.emptySchema, model: originalModel, acl: originalAcl, validation: {} }, actualDiff, src_1.VERSION_LATEST);
vitest_1.assert.deepStrictEqual(schema, {
...schema_utils_1.emptySchema,
model: updatedModel,

@@ -25,4 +34,5 @@ acl: updatedAcl,

function testApplyDiff(originalModel, diff, expectedModel, originalAcl = emptyAcl, expectedAcl = emptyAcl) {
const { meta, ...actualSchema } = schemaMigrator.applyModifications({ model: originalModel, acl: originalAcl, validation: {} }, diff, src_1.VERSION_LATEST);
const { meta, ...actualSchema } = schemaMigrator.applyModifications({ ...schema_utils_1.emptySchema, model: originalModel, acl: originalAcl, validation: {} }, diff, src_1.VERSION_LATEST);
vitest_1.assert.deepStrictEqual(actualSchema, {
...schema_utils_1.emptySchema,
model: expectedModel,

@@ -35,3 +45,3 @@ acl: expectedAcl,

function testGenerateSql(originalSchema, diff, expectedSql) {
let schema = { model: originalSchema, acl: emptyAcl, validation: {} };
let schema = { ...schema_utils_1.emptySchema, model: originalSchema, acl: emptyAcl, validation: {} };
const builder = (0, database_migrations_1.createMigrationBuilder)();

@@ -38,0 +48,0 @@ for (let { modification, ...data } of diff) {

{
"name": "@contember/schema-migrations",
"version": "1.2.0-alpha.18",
"version": "1.2.0-alpha.19",
"license": "Apache-2.0",

@@ -11,6 +11,6 @@ "main": "dist/src/index.js",

"dependencies": {
"@contember/database-migrations": "^1.2.0-alpha.18",
"@contember/engine-common": "^1.2.0-alpha.18",
"@contember/schema": "^1.2.0-alpha.18",
"@contember/schema-utils": "^1.2.0-alpha.18",
"@contember/database": "^1.2.0-alpha.19",
"@contember/database-migrations": "^1.2.0-alpha.19",
"@contember/schema": "^1.2.0-alpha.19",
"@contember/schema-utils": "^1.2.0-alpha.19",
"fast-deep-equal": "^3.1.3",

@@ -20,3 +20,3 @@ "rfc6902": "^5.0.1"

"devDependencies": {
"@contember/schema-definition": "^1.2.0-alpha.18"
"@contember/schema-definition": "^1.2.0-alpha.19"
},

@@ -23,0 +23,0 @@ "peerDependencies": {

@@ -1,2 +0,3 @@

import crypto from 'crypto'
import { SchemaValidatorSkippedErrors } from '@contember/schema-utils'
import crypto from 'node:crypto'

@@ -7,2 +8,3 @@ export interface MigrationInfo {

readonly formatVersion: number
readonly skippedErrors?: SchemaValidatorSkippedErrors[]
}

@@ -9,0 +11,0 @@

@@ -18,6 +18,7 @@ import { MigrationFilesManager } from './MigrationFilesManager'

migrationName: string,
{ skipInitialSchemaValidation = false }: { skipInitialSchemaValidation?: boolean } = {},
): Promise<{ migration: Migration; initialSchema: Schema } | null> {
await this.migrationFilesManager.createDirIfNotExist()
const modifications = this.schemaDiffer.diffSchemas(initialSchema, newSchema)
const modifications = this.schemaDiffer.diffSchemas(initialSchema, newSchema, { skipInitialSchemaValidation })
if (modifications.length === 0) {

@@ -24,0 +25,0 @@ return null

import { MigrationVersionHelper } from './MigrationVersionHelper'
import * as fs from 'fs'
import { promisify } from 'util'
import * as path from 'path'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
const readFile = promisify(fs.readFile)
const fsWrite = promisify(fs.writeFile)
const fsRemove = promisify(fs.unlink)
const fsRealpath = promisify(fs.realpath)
const mkdir = promisify(fs.mkdir)
const lstatFile = promisify(fs.lstat)
const readDir = promisify(fs.readdir)
const mvFile = promisify(fs.rename)
class MigrationFilesManager {

@@ -20,4 +10,4 @@ constructor(public readonly directory: string) {}

const path = this.formatPath(name)
await fsWrite(path, content, { encoding: 'utf8' })
return await fsRealpath(path)
await fs.writeFile(path, content, { encoding: 'utf8' })
return await fs.realpath(path)
}

@@ -27,7 +17,7 @@

const path = this.formatPath(name)
await fsRemove(path)
await fs.unlink(path)
}
public async moveFile(oldName: string, newName: string) {
await mvFile(this.formatPath(oldName), this.formatPath(newName))
await fs.rename(this.formatPath(oldName), this.formatPath(newName))
}

@@ -37,3 +27,3 @@

try {
await mkdir(this.directory)
await fs.mkdir(this.directory)
} catch (e) {

@@ -53,3 +43,3 @@ if (!(e instanceof Error) || !('code' in e) || (e as any).code !== 'EEXIST') {

.filter(async file => {
return (await lstatFile(`${this.directory}/${file}`)).isFile()
return (await fs.lstat(`${this.directory}/${file}`)).isFile()
}),

@@ -62,3 +52,3 @@ )

try {
return await readDir(this.directory)
return await fs.readdir(this.directory)
} catch (e) {

@@ -80,3 +70,3 @@ if (e instanceof Error && 'code' in e && (e as any).code === 'ENOENT') {

path: `${this.directory}/${filename}`,
content: await readFile(`${this.directory}/${filename}`, { encoding: 'utf8' }),
content: await fs.readFile(`${this.directory}/${filename}`, { encoding: 'utf8' }),
}))

@@ -83,0 +73,0 @@

@@ -21,2 +21,3 @@ import { MigrationFilesManager } from './MigrationFilesManager'

modifications: parsed.modifications,
skippedErrors: parsed.skippedErrors ?? [],
}

@@ -23,0 +24,0 @@ })

@@ -33,2 +33,3 @@ import { Schema } from '@contember/schema'

import { SchemaWithMeta } from './utils/schemaMeta'
import { updateSettingsModification } from './settings'

@@ -51,2 +52,3 @@

const handlers = [
updateSettingsModification,
updateAclSchemaModification,

@@ -53,0 +55,0 @@ patchAclSchemaModification,

@@ -274,3 +274,10 @@ import { Acl, Model, Schema, Writable } from '@contember/schema'

const { [field.name]: removed, ...fields } = entity.fields
return { ...entity, fields }
const indexes = Object.entries(entity.indexes).filter(([, index]) => !index.fields.includes(field.name))
const unique = Object.entries(entity.unique).filter(([, index]) => !index.fields.includes(field.name))
return {
...entity,
fields,
indexes: Object.fromEntries(indexes),
unique: Object.fromEntries(unique),
}
}),

@@ -277,0 +284,0 @@ isRelation(field) && isInverseRelation(field)

@@ -47,10 +47,18 @@ import { Schema } from '@contember/schema'

import { SchemaWithMeta } from './modifications/utils/schemaMeta'
import { UpdateSettingsDiffer } from './modifications/settings'
type DiffOptions = { skipRecreateValidation?: boolean; skipInitialSchemaValidation?: boolean }
export class SchemaDiffer {
constructor(private readonly schemaMigrator: SchemaMigrator) {}
diffSchemas(originalSchema: Schema, updatedSchema: Schema, checkRecreate: boolean = true): Migration.Modification[] {
const originalErrors = SchemaValidator.validate(originalSchema)
if (originalErrors.length > 0) {
throw new InvalidSchemaException('original schema is not valid', originalErrors)
diffSchemas(originalSchema: Schema, updatedSchema: Schema, {
skipInitialSchemaValidation = false,
skipRecreateValidation = false,
}: DiffOptions = {}): Migration.Modification[] {
if (!skipInitialSchemaValidation) {
const originalErrors = SchemaValidator.validate(originalSchema)
if (originalErrors.length > 0) {
throw new InvalidSchemaException('original schema is not valid', originalErrors)
}
}

@@ -63,2 +71,3 @@ const updatedErrors = SchemaValidator.validate(updatedSchema)

const differs: Differ[] = [
new UpdateSettingsDiffer(),
new ConvertOneToManyRelationDiffer(),

@@ -89,4 +98,4 @@ new ConvertOneHasManyToManyHasManyRelationDiffer(),

new CreateColumnDiffer(),
new CreateRelationDiffer(),
new CreateViewDiffer(),
new CreateRelationDiffer(),
new CreateRelationInverseSideDiffer(),

@@ -109,3 +118,3 @@ new CreateUniqueConstraintDiffer(),

if (checkRecreate) {
if (!skipRecreateValidation) {
const { meta, ...appliedDiffsSchema2 } = appliedDiffsSchema as SchemaWithMeta

@@ -112,0 +121,0 @@ const errors = deepCompare(updatedSchema, appliedDiffsSchema2, [])

@@ -10,6 +10,6 @@ {

{
"path": "../../database-migrations/src"
"path": "../../database/src"
},
{
"path": "../../engine-common/src"
"path": "../../database-migrations/src"
},

@@ -16,0 +16,0 @@ {

@@ -89,1 +89,71 @@ import { testMigrations } from '../../src/tests.js'

namespace ViewAddRelationOriginalSchema {
export class Article {
title = def.stringColumn()
}
export class Category {
name = def.stringColumn()
}
}
namespace ViewAddRelationUpdateSchema {
export class Article {
title = def.stringColumn()
category = def.manyHasOne(Category)
stats = def.oneHasOneInverse(ArticleStats, 'article')
}
export class Category {
name = def.stringColumn()
}
@def.View('SELECT 1')
export class ArticleStats {
article = def.oneHasOne(Article, 'stats')
visitCount = def.intColumn()
}
}
testMigrations('create a relation and a view', {
originalSchema: def.createModel(ViewAddRelationOriginalSchema), updatedSchema: def.createModel(ViewAddRelationUpdateSchema), diff: [
{
modification: 'createRelation',
entityName: 'Article',
owningSide: { name: 'category', nullable: true, type: 'ManyHasOne', target: 'Category', joiningColumn: { columnName: 'category_id', onDelete: 'restrict' } },
},
{
modification: 'createView',
entity: { name: 'ArticleStats',
primary: 'id',
primaryColumn: 'id',
unique: {},
indexes: {},
fields: { id: { name: 'id', columnName: 'id', nullable: false, type: 'Uuid', columnType: 'uuid' },
article: { name: 'article', inversedBy: 'stats', nullable: true, type: 'OneHasOne', target: 'Article', joiningColumn: { columnName: 'article_id', onDelete: 'restrict' } },
visitCount: { name: 'visitCount', columnName: 'visit_count', nullable: true, type: 'Integer', columnType: 'integer' } },
tableName: 'article_stats',
eventLog: { enabled: true },
view: { sql: 'SELECT 1' } },
},
{
modification: 'createRelationInverseSide',
entityName: 'Article',
relation: {
name: 'stats',
ownedBy: 'article',
target: 'ArticleStats',
type: 'OneHasOne',
nullable: true,
},
},
],
sql: SQL`ALTER TABLE "article" ADD "category_id" uuid;
ALTER TABLE "article" ADD CONSTRAINT "fk_article_category_id_703b8b" FOREIGN KEY ("category_id") REFERENCES "category"("id") ON DELETE NO ACTION DEFERRABLE INITIALLY IMMEDIATE;
CREATE INDEX "article_category_id_index" ON "article" ("category_id");
CREATE VIEW "article_stats" AS SELECT 1;`,
})

@@ -5,3 +5,5 @@ import { testMigrations } from '../../src/tests'

import { SQL } from '../../src/tags'
import { SchemaDefinition as def } from '@contember/schema-definition'
testMigrations('remove relation (many has one)', {

@@ -132,1 +134,57 @@ originalSchema: new SchemaBuilder()

})
namespace DropIndexOrigSchema {
@def.Unique('title', 'author')
@def.Index('title', 'author')
export class Article {
title = def.stringColumn()
author = def.manyHasOne(Author)
}
export class Author {
name = def.stringColumn()
}
}
namespace DropIndexUpSchema {
@def.Unique('title', 'author')
@def.Index('title', 'author')
export class Article {
title = def.stringColumn()
author = def.stringColumn()
}
export class Author {
name = def.stringColumn()
}
}
testMigrations('test drop index / unique when removing a field', {
originalSchema: def.createModel(DropIndexOrigSchema),
updatedSchema: def.createModel(DropIndexUpSchema),
diff: [{
modification: 'removeField',
entityName: 'Article',
fieldName: 'author',
}, {
modification: 'createColumn',
entityName: 'Article',
field: { name: 'author', columnName: 'author', nullable: true, type: 'String', columnType: 'text' },
}, {
modification: 'createUniqueConstraint',
entityName: 'Article',
unique: { name: 'unique_Article_title_author_7157ea', fields: ['title', 'author'] },
}, {
modification: 'createIndex',
entityName: 'Article',
index: { name: 'idx_Article_title_author_7157ea', fields: ['title', 'author'] },
}],
sql: SQL`ALTER TABLE "article" DROP "author_id";
ALTER TABLE "article" ADD "author" text;
ALTER TABLE "article" ADD CONSTRAINT "unique_Article_title_author_7157ea" UNIQUE ("title", "author");
CREATE INDEX "idx_Article_title_author_7157ea" ON "article" ("title", "author");`,
})

@@ -1,2 +0,2 @@

import { relative } from 'path'
import { relative } from 'node:path'
import {

@@ -21,3 +21,4 @@ MigrationFilesManager,

schemaType(nextSchema)
differ.diffSchemas(schema, nextSchema)
const { meta, ...nextSchemaWithoutMeta } = nextSchema
differ.diffSchemas(schema, nextSchemaWithoutMeta)

@@ -24,0 +25,0 @@ schema = nextSchema

@@ -6,2 +6,3 @@ import { Migration, ModificationHandlerFactory, SchemaDiffer, SchemaMigrator, VERSION_LATEST } from '../../src'

import { SchemaWithMeta } from '../../src/modifications/utils/schemaMeta'
import { emptySchema } from '@contember/schema-utils'

@@ -32,9 +33,15 @@ const modificationFactory = new ModificationHandlerFactory(ModificationHandlerFactory.defaultFactoryMap)

const actualDiff = schemaDiffer.diffSchemas(
{ model: originalModel, acl: originalAcl, validation: {} },
{ model: updatedModel, acl: updatedAcl, validation: {} },
false,
{ ...emptySchema, model: originalModel, acl: originalAcl, validation: {} },
{ ...emptySchema, model: updatedModel, acl: updatedAcl, validation: {} },
{ skipRecreateValidation: true },
)
assert.deepStrictEqual(actualDiff, expectedDiff)
try {
assert.deepStrictEqual(actualDiff, expectedDiff)
} catch (e) {
// eslint-disable-next-line no-console
console.log(JSON.stringify(actualDiff))
throw e
}
const { meta, ...schema } = schemaMigrator.applyModifications(
{ model: originalModel, acl: originalAcl, validation: {} },
{ ...emptySchema, model: originalModel, acl: originalAcl, validation: {} },
actualDiff,

@@ -44,2 +51,3 @@ VERSION_LATEST,

assert.deepStrictEqual(schema, {
...emptySchema,
model: updatedModel,

@@ -59,3 +67,3 @@ acl: updatedAcl,

const { meta, ...actualSchema } = schemaMigrator.applyModifications(
{ model: originalModel, acl: originalAcl, validation: {} },
{ ...emptySchema, model: originalModel, acl: originalAcl, validation: {} },
diff,

@@ -66,2 +74,3 @@ VERSION_LATEST,

assert.deepStrictEqual(actualSchema, {
...emptySchema,
model: expectedModel,

@@ -74,3 +83,3 @@ acl: expectedAcl,

export function testGenerateSql(originalSchema: Model.Schema, diff: Migration.Modification[], expectedSql: string) {
let schema = { model: originalSchema, acl: emptyAcl, validation: {} }
let schema = { ...emptySchema, model: originalSchema, acl: emptyAcl, validation: {} }
const builder = createMigrationBuilder()

@@ -77,0 +86,0 @@ for (let { modification, ...data } of diff) {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc