anote-server-libs 0.0.4 → 0.1.1

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.
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiCallRepository = void 0;
4
+ const mssql_1 = require("mssql");
5
+ const ModelDao_1 = require("./repository/ModelDao");
6
+ class ApiCallRepository extends ModelDao_1.ModelDao {
7
+ constructor(pool, poolMssql, logger) {
8
+ super(pool, poolMssql, logger, 'api_call', 5, pool ? 'id=$1,"updatedOn"=$2,"responseCode"=$3,"responseJson"=$4,"expiresAt"=$5' :
9
+ 'id=@1,"updatedOn"=@2,"responseCode"=@3,"responseJson"=@4,"expiresAt"=@5');
10
+ this.pool = pool;
11
+ this.poolMssql = poolMssql;
12
+ this.logger = logger;
13
+ }
14
+ buildObject(q) {
15
+ if (!q)
16
+ return undefined;
17
+ q.id = q.id.trim();
18
+ q.updatedOn = q.updatedOn && new Date(q.updatedOn);
19
+ q.responseCode = parseInt(q.responseCode, 10);
20
+ q.expiresAt = q.expiresAt && new Date(q.expiresAt);
21
+ return q;
22
+ }
23
+ serialize(instance, request) {
24
+ if (request) {
25
+ request.input('1', instance.id);
26
+ request.input('2', mssql_1.DateTimeOffset, instance.updatedOn);
27
+ request.input('3', instance.responseCode);
28
+ request.input('4', instance.responseJson);
29
+ request.input('5', mssql_1.DateTimeOffset, instance.expiresAt);
30
+ return undefined;
31
+ }
32
+ else {
33
+ return [instance.id, instance.updatedOn, instance.responseCode, instance.responseJson, instance.expiresAt];
34
+ }
35
+ }
36
+ }
37
+ exports.ApiCallRepository = ApiCallRepository;
package/models/ApiCall.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ConnectionPool, DateTimeOffset, Request } from 'mssql';
1
2
  import {Pool} from 'pg';
2
3
  import {Logger} from 'winston';
3
4
  import {Model, ModelDao} from './repository/ModelDao';
@@ -9,8 +10,9 @@ export interface ApiCall extends Model<string> {
9
10
  }
10
11
 
