1+ import Ajv from 'ajv' ;
12import { SchemaMigration } from '../types/SchemaMigration' ;
23import { SchemaType } from '../types/SchemaType' ;
34import { last } from '../helpers/ArrayHelper' ;
45
6+ enum ErrorCodes {
7+ NoMigrations ,
8+ NoZeroMigration ,
9+ IncorrectMigrationsOrder ,
10+ }
11+
512export default class MigrationRunner {
613 private schemaMigrations : SchemaMigration [ ] = [ ] ;
7- private genErrorMsg = ( message : string ) => `[MigrationRunner] ${ message } ` ;
14+ private ajv : Ajv ;
15+ private genErrorMsg = ( error : ErrorCodes | undefined , message : string ) =>
16+ `[MigrationRunner] ${ error ?? - 1 } ${ message } ` ;
817
918 constructor ( schemaMigrations : SchemaMigration [ ] ) {
1019 const schemaMigrationsSorted = schemaMigrations . slice ( ) ;
1120 schemaMigrationsSorted . sort ( ( a , b ) => a . version - b . version ) ;
1221
1322 if ( ! schemaMigrationsSorted . length ) {
14- throw new Error ( this . genErrorMsg ( 'schemaMigrations can`t be empty' ) ) ;
23+ throw new Error (
24+ this . genErrorMsg (
25+ ErrorCodes . NoMigrations ,
26+ 'schemaMigrations can`t be empty'
27+ )
28+ ) ;
1529 }
1630
1731 if ( ! schemaMigrationsSorted . find ( ( i ) => i . version === 0 ) ) {
@@ -32,6 +46,7 @@ export default class MigrationRunner {
3246 }
3347
3448 this . schemaMigrations = schemaMigrationsSorted ;
49+ this . ajv = new Ajv ( { allErrors : true } ) ;
3550 }
3651
3752 runMigration < T extends SchemaType > ( data : T ) {
@@ -42,19 +57,24 @@ export default class MigrationRunner {
4257 throw new Error ( 'There are no migrations' ) ;
4358 }
4459
45- // if (!('__version' in data)) {
46- // const migration = this.schemaMigrations.find((i) => i.version === 1);
47- // if (!migration) {
48- // throw new Error();
49- // }
50- // newData = migration.migration?.(data);
51- // } else {
52- // newData = data;
53- // }
54-
55- // if (newData === undefined || newData.__version === undefined) {
56- // throw new Error();
57- // }
60+ const firstMigration = this . schemaMigrations . find ( ( i ) => i . version === 0 ) ;
61+ if ( ! firstMigration ) {
62+ throw new Error ( ) ;
63+ }
64+ const validate = this . ajv . compile ( firstMigration . schema ) ;
65+ const validateResult = validate ( newData ) ;
66+ if ( ! validateResult ) {
67+ throw new Error (
68+ this . genErrorMsg (
69+ `Schema validation error "version=0". Found next errors:\n${ validate . errors
70+ ?. map (
71+ ( e ) =>
72+ `"${ e . instancePath ? e . instancePath : '/' } ": "${ e . message } "`
73+ )
74+ ?. join ( '\n' ) } `
75+ )
76+ ) ;
77+ }
5878
5979 let nextVersion =
6080 newData . __version !== undefined ? newData . __version + 1 : 1 ;
@@ -70,7 +90,9 @@ export default class MigrationRunner {
7090
7191 if ( ! migration ) {
7292 throw new Error (
73- `Migration from ${ newData . __version } to ${ nextVersion } not found`
93+ this . genErrorMsg (
94+ `Migration from ${ newData . __version } to ${ nextVersion } not found`
95+ )
7496 ) ;
7597 }
7698
@@ -84,6 +106,18 @@ export default class MigrationRunner {
84106 throw new Error ( ) ;
85107 }
86108
109+ const validate = this . ajv . compile ( migration . schema ) ;
110+ const validateObj = validate ( nextData ) ;
111+ if ( ! validateObj ) {
112+ throw new Error (
113+ this . genErrorMsg (
114+ `Schema validation error. version=${ nextVersion } : ${ validate . errors ?. join (
115+ ', '
116+ ) } `
117+ )
118+ ) ;
119+ }
120+
87121 newData = nextData ;
88122 nextVersion = nextData . __version + 1 ;
89123 }
0 commit comments