@modular-rest/server 1.6.5 → 1.7.0
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/README.md +2 -0
- package/package.json +8 -3
- package/src/application.js +32 -68
- package/src/class/collection_definition.js +32 -24
- package/src/class/combinator.js +82 -71
- package/src/class/database_trigger.js +18 -12
- package/src/class/db_schemas.js +11 -11
- package/src/class/security.js +105 -31
- package/src/class/trigger_operator.js +35 -31
- package/src/index.js +2 -25
- package/src/middlewares.js +14 -1
- package/src/services/data_provider/service.js +173 -177
- package/src/services/user_manager/service.js +139 -30
- package/tsconfig.json +10 -0
|
@@ -1,38 +1,42 @@
|
|
|
1
1
|
class TriggerOperator {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
constructor() {
|
|
3
|
+
this.triggers = [];
|
|
4
|
+
}
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
/**
|
|
7
|
+
* add a collection trigger
|
|
8
|
+
* @param {object} trigger DatabaseTrigger object
|
|
9
|
+
*/
|
|
10
|
+
addTrigger(trigger) {
|
|
11
|
+
this.triggers.push(trigger);
|
|
12
|
+
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Call a trigger
|
|
16
|
+
* @param {string} operation operation name
|
|
17
|
+
* @param {string} database database name
|
|
18
|
+
* @param {string} collection collection name
|
|
19
|
+
* @param {string} data
|
|
20
|
+
*/
|
|
21
|
+
call(operation, database, collection, data) {
|
|
22
|
+
let result;
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
this.triggers.forEach((trigger) => {
|
|
25
|
+
if (
|
|
26
|
+
operation == trigger.operation &&
|
|
27
|
+
database == trigger.database &&
|
|
28
|
+
collection == trigger.collection
|
|
29
|
+
)
|
|
30
|
+
result = trigger.callback(data.input, data.output);
|
|
31
|
+
});
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static get instance() {
|
|
37
|
+
return instance;
|
|
38
|
+
}
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
module.exports = TriggerOperator.instance;
|
|
41
|
+
const instance = new TriggerOperator();
|
|
42
|
+
module.exports = TriggerOperator.instance;
|
package/src/index.js
CHANGED
|
@@ -14,44 +14,21 @@ const CollectionDefinition = require("./class/collection_definition");
|
|
|
14
14
|
const Schemas = require("./class/db_schemas");
|
|
15
15
|
const DatabaseTrigger = require("./class/database_trigger");
|
|
16
16
|
const SecurityClass = require("./class/security");
|
|
17
|
-
|
|
18
17
|
const middleware = require("./middlewares");
|
|
18
|
+
const userManager = require("./services/user_manager/service");
|
|
19
19
|
|
|
20
20
|
module.exports = {
|
|
21
21
|
createRest,
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
// Utilities
|
|
25
|
-
//
|
|
26
22
|
reply,
|
|
27
23
|
TypeCasters,
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @type {import('./class/paginator').create}
|
|
31
|
-
*/
|
|
32
24
|
paginator,
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @type {import('./class/validator')}
|
|
36
|
-
*/
|
|
37
25
|
validator,
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @type {import('./services/data_provider/service').getCollection}
|
|
41
|
-
* @return {import('mongoose').Model} Mongoose model https://mongoosejs.com/docs/api/model.html
|
|
42
|
-
*/
|
|
43
26
|
getCollection,
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
// Base class
|
|
47
|
-
//
|
|
48
27
|
CollectionDefinition,
|
|
49
28
|
Schemas,
|
|
50
29
|
Schema,
|
|
51
30
|
DatabaseTrigger,
|
|
52
|
-
|
|
53
31
|
...SecurityClass,
|
|
54
|
-
|
|
55
|
-
// Middlewares
|
|
56
32
|
middleware,
|
|
33
|
+
userManager: userManager.main,
|
|
57
34
|
};
|
package/src/middlewares.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validator module
|
|
3
|
+
* @module class/validator
|
|
4
|
+
*/
|
|
1
5
|
let validateObject = require("./class/validator");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* User manager service
|
|
9
|
+
* @module services/user_manager/service
|
|
10
|
+
*/
|
|
2
11
|
const userManager = require("./services/user_manager/service");
|
|
3
12
|
|
|
4
13
|
/**
|
|
@@ -9,7 +18,7 @@ const userManager = require("./services/user_manager/service");
|
|
|
9
18
|
* @param {Function} next - Koa next function
|
|
10
19
|
* @returns {Promise<void>}
|
|
11
20
|
*/
|
|
12
|
-
|
|
21
|
+
async function auth(ctx, next) {
|
|
13
22
|
let headers = ctx.header;
|
|
14
23
|
let headersValidated = validateObject(headers, "authorization");
|
|
15
24
|
|
|
@@ -27,4 +36,8 @@ module.exports.auth = async (ctx, next) => {
|
|
|
27
36
|
console.log(err);
|
|
28
37
|
ctx.throw(err.status || 412, err.message);
|
|
29
38
|
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = {
|
|
42
|
+
auth,
|
|
30
43
|
};
|
|
@@ -1,196 +1,193 @@
|
|
|
1
|
-
let name =
|
|
2
|
-
const colog = require(
|
|
3
|
-
let {
|
|
4
|
-
AccessTypes,
|
|
5
|
-
AccessDefinition
|
|
6
|
-
} = require('../../class/security');
|
|
1
|
+
let name = "dataProvider";
|
|
2
|
+
const colog = require("colog");
|
|
3
|
+
let { AccessTypes, AccessDefinition } = require("../../class/security");
|
|
7
4
|
|
|
8
|
-
const Mongoose = require(
|
|
9
|
-
Mongoose.set(
|
|
5
|
+
const Mongoose = require("mongoose");
|
|
6
|
+
Mongoose.set("useCreateIndex", true);
|
|
10
7
|
|
|
11
8
|
let connections = {};
|
|
12
9
|
let collections = {};
|
|
13
10
|
let permissionDefinitions = {};
|
|
14
11
|
|
|
15
|
-
let triggers = require(
|
|
16
|
-
let TypeCasters = require(
|
|
12
|
+
let triggers = require("../../class/trigger_operator");
|
|
13
|
+
let TypeCasters = require("./typeCasters");
|
|
17
14
|
|
|
18
15
|
/**
|
|
19
|
-
*
|
|
16
|
+
*
|
|
20
17
|
* @param {string} dbName database name
|
|
21
18
|
* @param {array} CollectionDefinitionList an array of CollectionDefinition instance
|
|
22
19
|
* @param {object} mongoOption
|
|
23
20
|
* @param {string} mongoOption.dbPrefix
|
|
24
21
|
* @param {string} mongoOption.mongoBaseAddress
|
|
25
22
|
*/
|
|
26
|
-
function connectToDatabaseByCollectionDefinitionList(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
23
|
+
function connectToDatabaseByCollectionDefinitionList(
|
|
24
|
+
dbName,
|
|
25
|
+
collectionDefinitionList = [],
|
|
26
|
+
mongoOption
|
|
27
|
+
) {
|
|
28
|
+
return new Promise((done, reject) => {
|
|
29
|
+
// Create db connection
|
|
30
|
+
//
|
|
31
|
+
const fullDbName = (mongoOption.dbPrefix || "") + dbName;
|
|
32
|
+
const connectionString = mongoOption.mongoBaseAddress + "/" + fullDbName;
|
|
33
|
+
|
|
34
|
+
colog.info(`- Connecting to database ${connectionString}`);
|
|
35
|
+
|
|
36
|
+
let connection = Mongoose.createConnection(connectionString, {
|
|
37
|
+
...mongoOption,
|
|
38
|
+
useUnifiedTopology: true,
|
|
39
|
+
useNewUrlParser: true,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Store connection
|
|
43
|
+
connections[dbName] = connection;
|
|
44
|
+
|
|
45
|
+
// add db models from schemas
|
|
46
|
+
collectionDefinitionList.forEach((collectionDefinition) => {
|
|
47
|
+
let collection = collectionDefinition.collection;
|
|
48
|
+
let schema = collectionDefinition.schema;
|
|
49
|
+
|
|
50
|
+
if (collections[dbName] == undefined) collections[dbName] = {};
|
|
51
|
+
|
|
52
|
+
if (permissionDefinitions[dbName] == undefined)
|
|
53
|
+
permissionDefinitions[dbName] = {};
|
|
54
|
+
|
|
55
|
+
// create model from schema
|
|
56
|
+
// and store in on global collection object
|
|
57
|
+
let model = connection.model(collection, schema);
|
|
58
|
+
collections[dbName][collection] = model;
|
|
59
|
+
|
|
60
|
+
// define Access Definition from component permissions
|
|
61
|
+
// and store it on global access definition object
|
|
62
|
+
permissionDefinitions[dbName][collection] = new AccessDefinition({
|
|
63
|
+
database: dbName,
|
|
64
|
+
collection: collection,
|
|
65
|
+
permissionList: collectionDefinition.permissions,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// add trigger
|
|
69
|
+
if (collectionDefinition.trigger != undefined) {
|
|
70
|
+
triggers.addTrigger(collectionDefinition.trigger);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
connection.on("connected", () => {
|
|
75
|
+
colog.success(`- ${fullDbName} database has been connected`);
|
|
76
|
+
done();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
82
79
|
}
|
|
83
80
|
|
|
84
81
|
/**
|
|
85
|
-
*
|
|
82
|
+
*
|
|
86
83
|
* @param {object} option
|
|
87
84
|
* @param {array} option.list an array of CollectionDefinition instance
|
|
88
85
|
* @param {object} option.mongoOption
|
|
89
86
|
* @param {string} option.mongoOption.dbPrefix
|
|
90
87
|
* @param {string} option.mongoOption.mongoBaseAddress
|
|
91
88
|
*/
|
|
92
|
-
async function addCollectionDefinitionByList({
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
89
|
+
async function addCollectionDefinitionByList({ list, mongoOption }) {
|
|
90
|
+
let clusteredByDBName = {};
|
|
91
|
+
|
|
92
|
+
// cluster list by their database name.
|
|
93
|
+
list.forEach((collectionDefinition) => {
|
|
94
|
+
let database = collectionDefinition.database;
|
|
95
|
+
if (!clusteredByDBName[database]) clusteredByDBName[database] = [];
|
|
96
|
+
clusteredByDBName[database].push(collectionDefinition);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// connect to databases
|
|
100
|
+
for (const dbName in clusteredByDBName) {
|
|
101
|
+
if (clusteredByDBName.hasOwnProperty(dbName)) {
|
|
102
|
+
const collectionDefinitionList = clusteredByDBName[dbName];
|
|
103
|
+
await connectToDatabaseByCollectionDefinitionList(
|
|
104
|
+
dbName,
|
|
105
|
+
collectionDefinitionList,
|
|
106
|
+
mongoOption
|
|
107
|
+
);
|
|
111
108
|
}
|
|
109
|
+
}
|
|
112
110
|
}
|
|
113
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Get a collection from a database.
|
|
114
|
+
* @param {string} db - The database name.
|
|
115
|
+
* @param {string} collection - The collection name.
|
|
116
|
+
* @returns {import('mongoose').Model} The found collection.
|
|
117
|
+
*/
|
|
114
118
|
function getCollection(db, collection) {
|
|
115
|
-
|
|
119
|
+
let fountCollection;
|
|
116
120
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
if (collections.hasOwnProperty(db)) {
|
|
122
|
+
if (collections[db].hasOwnProperty(collection))
|
|
123
|
+
fountCollection = collections[db][collection];
|
|
124
|
+
}
|
|
121
125
|
|
|
122
|
-
|
|
126
|
+
return fountCollection;
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
function _getPermissionList(db, collection, operationType) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return permissionList;
|
|
131
|
-
|
|
132
|
-
permissionDefinition = permissionDefinitions[db][collection];
|
|
133
|
-
|
|
134
|
-
permissionDefinition.permissionList.forEach(permission => {
|
|
135
|
-
if (permission.onlyOwnData == true) {
|
|
136
|
-
permissionList.push(permission);
|
|
137
|
-
} else if (operationType == AccessTypes.read &&
|
|
138
|
-
permission.read == true) {
|
|
139
|
-
permissionList.push(permission);
|
|
140
|
-
} else if (operationType == AccessTypes.write &&
|
|
141
|
-
permission.write == true) {
|
|
142
|
-
permissionList.push(permission);
|
|
143
|
-
}
|
|
144
|
-
});
|
|
130
|
+
let permissionList = [];
|
|
131
|
+
let permissionDefinition;
|
|
132
|
+
|
|
133
|
+
if (!permissionDefinitions.hasOwnProperty(db)) return permissionList;
|
|
145
134
|
|
|
146
|
-
|
|
135
|
+
permissionDefinition = permissionDefinitions[db][collection];
|
|
136
|
+
|
|
137
|
+
permissionDefinition.permissionList.forEach((permission) => {
|
|
138
|
+
if (permission.onlyOwnData == true) {
|
|
139
|
+
permissionList.push(permission);
|
|
140
|
+
} else if (operationType == AccessTypes.read && permission.read == true) {
|
|
141
|
+
permissionList.push(permission);
|
|
142
|
+
} else if (operationType == AccessTypes.write && permission.write == true) {
|
|
143
|
+
permissionList.push(permission);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return permissionList;
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
function checkAccess(db, collection, operationType, queryOrDoc, user) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
permissionList.forEach(permission => {
|
|
154
|
-
let permissionType = permission.type;
|
|
155
|
-
|
|
156
|
-
if (permission.onlyOwnData == true) {
|
|
157
|
-
let owner = queryOrDoc.owner;
|
|
158
|
-
let userId = user.id;
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
if (owner.toString() == userId.toString())
|
|
162
|
-
key = true;
|
|
163
|
-
} catch (error) {
|
|
164
|
-
key = false;
|
|
165
|
-
}
|
|
166
|
-
} else if (operationType == AccessTypes.read) {
|
|
167
|
-
if (permission.read &&
|
|
168
|
-
user.permission[permissionType] == true)
|
|
169
|
-
key = true;
|
|
170
|
-
} else if (operationType == AccessTypes.write) {
|
|
171
|
-
|
|
172
|
-
if (permission.write &&
|
|
173
|
-
user.permission[permissionType] == true)
|
|
174
|
-
key = true;
|
|
175
|
-
}
|
|
176
|
-
});
|
|
151
|
+
let key = false;
|
|
152
|
+
let permissionList = _getPermissionList(db, collection, operationType);
|
|
177
153
|
|
|
178
|
-
|
|
179
|
-
|
|
154
|
+
permissionList.forEach((permission) => {
|
|
155
|
+
let permissionType = permission.type;
|
|
180
156
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
157
|
+
if (permission.onlyOwnData == true) {
|
|
158
|
+
let owner = queryOrDoc.owner;
|
|
159
|
+
let userId = user.id;
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
if (owner.toString() == userId.toString()) key = true;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
key = false;
|
|
165
|
+
}
|
|
166
|
+
} else if (operationType == AccessTypes.read) {
|
|
167
|
+
if (permission.read && user.permission[permissionType] == true)
|
|
168
|
+
key = true;
|
|
169
|
+
} else if (operationType == AccessTypes.write) {
|
|
170
|
+
if (permission.write && user.permission[permissionType] == true)
|
|
171
|
+
key = true;
|
|
187
172
|
}
|
|
173
|
+
});
|
|
188
174
|
|
|
189
|
-
|
|
175
|
+
return key;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function getAsID(strId) {
|
|
179
|
+
let id;
|
|
180
|
+
try {
|
|
181
|
+
id = Mongoose.Types.ObjectId(strId);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
console.log("strId did not cast objectId", e);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return id;
|
|
190
187
|
}
|
|
191
188
|
|
|
192
189
|
function performPopulateToQueryObject(queryObj, popArr = []) {
|
|
193
|
-
|
|
190
|
+
/*
|
|
194
191
|
https://mongoosejs.com/docs/populate.html
|
|
195
192
|
popArr must be contains this objects
|
|
196
193
|
{
|
|
@@ -198,36 +195,35 @@ function performPopulateToQueryObject(queryObj, popArr = []) {
|
|
|
198
195
|
select: 'name -_id',
|
|
199
196
|
}
|
|
200
197
|
*/
|
|
201
|
-
|
|
202
|
-
|
|
198
|
+
popArr.forEach((pop) => queryObj.populate(pop));
|
|
199
|
+
return queryObj;
|
|
203
200
|
}
|
|
204
201
|
|
|
205
202
|
function performAdditionalOptionsToQueryObject(queryObj, options) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
203
|
+
/**
|
|
204
|
+
* https://mongoosejs.com/docs/api/query.html#query_Query-sort
|
|
205
|
+
*
|
|
206
|
+
* Options must be contain a method name and an argument of above methods.
|
|
207
|
+
* {
|
|
208
|
+
* sort: '-_id',
|
|
209
|
+
* limit: 10,
|
|
210
|
+
* }
|
|
211
|
+
*/
|
|
212
|
+
Object.keys(options).forEach((method) => {
|
|
213
|
+
queryObj = queryObj[method](options[method]);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
return queryObj;
|
|
220
217
|
}
|
|
221
218
|
|
|
222
|
-
|
|
223
219
|
module.exports = {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
220
|
+
name,
|
|
221
|
+
getCollection,
|
|
222
|
+
addCollectionDefinitionByList,
|
|
223
|
+
checkAccess,
|
|
224
|
+
getAsID,
|
|
225
|
+
performPopulateToQueryObject,
|
|
226
|
+
performAdditionalOptionsToQueryObject,
|
|
227
|
+
triggers,
|
|
228
|
+
TypeCasters,
|
|
229
|
+
};
|