Skip to content
This repository was archived by the owner on Dec 26, 2022. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"editor.defaultFormatter": "esbenp.prettier-vscode"
},

"jest.autoRun": {},

"search.exclude": {
".git": true,
".eslintcache": true,
Expand All @@ -34,5 +36,5 @@
"*.{css,sass,scss}.d.ts": true
},

"cSpell.words": ["Popconfirm", "Sider"]
"cSpell.words": ["autorun", "Popconfirm", "Sider", "Yadro"]
}
19 changes: 19 additions & 0 deletions docs/Migrations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
1. 袛芯斜邪胁懈褌褜 胁 `schemas` 薪芯胁褍褞 褋褏械屑褍 `{entity}/migrations/{Name}SchemaV{N}`
2. 袛芯斜邪胁懈褌褜 胁 `schemas` 胁 褎邪泄谢 `index.ts` 薪芯胁褍褞 褋褏械屑褍

```ts
export const schemaMigrations: SchemaMigration[] = [
...{ version: N, schema: NameSchemaVN },
];
```

3. 袙 AbstractFileRepository.ts restore
1. 袩邪褉褋懈褌 json
2. 袩褉芯胁械褉褟械褌 褟胁谢褟褞褌褋褟 谢懈 锌芯谢褍褔械薪薪褘械 写邪薪薪褘械 芯斜褗械泻褌芯屑
4. 袦懈谐褉邪褑懈褟
1. 小屑芯褌褉懈褌 械褋褌褜 谢懈 `__version` 胁 泻芯褉薪械 芯斜褗械泻褌邪
2. 袝褋谢懈 薪械褌, 褌芯 胁邪谢懈写懈褉褍械褌 褋 锌芯屑芯褖褜褞 褋褏械屑褘 `{Name}SchemaV0`, 懈薪邪褔械 懈褖械褌 褋褏械屑褍 褋 褋芯芯褌胁械褌褋褌胁褍褞褖械泄 胁械褉褋懈械泄
3. 袝褋谢懈 胁械褉褋懈褟 薪械 褋邪屑邪褟 锌芯褋谢械写薪褟褟 懈蟹 褋锌懈褋泻邪 `schemaMigrations`, 褌芯 锌褉懈屑械薪褟械褌 屑懈谐褉邪褑懈褞
1. 袦懈谐褉邪褑懈褟 锌褉懈薪懈屑邪械褌 胁械褉褋懈褞 V{N} 懈 胁芯蟹胁褉邪褖邪械褌 v{N+1}
2. 袩褉懈屑械薪褟械屑 屑懈谐褉邪褑懈懈 褋 N 写芯 锌芯褋谢械写薪械泄
4. 袩芯褋谢械 褌芯谐芯 泻邪泻 胁褋械 屑懈谐褉邪褑懈懈 锌褉芯褕谢懈, 锌械褉械写邪械屑 写邪薪薪褘械 胁 泻芯薪褋褌褉褍泻褌芯褉 屑芯写械谢懈
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@
"@ant-design/colors": "6.0.0",
"@ant-design/icons": "4.6.2",
"@sentry/electron": "2.5.0",
"ajv": "^8.8.2",
"antd": "4.17.2",
"caniuse-lite": "1.0.30001214",
"clsx": "^1.1.1",
Expand Down
4 changes: 3 additions & 1 deletion src/base/AbstractFactory.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// ConstructorParameters<typeof SomeClass>