11
12
  export class ApiCallRepository extends ModelDao<string, ApiCall> {
12
- constructor(protected pool: Pool, protected logger: Logger) {
13
- super(pool, logger, 'api_call', 5, 'id=$1,"updatedOn"=$2,"responseCode"=$3,"responseJson"=$4,"expiresAt"=$5');
13
+ constructor(protected pool: Pool, protected poolMssql: ConnectionPool, protected logger: Logger) {
14
+ super(pool, poolMssql, logger, 'api_call', 5, pool ? 'id=$1,"updatedOn"=$2,"responseCode"=$3,"responseJson"=$4,"expiresAt"=$5' :
15
+ 'id=@1,"updatedOn"=@2,"responseCode"=@3,"responseJson"=@4,"expiresAt"=@5');
14
16
  }
15
17
 
16
18
  buildObject(q: any): ApiCall {
@@ -22,7 +24,17 @@ export class ApiCallRepository extends ModelDao<string, ApiCall> {
22
24
  return q;
23
25
  }
24
26
 
25
- serialize(instance: ApiCall): any[] {
26
- return [instance.id, instance.updatedOn, instance.responseCode, instance.responseJson, instance.expiresAt];
27
+ serialize(instance: ApiCall, request?: Request) {
28
+ if(request) {
29
+ request.input('1', instance.id);
30
+ request.input('2', DateTimeOffset, instance.updatedOn);
31
+ request.input('3', instance.responseCode);
32
+ request.input('4', instance.responseJson);
33
+ request.input('5', DateTimeOffset, instance.expiresAt);
34
+ return undefined;
35
+ } else {
36
+ return [instance.id, instance.updatedOn, instance.responseCode, instance.responseJson, instance.expiresAt];
37
+ }
27
38
  }
39
+
28
40
  }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MigrationRepository = void 0;
4
+ const ModelDao_1 = require("./repository/ModelDao");
5
+ class MigrationRepository extends ModelDao_1.ModelDao {
6
+ constructor(pool, poolMssql, logger) {
7
+ super(pool, poolMssql, logger, 'migration', 5, pool ? 'id=$1,hash=$2,"sqlUp"=$3,"sqlDown"=$4,state=$5' :
8
+ 'id=@1,hash=@2,"sqlUp"=@3,"sqlDown"=@4,state=@5');
9
+ this.pool = pool;
10
+ this.poolMssql = poolMssql;
11
+ this.logger = logger;
12
+ }
13
+ buildObject(q) {
14
+ if (!q)
15
+ return undefined;
16
+ q.id = parseInt(q.id, 10);
17
+ q.hash = q.hash.trim();
18
+ q.state = parseInt(q.state, 10);
19
+ return q;
20
+ }
21
+ serialize(instance, request) {
22
+ if (request) {
23
+ request.input('1', instance.id);
24
+ request.input('2', instance.hash);
25
+ request.input('3', instance.sqlUp);
26
+ request.input('4', instance.sqlDown);
27
+ request.input('5', instance.state);
28
+ return undefined;
29
+ }
30
+ else {
31
+ return [instance.id, instance.hash, instance.sqlUp, instance.sqlDown, instance.state];
32
+ }
33
+ }
34
+ }
35
+ exports.MigrationRepository = MigrationRepository;
@@ -1,3 +1,4 @@
1
+ import { ConnectionPool, Request } from 'mssql';
1
2
  import {Pool} from 'pg';
2
3
  import {Logger} from 'winston';
3
4
  import {ModelDao} from './repository/ModelDao';
@@ -11,8 +12,9 @@ export interface Migration {
11
12
  }
12
13
 
13
14
  export class MigrationRepository extends ModelDao<number, Migration> {
14
- constructor(protected pool: Pool, protected logger: Logger) {
15
- super(pool, logger, 'migration', 5, 'id=$1,hash=$2,"sqlUp"=$3,"sqlDown"=$4,state=$5');
15
+ constructor(protected pool: Pool, protected poolMssql: ConnectionPool, protected logger: Logger) {
16
+ super(pool, poolMssql, logger, 'migration', 5, pool ? 'id=$1,hash=$2,"sqlUp"=$3,"sqlDown"=$4,state=$5' :
17
+ 'id=@1,hash=@2,"sqlUp"=@3,"sqlDown"=@4,state=@5');
16
18
  }
17
19
 
18
20
  buildObject(q: any): Migration {
@@ -23,7 +25,16 @@ export class MigrationRepository extends ModelDao<number, Migration> {
23
25
  return q;
24
26
  }
25
27
 
26
- serialize(instance: Migration): any[] {
27
- return [instance.id, instance.hash, instance.sqlUp, instance.sqlDown, instance.state];
28
+ serialize(instance: Migration, request?: Request) {
29
+ if(request) {
30
+ request.input('1', instance.id);
31
+ request.input('2', instance.hash);
32
+ request.input('3', instance.sqlUp);
33
+ request.input('4', instance.sqlDown);
34
+ request.input('5', instance.state);
35
+ return undefined;
36
+ } else {
37
+ return [instance.id, instance.hash, instance.sqlUp, instance.sqlDown, instance.state];
38
+ }
28
39
  }
29
40
  }
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseModelRepository = void 0;
4
+ const fs = require("fs");
5
+ const node_forge_1 = require("node-forge");
6
+ const ApiCall_1 = require("../ApiCall");
7
+ const Migration_1 = require("../Migration");
8
+ class BaseModelRepository {
9
+ constructor(db, dbSpare, dbMssql, logger, cache) {
10
+ this.db = db;
11
+ this.dbSpare = dbSpare;
12
+ this.dbMssql = dbMssql;
13
+ this.logger = logger;
14
+ this.cache = cache;
15
+ if (db && dbSpare) {
16
+ const dbQuery = db.query.bind(db);
17
+ db.query = (function (text, values, cb) {
18
+ if ((this.idleCount + this.waitingCount) >= this.totalCount && this.totalCount === this.options.max)
19
+ return dbSpare.query(text, values, cb);
20
+ return dbQuery(text, values, cb);
21
+ }).bind(db);
22
+ }
23
+ this.Migration = new Migration_1.MigrationRepository(db, dbMssql, logger);
24
+ this.ApiCall = new ApiCall_1.ApiCallRepository(db, dbMssql, logger);
25
+ }
26
+ migrate(migrationsPath, callback) {
27
+ (this.db ? this.db.query('CREATE TABLE IF NOT EXISTS migration (' +
28
+ 'id integer PRIMARY KEY,' +
29
+ 'hash text NOT NULL,' +
30
+ '"sqlUp" text NOT NULL,' +
31
+ '"sqlDown" text NOT NULL,' +
32
+ 'state integer NOT NULL' +
33
+ ')') : this.dbMssql.query('if not exists (select * from sysobjects where name=\'migration\' and xtype=\'U\') CREATE TABLE migration (' +
34
+ 'id int PRIMARY KEY,' +
35
+ 'hash text NOT NULL,' +
36
+ '"sqlUp" text NOT NULL,' +
37
+ '"sqlDown" text NOT NULL,' +
38
+ 'state int NOT NULL' +
39
+ ')')).then(() => {
40
+ this.Migration.getAllBy('id').then((migrations) => {
41
+ if (migrations.find(migration => migration.state !== 0))
42
+ process.exit(4);
43
+ fs.readdir(migrationsPath, (_, files) => {
44
+ const migrationsAvailable = files
45
+ .filter(file => /[0-9]+\.sql/.test(file))
46
+ .map(file => parseInt(file.split('.sql')[0], 10))
47
+ .filter(file => file > 0)
48
+ .sort((a, b) => a - b)
49
+ .map(file => {
50
+ const content = fs.readFileSync(migrationsPath + file + '.sql', 'utf-8');
51
+ return {
52
+ id: file,
53
+ content: content,
54
+ hash: node_forge_1.md.sha256.create().update(content).digest().toHex()
55
+ };
56
+ });
57
+ if (migrationsAvailable.length === 0
58
+ || migrationsAvailable.length
59
+ !== migrationsAvailable[migrationsAvailable.length - 1].id)
60
+ process.exit(5);
61
+ let highestCommon = 0;
62
+ while (highestCommon < migrations.length && highestCommon < migrationsAvailable.length
63
+ && migrations[highestCommon].hash === migrationsAvailable[highestCommon].hash)
64
+ highestCommon++;
65
+ this.applyDownUntil(migrations, migrations.length, highestCommon).then(() => {
66
+ this.applyUpUntil(migrationsAvailable, highestCommon, migrationsAvailable.length).then(callback, process.exit);
67
+ }, process.exit);
68
+ });
69
+ }, () => process.exit(3));
70
+ }, () => process.exit(2));
71
+ }
72
+ lockTables(tables, client) {
73
+ if (this.db)
74
+ return Promise.all(tables.map(t => client.query('LOCK TABLE ' + t.table + ' IN EXCLUSIVE MODE')));
75
+ return Promise.all(tables.map(t => client.request().query('SELECT id FROM ' + t.table + ' WITH (UPDLOCK)')));
76
+ }
77
+ applyUpUntil(migrations, current, until) {
78
+ if (current < until)
79
+ return this.applyUp(migrations[current]).then(() => this.applyUpUntil(migrations, current + 1, until));
80
+ return Promise.resolve();
81
+ }
82
+ applyUp(migration) {
83
+ return new Promise((resolve, reject) => {
84
+ const sqlParts = migration.content.split('----');
85
+ this.Migration.create({
86
+ id: migration.id,
87
+ hash: migration.hash,
88
+ sqlUp: sqlParts[0],
89
+ sqlDown: sqlParts[1],
90
+ state: 2
91
+ }).then(() => {
92
+ (this.db || this.dbMssql).query(sqlParts[0], (err) => {
93
+ if (err) {
94
+ console.error(err);
95
+ reject(10);
96
+ }
97
+ else {
98
+ (this.db || this.dbMssql).query('UPDATE "migration" SET "state"=0 WHERE "id"=' + migration.id, (err2) => {
99
+ if (err2)
100
+ reject(11);
101
+ else
102
+ resolve();
103
+ });
104
+ }
105
+ });
106
+ }, () => process.exit(9));
107
+ });
108
+ }
109
+ applyDownUntil(migrations, current, until) {
110
+ if (current > until) {
111
+ current--;
112
+ return this.applyDown(migrations[current]).then(() => this.applyDownUntil(migrations, current, until));
113
+ }
114
+ return Promise.resolve();
115
+ }
116
+ applyDown(migration) {
117
+ return new Promise((resolve, reject) => {
118
+ (this.db || this.dbMssql).query('UPDATE "migration" SET "state"=1 WHERE "id"=' + migration.id, (err) => {
119
+ if (err)
120
+ reject(6);
121
+ else
122
+ (this.db || this.dbMssql).query(migration.sqlDown, (err2) => {
123
+ if (err2) {
124
+ console.error(err2);
125
+ reject(7);
126
+ }
127
+ else
128
+ (this.db || this.dbMssql).query('DELETE FROM "migration" WHERE "id"=' + migration.id, (err3) => {
129
+ if (err3 && migration.id !== 1)
130
+ reject(8);
131
+ else {
132
+ if (migration.id === 1) {
133
+ (this.db ? this.db.query('CREATE TABLE IF NOT EXISTS migration (' +
134
+ 'id integer PRIMARY KEY,' +
135
+ 'hash text NOT NULL,' +
136
+ '"sqlUp" text NOT NULL,' +
137
+ '"sqlDown" text NOT NULL,' +
138
+ 'state integer NOT NULL' +
139
+ ')') : this.dbMssql.query('if not exists (select * from sysobjects where name=\'migration\' and xtype=\'U\') CREATE TABLE migration (' +
140
+ 'id int PRIMARY KEY,' +
141
+ 'hash text NOT NULL,' +
142
+ '"sqlUp" text NOT NULL,' +
143
+ '"sqlDown" text NOT NULL,' +
144
+ 'state int NOT NULL' +
145
+ ')')).then(() => resolve(), reject);
146
+ }
147
+ else
148
+ resolve();
149
+ }
150
+ });
151
+ });
152
+ });
153
+ });
154
+ }
155
+ }
156
+ exports.BaseModelRepository = BaseModelRepository;
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'fs';
2
2
  import * as Memcached from 'memcached';
3
+ import {ConnectionPool, Transaction} from 'mssql';
3
4
  import {md} from 'node-forge';
4
5
  import {ClientBase, Pool} from 'pg';
5
6
  import {Logger} from 'winston';
@@ -11,25 +12,33 @@ export class BaseModelRepository {
11
12
  Migration: MigrationRepository;
12
13
  ApiCall: ApiCallRepository;
13
14
 
14
- constructor(public db: Pool, public dbSpare: Pool, public logger: Logger, public cache: Memcached) {
15
- const dbQuery = db.query.bind(db);
16
- db.query = (function(text: any, values: any, cb: any) {
17
- if((this.idleCount + this.waitingCount) >= this.totalCount && this.totalCount === this.options.max)
18
- return dbSpare.query(text, values, cb);
19
- return dbQuery(text, values, cb);
20
- }).bind(db);
21
- this.Migration = new MigrationRepository(db, logger);
22
- this.ApiCall = new ApiCallRepository(db, logger);
15
+ constructor(public db: Pool, public dbSpare: Pool, public dbMssql: ConnectionPool, public logger: Logger, public cache: Memcached) {
16
+ if(db && dbSpare) {
17
+ const dbQuery = db.query.bind(db);
18
+ db.query = (function(text: any, values: any, cb: any) {
19
+ if((this.idleCount + this.waitingCount) >= this.totalCount && this.totalCount === this.options.max)
20
+ return dbSpare.query(text, values, cb);
21
+ return dbQuery(text, values, cb);
22
+ }).bind(db);
23
+ }
24
+ this.Migration = new MigrationRepository(db, dbMssql, logger);
25
+ this.ApiCall = new ApiCallRepository(db, dbMssql, logger);
23
26
  }
24
27
 
25
28
  migrate(migrationsPath: string, callback: (() => void)) {
26
- this.db.query('CREATE TABLE IF NOT EXISTS migration (' +
29
+ (this.db ? this.db.query('CREATE TABLE IF NOT EXISTS migration (' +
27
30
  'id integer PRIMARY KEY,' +
28
31
  'hash text NOT NULL,' +
29
32
  '"sqlUp" text NOT NULL,' +
30
33
  '"sqlDown" text NOT NULL,' +
31
34
  'state integer NOT NULL' +
32
- ')').then(() => {
35
+ ')') : this.dbMssql.query('if not exists (select * from sysobjects where name=\'migration\' and xtype=\'U\') CREATE TABLE migration (' +
36
+ 'id int PRIMARY KEY,' +
37
+ 'hash text NOT NULL,' +
38
+ '"sqlUp" text NOT NULL,' +
39
+ '"sqlDown" text NOT NULL,' +
40
+ 'state int NOT NULL' +
41
+ ')')).then(() => {
33
42
  this.Migration.getAllBy('id').then((migrations: Migration[]) => {
34
43
  if(migrations.find(migration => migration.state !== 0)) process.exit(4); // Have to fix manually
35
44
  // Read the new ones
@@ -62,8 +71,10 @@ export class BaseModelRepository {
62
71
  }, () => process.exit(2));
63
72
  }
64
73
 
65
- lockTables(tables: ModelRepr[], client: ClientBase): Promise<any> {
66
- return Promise.all(tables.map(t => client.query('LOCK TABLE ' + t.table + ' IN EXCLUSIVE MODE')));
74
+ lockTables(tables: ModelRepr[], client: ClientBase | Transaction): Promise<any> {
75
+ if(this.db)
76
+ return Promise.all(tables.map(t => (<ClientBase>client).query('LOCK TABLE ' + t.table + ' IN EXCLUSIVE MODE')));
77
+ return Promise.all(tables.map(t => (<Transaction>client).request().query('SELECT id FROM ' + t.table + ' WITH (UPDLOCK)')));
67
78
  }
68
79
 
69
80
  private applyUpUntil(migrations: {id: number, content: string, hash: string}[], current: number, until: number): Promise<void> {
@@ -82,12 +93,12 @@ export class BaseModelRepository {
82
93
  sqlDown: sqlParts[1],
83
94
  state: 2
84
95
  }).then(() => {
85
- this.db.query(sqlParts[0], (err: any) => {
96
+ (<any>(this.db|| this.dbMssql)).query(sqlParts[0], (err: any) => {
86
97
  if(err) {
87
98
  console.error(err);
88
99
  reject(10);
89
100
  } else {
90
- this.db.query('UPDATE "migration" SET "state"=0 WHERE "id"=' + migration.id, (err2: any) => {
101
+ (<any>(this.db|| this.dbMssql)).query('UPDATE "migration" SET "state"=0 WHERE "id"=' + migration.id, (err2: any) => {
91
102
  if(err2) reject(11); // No cleanup
92
103
  else resolve();
93
104
  });
@@ -107,24 +118,30 @@ export class BaseModelRepository {
107
118
 
108
119
  private applyDown(migration: Migration): Promise<void> {
109
120
  return new Promise((resolve, reject) => {
110
- this.db.query('UPDATE "migration" SET "state"=1 WHERE "id"=' + migration.id, (err: any) => {
121
+ (<any>(this.db|| this.dbMssql)).query('UPDATE "migration" SET "state"=1 WHERE "id"=' + migration.id, (err: any) => {
111
122
  if(err) reject(6); // No required change
112
- else this.db.query(migration.sqlDown, (err2: any) => {
123
+ else (<any>(this.db|| this.dbMssql)).query(migration.sqlDown, (err2: any) => {
113
124
  if(err2) {
114
125
  console.error(err2);
115
126
  reject(7);
116
127
  } // No apply down
117
- else this.db.query('DELETE FROM "migration" WHERE "id"=' + migration.id, (err3: any) => {
128
+ else (<any>(this.db|| this.dbMssql)).query('DELETE FROM "migration" WHERE "id"=' + migration.id, (err3: any) => {
118
129
  if(err3 && migration.id !== 1) reject(8); // No cleanup for not base migration
119
130
  else {
120
131
  if(migration.id === 1) {
121
- this.db.query('CREATE TABLE IF NOT EXISTS migration (' +
132
+ (this.db ? this.db.query('CREATE TABLE IF NOT EXISTS migration (' +
122
133
  'id integer PRIMARY KEY,' +
123
134
  'hash text NOT NULL,' +
124
135
  '"sqlUp" text NOT NULL,' +
125
136
  '"sqlDown" text NOT NULL,' +
126
137
  'state integer NOT NULL' +
127
- ')').then(() => resolve(), reject);
138
+ ')') : this.dbMssql.query('if not exists (select * from sysobjects where name=\'migration\' and xtype=\'U\') CREATE TABLE migration (' +
139
+ 'id int PRIMARY KEY,' +
140
+ 'hash text NOT NULL,' +
141
+ '"sqlUp" text NOT NULL,' +
142
+ '"sqlDown" text NOT NULL,' +
143
+ 'state int NOT NULL' +
144
+ ')')).then(() => resolve(), reject);
128
145
  } else resolve();
129
146
  }
130
147
  });
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryCache = void 0;
4
+ class MemoryCache {
5
+ constructor(localKey, cache) {
6
+ this.localKey = localKey;
7
+ this.cache = cache;
8
+ this.localCache = {};
9
+ }
10
+ list() {
11
+ if (this.cache)
12
+ return new Promise(resolve => {
13
+ let pending = true;
14
+ setTimeout(() => {
15
+ if (pending) {
16
+ pending = false;
17
+ resolve([]);
18
+ }
19
+ }, 250);
20
+ this.cache.items((_, data) => {
21
+ if (pending) {
22
+ pending = false;
23
+ resolve(data.map(s => {
24
+ const value = s[Object.getOwnPropertyNames(s).find(sname => sname.startsWith(this.localKey))];
25
+ return value && JSON.parse(value);
26
+ }).filter(x => x));
27
+ }
28
+ });
29
+ });
30
+ else
31
+ return Promise.resolve(Object.getOwnPropertyNames(this.localCache).map(key => this.localCache[key]));
32
+ }
33
+ get(key) {
34
+ if (this.cache)
35
+ return new Promise(resolve => {
36
+ let pending = true;
37
+ setTimeout(() => {
38
+ if (pending) {
39
+ pending = false;
40
+ resolve(undefined);
41
+ }
42
+ }, 250);
43
+ this.cache.get(this.localKey + key, (_, data) => {
44
+ if (pending) {
45
+ pending = false;
46
+ resolve(data && JSON.parse(data));
47
+ }
48
+ });
49
+ });
50
+ else
51
+ return Promise.resolve(this.localCache[key]);
52
+ }
53
+ set(key, val) {
54
+ if (this.cache)
55
+ return new Promise(resolve => this.cache.add(this.localKey + key, JSON.stringify(val), 15 * 60, resolve));
56
+ else {
57
+ this.localCache[key] = val;
58
+ return Promise.resolve();
59
+ }
60
+ }
61
+ delete(key) {
62
+ if (this.cache) {
63
+ return new Promise(resolve => {
64
+ let fired = false;
65
+ const handler = setTimeout(() => {
66
+ fired = true;
67
+ resolve();
68
+ }, 50);
69
+ this.cache.del(this.localKey + key, () => {
70
+ if (!fired) {
71
+ clearTimeout(handler);
72
+ resolve();
73
+ }
74
+ });
75
+ });
76
+ }
77
+ else {
78
+ this.localCache[key] = undefined;
79
+ return Promise.resolve();
80
+ }
81
+ }
82
+ clear() {
83
+ if (this.cache) {
84
+ this.localKey = this.localKey + '_';
85
+ if (this.localKey.length > 40)
86
+ this.localKey = this.localKey.replace(/_+$/, '');
87
+ }
88
+ else
89
+ this.localCache = {};
90
+ }
91
+ }
92
+ exports.MemoryCache = MemoryCache;