@nocobase/database 0.7.0-alpha.80 → 0.7.0-alpha.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/database.d.ts CHANGED
@@ -3,10 +3,12 @@ import { AsyncEmitter } from '@nocobase/utils';
3
3
  import merge from 'deepmerge';
4
4
  import { EventEmitter } from 'events';
5
5
  import { ModelCtor, Options, QueryInterfaceDropAllTablesOptions, QueryOptions, Sequelize, SyncOptions } from 'sequelize';
6
+ import { Umzug } from 'umzug';
6
7
  import { Collection, CollectionOptions, RepositoryType } from './collection';
7
8
  import { ImportFileExtension } from './collection-importer';
8
9
  import * as FieldTypes from './fields';
9
10
  import { Field, FieldContext, RelationField } from './fields';
11
+ import { Migrations } from './migration';
10
12
  import { Model } from './model';
11
13
  import { ModelHook } from './model-hook';
12
14
  import { RelationRepository } from './relation-repository/relation-repository';
@@ -22,8 +24,9 @@ interface MapOf<T> {
22
24
  }
23
25
  export interface IDatabaseOptions extends Options {
24
26
  tablePrefix?: string;
27
+ migrator?: any;
25
28
  }
26
- export declare type DatabaseOptions = IDatabaseOptions | Sequelize;
29
+ export declare type DatabaseOptions = IDatabaseOptions;
27
30
  interface RegisterOperatorsContext {
28
31
  db?: Database;
29
32
  path?: string;
@@ -36,6 +39,8 @@ export interface CleanOptions extends QueryInterfaceDropAllTablesOptions {
36
39
  declare type OperatorFunc = (value: any, ctx?: RegisterOperatorsContext) => any;
37
40
  export declare class Database extends EventEmitter implements AsyncEmitter {
38
41
  sequelize: Sequelize;
42
+ migrator: Umzug;
43
+ migrations: Migrations;
39
44
  fieldTypes: Map<any, any>;
40
45
  options: IDatabaseOptions;
41
46
  models: Map<string, ModelCtor<Model<any, any>>>;
@@ -50,6 +55,7 @@ export declare class Database extends EventEmitter implements AsyncEmitter {
50
55
  mergeOptions?: any;
51
56
  }[]>;
52
57
  constructor(options: DatabaseOptions);
58
+ addMigration(item: any): void;
53
59
  /**
54
60
  * Add collection to database
55
61
  * @param options
package/lib/database.js CHANGED
@@ -57,12 +57,24 @@ function _sequelize() {
57
57
  return data;
58
58
  }
59
59
 
60
+ function _umzug() {
61
+ const data = require("umzug");
62
+
63
+ _umzug = function _umzug() {
64
+ return data;
65
+ };
66
+
67
+ return data;
68
+ }
69
+
60
70
  var _collection = require("./collection");
61
71
 
62
72
  var _collectionImporter = require("./collection-importer");
63
73
 
64
74
  var FieldTypes = _interopRequireWildcard(require("./fields"));
65
75
 
76
+ var _migration = require("./migration");
77
+
66
78
  var _modelHook = require("./model-hook");
67
79
 
68
80
  var _operators = _interopRequireDefault(require("./operators"));
@@ -108,6 +120,8 @@ class Database extends _events().EventEmitter {
108
120
  constructor(options) {
109
121
  super();
110
122
  this.sequelize = void 0;
123
+ this.migrator = void 0;
124
+ this.migrations = void 0;
111
125
  this.fieldTypes = new Map();
112
126
  this.options = void 0;
113
127
  this.models = new Map();
@@ -119,28 +133,29 @@ class Database extends _events().EventEmitter {
119
133
  this.modelHook = void 0;
120
134
  this.delayCollectionExtend = new Map();
121
135
 
122
- if (options instanceof _sequelize().Sequelize) {
123
- this.sequelize = options;
124
- } else {
125
- const opts = _objectSpread({
126
- sync: {
127
- alter: {
128
- drop: false
129
- },
130
- force: false
131
- }
132
- }, options);
136
+ const opts = _objectSpread({
137
+ sync: {
138
+ alter: {
139
+ drop: false
140
+ },
141
+ force: false
142
+ }
143
+ }, options);
133
144
 
134
- if (options.storage && options.storage !== ':memory:') {
135
- if (!(0, _path().isAbsolute)(options.storage)) {
136
- opts.storage = (0, _path().resolve)(process.cwd(), options.storage);
137
- }
145
+ if (options.storage && options.storage !== ':memory:') {
146
+ if (!(0, _path().isAbsolute)(options.storage)) {
147
+ opts.storage = (0, _path().resolve)(process.cwd(), options.storage);
138
148
  }
149
+ }
139
150
 
140
- this.sequelize = new (_sequelize().Sequelize)(opts);
141
- this.options = opts;
151
+ if (options.dialect === 'sqlite') {
152
+ delete opts.timezone;
153
+ } else if (!opts.timezone) {
154
+ opts.timezone = '+00:00';
142
155
  }
143
156
 
157
+ this.sequelize = new (_sequelize().Sequelize)(opts);
158
+ this.options = opts;
144
159
  this.collections = new Map();
145
160
  this.modelHook = new _modelHook.ModelHook(this);
146
161
  this.on('afterDefineCollection', collection => {
@@ -170,6 +185,29 @@ class Database extends _events().EventEmitter {
170
185
  }
171
186
 
172
187
  this.initOperators();
188
+ const migratorOptions = this.options.migrator || {};
189
+
190
+ const context = _objectSpread({
191
+ db: this,
192
+ sequelize: this.sequelize,
193
+ queryInterface: this.sequelize.getQueryInterface()
194
+ }, migratorOptions.context);
195
+
196
+ this.migrations = new _migration.Migrations(context);
197
+ this.migrator = new (_umzug().Umzug)({
198
+ logger: migratorOptions.logger || console,
199
+ migrations: this.migrations.callback(),
200
+ context,
201
+ storage: new (_umzug().SequelizeStorage)(_objectSpread(_objectSpread({
202
+ modelName: `${this.options.tablePrefix || ''}migrations`
203
+ }, migratorOptions.storage), {}, {
204
+ sequelize: this.sequelize
205
+ }))
206
+ });
207
+ }
208
+
209
+ addMigration(item) {
210
+ return this.migrations.add(item);
173
211
  }
174
212
  /**
175
213
  * Add collection to database
package/lib/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export * from './database';
4
4
  export { Database as default } from './database';
5
5
  export * from './fields';
6
6
  export * from './magic-attribute-model';
7
+ export * from './migration';
7
8
  export * from './mock-database';
8
9
  export * from './model';
9
10
  export * from './relation-repository/belongs-to-many-repository';
package/lib/index.js CHANGED
@@ -85,6 +85,20 @@ Object.keys(_magicAttributeModel).forEach(function (key) {
85
85
  });
86
86
  });
87
87
 
88
+ var _migration = require("./migration");
89
+
90
+ Object.keys(_migration).forEach(function (key) {
91
+ if (key === "default" || key === "__esModule") return;
92
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
93
+ if (key in exports && exports[key] === _migration[key]) return;
94
+ Object.defineProperty(exports, key, {
95
+ enumerable: true,
96
+ get: function get() {
97
+ return _migration[key];
98
+ }
99
+ });
100
+ });
101
+
88
102
  var _mockDatabase = require("./mock-database");
89
103
 
90
104
  Object.keys(_mockDatabase).forEach(function (key) {
@@ -0,0 +1,33 @@
1
+ import { QueryInterface, Sequelize } from 'sequelize';
2
+ import Database from './database';
3
+ export interface MigrationContext {
4
+ db: Database;
5
+ queryInterface: QueryInterface;
6
+ sequelize: Sequelize;
7
+ }
8
+ export declare class Migration {
9
+ name: string;
10
+ context: {
11
+ db: Database;
12
+ };
13
+ constructor(context: MigrationContext);
14
+ get db(): Database;
15
+ get sequelize(): Sequelize;
16
+ get queryInterface(): QueryInterface;
17
+ up(): Promise<void>;
18
+ down(): Promise<void>;
19
+ }
20
+ export interface MigrationItem {
21
+ name: string;
22
+ migration?: typeof Migration;
23
+ up?: any;
24
+ down?: any;
25
+ }
26
+ export declare class Migrations {
27
+ items: any[];
28
+ context: any;
29
+ constructor(context: any);
30
+ clear(): void;
31
+ add(item: MigrationItem): void;
32
+ callback(): (ctx: any) => any[];
33
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Migrations = exports.Migration = void 0;
7
+
8
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
9
+
10
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
11
+
12
+ class Migration {
13
+ constructor(context) {
14
+ this.name = void 0;
15
+ this.context = void 0;
16
+ this.context = context;
17
+ }
18
+
19
+ get db() {
20
+ return this.context.db;
21
+ }
22
+
23
+ get sequelize() {
24
+ return this.context.db.sequelize;
25
+ }
26
+
27
+ get queryInterface() {
28
+ return this.context.db.sequelize.getQueryInterface();
29
+ }
30
+
31
+ up() {// todo
32
+
33
+ return _asyncToGenerator(function* () {})();
34
+ }
35
+
36
+ down() {// todo
37
+
38
+ return _asyncToGenerator(function* () {})();
39
+ }
40
+
41
+ }
42
+
43
+ exports.Migration = Migration;
44
+
45
+ class Migrations {
46
+ constructor(context) {
47
+ this.items = [];
48
+ this.context = void 0;
49
+ this.context = context;
50
+ }
51
+
52
+ clear() {
53
+ this.items = [];
54
+ }
55
+
56
+ add(item) {
57
+ const Migration = item.migration;
58
+
59
+ if (Migration) {
60
+ const migration = new Migration(this.context);
61
+ migration.name = item.name;
62
+ this.items.push(migration);
63
+ } else {
64
+ this.items.push(item);
65
+ }
66
+ }
67
+
68
+ callback() {
69
+ return ctx => {
70
+ return this.items;
71
+ };
72
+ }
73
+
74
+ }
75
+
76
+ exports.Migrations = Migrations;
@@ -18,5 +18,6 @@ export declare function getConfigByEnv(): {
18
18
  charset: string;
19
19
  collate: string;
20
20
  };
21
+ timezone: string;
21
22
  };
22
23
  export declare function mockDatabase(options?: IDatabaseOptions): MockDatabase;
@@ -64,7 +64,8 @@ function getConfigByEnv() {
64
64
  define: {
65
65
  charset: 'utf8mb4',
66
66
  collate: 'utf8mb4_unicode_ci'
67
- }
67
+ },
68
+ timezone: process.env.DB_TIMEZONE
68
69
  };
69
70
  }
70
71
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/database",
3
- "version": "0.7.0-alpha.80",
3
+ "version": "0.7.0-alpha.83",
4
4
  "description": "",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -12,17 +12,18 @@
12
12
  }
13
13
  ],
14
14
  "dependencies": {
15
- "@nocobase/utils": "0.7.0-alpha.80",
15
+ "@nocobase/utils": "0.7.0-alpha.83",
16
16
  "async-mutex": "^0.3.2",
17
17
  "deepmerge": "^4.2.2",
18
18
  "flat": "^5.0.2",
19
19
  "glob": "^7.1.6",
20
- "sequelize": "^6.9.0"
20
+ "sequelize": "^6.9.0",
21
+ "umzug": "^3.1.1"
21
22
  },
22
23
  "repository": {
23
24
  "type": "git",
24
25
  "url": "git+https://github.com/nocobase/nocobase.git",
25
26
  "directory": "packages/database"
26
27
  },
27
- "gitHead": "9ec196c514d958d2c32c0879def6b98f24bf92fc"
28
+ "gitHead": "838f4f18dcd9ed4fe2ae2660a4918e2a5bd3a869"
28
29
  }
@@ -0,0 +1,52 @@
1
+ import { Database, Migration, mockDatabase } from '@nocobase/database';
2
+
3
+ const names = (migrations: Array<{ name: string }>) => migrations.map(m => m.name);
4
+
5
+ describe('migrator', () => {
6
+ let db: Database;
7
+
8
+ beforeEach(async () => {
9
+
10
+ db = mockDatabase({
11
+ tablePrefix: 'test_',
12
+ });
13
+
14
+ await db.clean({ drop: true });
15
+ });
16
+
17
+ afterEach(async () => {
18
+ await db.close();
19
+ });
20
+
21
+ test('up and down', async () => {
22
+ const spy = jest.fn();
23
+ db.addMigration({
24
+ name: 'migration1',
25
+ migration: class extends Migration {
26
+ async up() {
27
+ spy('migration1-up');
28
+ }
29
+ async down() {
30
+ spy('migration1-down');
31
+ }
32
+ },
33
+ });
34
+ db.addMigration({
35
+ name: 'migration2',
36
+ migration: class extends Migration {
37
+ async up() {
38
+ spy('migration2-up');
39
+ }
40
+ async down() {
41
+ spy('migration2-down');
42
+ }
43
+ },
44
+ });
45
+ await db.migrator.up();
46
+ expect(names(await db.migrator.executed())).toEqual(['migration1', 'migration2']);
47
+ await db.migrator.down();
48
+ expect(names(await db.migrator.executed())).toEqual(['migration1']);
49
+ expect(spy).toHaveBeenCalledTimes(3);
50
+ expect(spy).toHaveBeenNthCalledWith(1, 'migration1-up');
51
+ });
52
+ });
package/src/database.ts CHANGED
@@ -13,10 +13,12 @@ import {
13
13
  SyncOptions,
14
14
  Utils
15
15
  } from 'sequelize';
16
+ import { SequelizeStorage, Umzug } from 'umzug';
16
17
  import { Collection, CollectionOptions, RepositoryType } from './collection';
17
18
  import { ImporterReader, ImportFileExtension } from './collection-importer';
18
19
  import * as FieldTypes from './fields';
19
20
  import { Field, FieldContext, RelationField } from './fields';
21
+ import { Migrations } from './migration';
20
22
  import { Model } from './model';
21
23
  import { ModelHook } from './model-hook';
22
24
  import extendOperators from './operators';
@@ -36,9 +38,10 @@ interface MapOf<T> {
36
38
 
37
39
  export interface IDatabaseOptions extends Options {
38
40
  tablePrefix?: string;
41
+ migrator?: any;
39
42
  }
40
43
 
41
- export type DatabaseOptions = IDatabaseOptions | Sequelize;
44
+ export type DatabaseOptions = IDatabaseOptions;
42
45
 
43
46
  interface RegisterOperatorsContext {
44
47
  db?: Database;
@@ -55,6 +58,8 @@ type OperatorFunc = (value: any, ctx?: RegisterOperatorsContext) => any;
55
58
 
56
59
  export class Database extends EventEmitter implements AsyncEmitter {
57
60
  sequelize: Sequelize;
61
+ migrator: Umzug;
62
+ migrations: Migrations;
58
63
  fieldTypes = new Map();
59
64
  options: IDatabaseOptions;
60
65
  models = new Map<string, ModelCtor<Model>>();
@@ -71,27 +76,30 @@ export class Database extends EventEmitter implements AsyncEmitter {
71
76
  constructor(options: DatabaseOptions) {
72
77
  super();
73
78
 
74
- if (options instanceof Sequelize) {
75
- this.sequelize = options;
76
- } else {
77
- const opts = {
78
- sync: {
79
- alter: {
80
- drop: false,
81
- },
82
- force: false,
79
+ const opts = {
80
+ sync: {
81
+ alter: {
82
+ drop: false,
83
83
  },
84
- ...options,
85
- };
86
- if (options.storage && options.storage !== ':memory:') {
87
- if (!isAbsolute(options.storage)) {
88
- opts.storage = resolve(process.cwd(), options.storage);
89
- }
84
+ force: false,
85
+ },
86
+ ...options,
87
+ };
88
+
89
+ if (options.storage && options.storage !== ':memory:') {
90
+ if (!isAbsolute(options.storage)) {
91
+ opts.storage = resolve(process.cwd(), options.storage);
90
92
  }
91
- this.sequelize = new Sequelize(opts);
92
- this.options = opts;
93
93
  }
94
94
 
95
+ if (options.dialect === 'sqlite') {
96
+ delete opts.timezone;
97
+ } else if (!opts.timezone) {
98
+ opts.timezone = '+00:00';
99
+ }
100
+
101
+ this.sequelize = new Sequelize(opts);
102
+ this.options = opts;
95
103
  this.collections = new Map();
96
104
  this.modelHook = new ModelHook(this);
97
105
 
@@ -116,6 +124,32 @@ export class Database extends EventEmitter implements AsyncEmitter {
116
124
  }
117
125
 
118
126
  this.initOperators();
127
+
128
+ const migratorOptions: any = this.options.migrator || {};
129
+
130
+ const context = {
131
+ db: this,
132
+ sequelize: this.sequelize,
133
+ queryInterface: this.sequelize.getQueryInterface(),
134
+ ...migratorOptions.context,
135
+ };
136
+
137
+ this.migrations = new Migrations(context);
138
+
139
+ this.migrator = new Umzug({
140
+ logger: migratorOptions.logger || console,
141
+ migrations: this.migrations.callback(),
142
+ context,
143
+ storage: new SequelizeStorage({
144
+ modelName: `${this.options.tablePrefix||''}migrations`,
145
+ ...migratorOptions.storage,
146
+ sequelize: this.sequelize,
147
+ }),
148
+ });
149
+ }
150
+
151
+ addMigration(item) {
152
+ return this.migrations.add(item);
119
153
  }
120
154
 
121
155
  /**
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export * from './database';
4
4
  export { Database as default } from './database';
5
5
  export * from './fields';
6
6
  export * from './magic-attribute-model';
7
+ export * from './migration';
7
8
  export * from './mock-database';
8
9
  export * from './model';
9
10
  export * from './relation-repository/belongs-to-many-repository';
@@ -0,0 +1,75 @@
1
+ import { QueryInterface, Sequelize } from 'sequelize';
2
+ import Database from './database';
3
+
4
+ export interface MigrationContext {
5
+ db: Database;
6
+ queryInterface: QueryInterface;
7
+ sequelize: Sequelize;
8
+ }
9
+
10
+ export class Migration {
11
+ public name: string;
12
+
13
+ public context: { db: Database };
14
+
15
+ constructor(context: MigrationContext) {
16
+ this.context = context;
17
+ }
18
+
19
+ get db() {
20
+ return this.context.db;
21
+ }
22
+
23
+ get sequelize() {
24
+ return this.context.db.sequelize;
25
+ }
26
+
27
+ get queryInterface() {
28
+ return this.context.db.sequelize.getQueryInterface();
29
+ }
30
+
31
+ async up() {
32
+ // todo
33
+ }
34
+
35
+ async down() {
36
+ // todo
37
+ }
38
+ }
39
+
40
+ export interface MigrationItem {
41
+ name: string;
42
+ migration?: typeof Migration;
43
+ up?: any;
44
+ down?: any;
45
+ }
46
+
47
+ export class Migrations {
48
+ items = [];
49
+ context: any;
50
+
51
+ constructor(context: any) {
52
+ this.context = context;
53
+ }
54
+
55
+ clear() {
56
+ this.items = [];
57
+ }
58
+
59
+ add(item: MigrationItem) {
60
+ const Migration = item.migration;
61
+ if (Migration) {
62
+ const migration = new Migration(this.context);
63
+ migration.name = item.name;
64
+ this.items.push(migration);
65
+ } else {
66
+ this.items.push(item);
67
+ }
68
+ }
69
+
70
+ callback() {
71
+ return (ctx) => {
72
+ return this.items;
73
+ };
74
+ }
75
+ }
@@ -33,6 +33,7 @@ export function getConfigByEnv() {
33
33
  charset: 'utf8mb4',
34
34
  collate: 'utf8mb4_unicode_ci',
35
35
  },
36
+ timezone: process.env.DB_TIMEZONE,
36
37
  };
37
38
  }
38
39