export default abstract class AbstractFactory {
create<T>(Model: any, data: any): T {
return new Model(data);
}

createList<T>(Model: any, data: any): T[] {
createList<M, T>(Model: M, data: any): T[] {
let items: T[] = [];

data.forEach((json: any) => {
Expand Down
261 changes: 261 additions & 0 deletions src/base/MigrationRunner.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
import MigrationRunner from './MigrationRunner';
import { SchemaMigration } from '../types/SchemaMigration';
import { JSONSchemaType } from 'ajv';
import MigrationErrorCodes from '../types/MigrationErrorCodes';

describe('MigrationRunner tests', () => {
describe('Constructor tests', () => {
test(`Should be fine`, () => {
expect(
new MigrationRunner([
{ version: 0, schema: {} },
{ version: 1, schema: {}, migration: () => null },
])
).toBeDefined();
});

test(`Throw ErrorCode=${MigrationErrorCodes.NoMigrations} NoMigrations`, () => {
expect(() => new MigrationRunner([])).toThrow(
`schemaMigrations can't be empty`
);
});

test(`Throw ErrorCode=${MigrationErrorCodes.NoZeroMigration} NoZeroMigration`, () => {
expect(() => new MigrationRunner([{ version: 1, schema: {} }])).toThrow(
'schemaMigrations should have migration for `version=0`'
);
});

test(`Throw ErrorCode=${MigrationErrorCodes.IncorrectMigrationsOrder} IncorrectMigrationsOrder`, () => {
expect(
() =>
new MigrationRunner([
{ version: 0, schema: {} },
{ version: 2, schema: {} },
])
).toThrow('Each version should go one after the other');
});
});

describe('Migration tests', () => {
type TypeV0 = { data: number };
type TypeV1 = { data: number[]; __version: number };
type TypeV2 = {
data: number[];
additionalData: string;
__version: number;
};
const schemaV0: JSONSchemaType<TypeV0> = {
type: 'object',
properties: {
data: { type: 'number' },
},
required: ['data'],
};
const schemaV1: JSONSchemaType<TypeV1> = {
type: 'object',
properties: {
__version: { type: 'number' },
data: { type: 'array', items: { type: 'number' } },
},
required: ['data', '__version'],
};
const schemaV2: JSONSchemaType<TypeV2> = {
type: 'object',
properties: {
__version: { type: 'number' },
data: { type: 'array', items: { type: 'number' } },
additionalData: { type: 'string' },
},
required: ['data', '__version', 'additionalData'],
};
const migrations: SchemaMigration[] = [
{ version: 0, schema: schemaV0 },
{
version: 1,
schema: schemaV1,
migration(item: TypeV0): TypeV1 {
return {
data: [item.data],
__version: 1,
};
},
},
{
version: 2,
schema: schemaV2,
migration(item: TypeV1): TypeV2 {
return {
data: item.data,
additionalData: 'test',
__version: 2,
};
},
},
];

test('Test migration with 3 iterations', () => {
const dataV0: TypeV0 = { data: 777 };
const expectedData: TypeV2 = {
data: [777],
additionalData: 'test',
__version: 2,
};
const mr = new MigrationRunner(migrations);

const resultData = mr.runMigration(dataV0);
expect(resultData).toStrictEqual(expectedData);
});

test('Test continue migration', () => {
const dataV0: TypeV1 = { data: [777], __version: 1 };
const expectedData: TypeV2 = {
data: [777],
additionalData: 'test',
__version: 2,
};
const mr = new MigrationRunner(migrations);

const resultData = mr.runMigration(dataV0);
expect(resultData).toStrictEqual(expectedData);
});
});

describe('Migration errors', () => {
test(`Throw ErrorCode=${MigrationErrorCodes.ValidationFailed} ValidationFailed`, () => {
type TypeV0 = { data: number; text: string; prop: { test: 1 } };

const schemaV0: JSONSchemaType<TypeV0> = {
type: 'object',
properties: {
data: { type: 'number' },
text: { type: 'string' },
prop: {
type: 'object',
properties: { test: { type: 'number' } },
required: ['test'],
},
},
required: ['data', 'text'],
};

const migrations: SchemaMigration[] = [{ version: 0, schema: schemaV0 }];

const dataV0 = { fakeData: 77, text: 123, prop: { testFail: 1 } };

const mr = new MigrationRunner(migrations);

expect(() => mr.runMigration(dataV0)).toThrow(
[
'Migration to version=1. Schema validation error. Found next errors:',
'"/": "must have required property \'data\'"',
'"/text": "must be string"',
'"/prop": "must have required property \'test\'"',
].join('\n')
);
});

test(`Throw ErrorCode=${MigrationErrorCodes.MigrationNotFound} MigrationNotFound - no migration for version=1`, () => {
type TypeV0 = { data: number };

const schemaV0: JSONSchemaType<TypeV0> = {
type: 'object',
properties: {
data: { type: 'number' },
},
required: ['data'],
};

const migrations: SchemaMigration[] = [
{ version: 0, schema: schemaV0 },
{ version: 1, schema: schemaV0 },
];

const dataV0 = { data: 1 };

const mr = new MigrationRunner(migrations);

expect(() => mr.runMigration(dataV0)).toThrow(
/Migration {\d->\d} not found/
);
});

test(`Throw ErrorCode=${MigrationErrorCodes.MigrationFailed} MigrationFailed - migration returned undefined`, () => {
type TypeV0 = { data: number };
type TypeV1 = { data: number };

const schemaV0: JSONSchemaType<TypeV0> = {
type: 'object',
properties: {
data: { type: 'number' },
},
required: ['data'],
};
const schemaV1: JSONSchemaType<TypeV1> = {
type: 'object',
properties: {
data: { type: 'number' },
},
required: ['data'],
};

const migrations: SchemaMigration[] = [
{ version: 0, schema: schemaV0 },
{
version: 1,
schema: schemaV1,
migration() {
return undefined;
},
},
];

const dataV0 = { data: 1 };

const mr = new MigrationRunner(migrations);

expect(() => mr.runMigration(dataV0)).toThrow(
`migration returned 'undefined'`
);
});

test(`Throw ErrorCode=${MigrationErrorCodes.MigrationFailed} MigrationFailed migration returned 'data' without '__version'`, () => {
type TypeV0 = { data: number };
type TypeV1 = { data: number };

const schemaV0: JSONSchemaType<TypeV0> = {
type: 'object',
properties: {
data: { type: 'number' },
},
required: ['data'],
};
const schemaV1: JSONSchemaType<TypeV1> = {
type: 'object',
properties: {
data: { type: 'number' },
},
required: ['data'],
};

const migrations: SchemaMigration[] = [
{ version: 0, schema: schemaV0 },
{
version: 1,
schema: schemaV1,
migration(item) {
return item;
},
},
];

const dataV0 = { data: 1 };

const mr = new MigrationRunner(migrations);

expect(() => mr.runMigration(dataV0)).toThrow(
`migration returned 'data' without '__version'`
);
});
});
});
Loading