@liminalfunctions/framework 1.0.51 → 1.0.53
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/dist/F_Collection.d.ts +2 -0
- package/dist/F_Collection.js +9 -0
- package/dist/F_Collection.js.map +1 -1
- package/dist/F_Compile.js +247 -0
- package/dist/F_Compile.js.map +1 -1
- package/dist/F_Security_Models/F_SM_Role_Membership.d.ts +2 -2
- package/dist/F_Security_Models/F_SM_Role_Membership.js +10 -6
- package/dist/F_Security_Models/F_SM_Role_Membership.js.map +1 -1
- package/dist/code_generation/generate_client_library.d.ts +1 -0
- package/dist/code_generation/generate_client_library.js +37 -16
- package/dist/code_generation/generate_client_library.js.map +1 -1
- package/dist/code_generation/templates/collection.mustache +79 -1
- package/dist/utils/array_children_from_zod.d.ts +3 -0
- package/dist/utils/array_children_from_zod.js +39 -0
- package/dist/utils/array_children_from_zod.js.map +1 -0
- package/dist/utils/mongoose_from_zod.js +7 -2
- package/dist/utils/mongoose_from_zod.js.map +1 -1
- package/package.json +1 -1
- package/src/F_Collection.ts +14 -3
- package/src/F_Compile.ts +288 -16
- package/src/F_Security_Models/F_SM_Role_Membership.ts +12 -8
- package/src/code_generation/generate_client_library.ts +35 -8
- package/src/code_generation/templates/collection.mustache +79 -1
- package/src/utils/array_children_from_zod.ts +44 -0
- package/src/utils/mongoose_from_zod.ts +7 -3
- package/test/0_1_mongoose_from_zod.test.ts +16 -10
- package/test/0_6_array_children.test.ts +127 -0
- package/test/1_0_basic_server.test.ts +107 -2
- package/test/1_1_security_ownership.test.ts +186 -6
- package/test/1_2_role_membership.test.ts +329 -9
- package/test/2_1_client_library_generation.test.ts +125 -1
- package/test/tmp/dist/Brief_News_Category.js.map +1 -1
- package/test/tmp/dist/Client.js.map +1 -1
- package/test/tmp/dist/Institution.js.map +1 -1
- package/test/tmp/dist/Project.d.ts +20 -0
- package/test/tmp/dist/Project.js +63 -0
- package/test/tmp/dist/Project.js.map +1 -1
- package/test/tmp/dist/types/project.d.ts +4 -0
- package/test/tmp/dist/types/project_post.d.ts +4 -0
- package/test/tmp/dist/types/project_put.d.ts +4 -0
- package/test/tmp/package-lock.json +111 -111
- package/test/tmp/src/Brief_News_Category.ts +3 -1
- package/test/tmp/src/Client.ts +3 -1
- package/test/tmp/src/Institution.ts +3 -1
- package/test/tmp/src/Project.ts +73 -1
- package/test/tmp/src/types/project.ts +4 -0
- package/test/tmp/src/types/project_post.ts +4 -0
- package/test/tmp/src/types/project_put.ts +4 -0
|
@@ -130,4 +130,82 @@ class Document {
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
{{/has_subcollections}}
|
|
133
|
-
|
|
133
|
+
|
|
134
|
+
{{#has_array_children}}
|
|
135
|
+
{{#array_children}}
|
|
136
|
+
array(key: "{{array_name}}"): Collection_{{my_built_collection}}_Array_{{array_name}};
|
|
137
|
+
{{/array_children}}
|
|
138
|
+
array(key: string) {
|
|
139
|
+
switch(key) {
|
|
140
|
+
{{#array_children}}
|
|
141
|
+
case "{{array_name}}":
|
|
142
|
+
return new Collection_{{my_built_collection}}_Array_{{array_name}}([...this.path, this.document_id, "{{array_name}}"], this.get_auth);
|
|
143
|
+
{{/array_children}}
|
|
144
|
+
default:
|
|
145
|
+
throw new Error(`Collection ${this.collection_id} does not have an array at the key ${key}`)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
{{/has_array_children}}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
{{#array_children}}
|
|
152
|
+
{{array_type_put_definition}}
|
|
153
|
+
{{array_type_post_definition}}
|
|
154
|
+
|
|
155
|
+
export class Collection_{{my_built_collection}}_Array_{{array_name}} {
|
|
156
|
+
path: string[]
|
|
157
|
+
get_auth: () => Promise<any>
|
|
158
|
+
collection_id: string
|
|
159
|
+
collection_name_plural: string
|
|
160
|
+
array_key: string
|
|
161
|
+
|
|
162
|
+
constructor(path: string[], get_auth: () => Promise<any>) {
|
|
163
|
+
this.path = path;
|
|
164
|
+
this.get_auth = get_auth;
|
|
165
|
+
this.collection_id = "{{collection_id}}";
|
|
166
|
+
this.collection_name_plural = "{{collection_name_plural}}"
|
|
167
|
+
this.array_key = "{{array_name}}"
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async push(document: {{type_array_child_post}}): Promise<{{type_return}}>{
|
|
171
|
+
try {
|
|
172
|
+
let result = await ky.post(this.path.join('/'), {
|
|
173
|
+
headers: {
|
|
174
|
+
authorization: await this.get_auth()
|
|
175
|
+
},
|
|
176
|
+
json: document
|
|
177
|
+
}).json() as Response<{{type_return}}>;
|
|
178
|
+
return result.data;
|
|
179
|
+
} catch(err){
|
|
180
|
+
return Promise.reject(err)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async replace(document: {{type_array_child_put}}): Promise<{{type_return}}>{
|
|
185
|
+
try {
|
|
186
|
+
let result = await ky.put([...this.path, document._id].join('/'), {
|
|
187
|
+
headers: {
|
|
188
|
+
authorization: await this.get_auth()
|
|
189
|
+
},
|
|
190
|
+
json: document
|
|
191
|
+
}).json() as Response<{{type_return}}>;
|
|
192
|
+
return result.data;
|
|
193
|
+
} catch(err){
|
|
194
|
+
return Promise.reject(err)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async delete(document_id: string): Promise<{{type_return}}>{
|
|
199
|
+
try {
|
|
200
|
+
let result = await ky.delete([...this.path, document_id].join('/'), {
|
|
201
|
+
headers: {
|
|
202
|
+
authorization: await this.get_auth()
|
|
203
|
+
}
|
|
204
|
+
}).json() as Response<{{type_return}}>;
|
|
205
|
+
return result.data;
|
|
206
|
+
} catch(err){
|
|
207
|
+
return Promise.reject(err)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
{{/array_children}}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import z from "zod/v4";
|
|
2
|
+
import { validator_group } from './zod_loop_seperator.js';
|
|
3
|
+
export declare function array_children_from_zod(zod_definition: z.ZodObject, loop_detector?: Map<any, validator_group>, built_map?: Map<string, z.ZodObject>, prefix?: string): Map<string, z.ZodObject>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { find_loops } from './zod_loop_seperator.js';
|
|
2
|
+
export function array_children_from_zod(zod_definition, loop_detector, built_map, prefix = '') {
|
|
3
|
+
let loops = loop_detector ?? find_loops(zod_definition);
|
|
4
|
+
let results = built_map ?? new Map();
|
|
5
|
+
for (let [key, value] of Object.entries(zod_definition.shape)) {
|
|
6
|
+
if (loops.has(value._zod.def)) {
|
|
7
|
+
continue;
|
|
8
|
+
}
|
|
9
|
+
let real_value = distill_zod(value);
|
|
10
|
+
switch (real_value._zod.def.type) {
|
|
11
|
+
case "object":
|
|
12
|
+
array_children_from_zod(real_value, loop_detector, results, prefix.length > 0 ? `${prefix}.${key}` : key);
|
|
13
|
+
break;
|
|
14
|
+
case "array":
|
|
15
|
+
let element = distill_zod(real_value.element);
|
|
16
|
+
if (element._zod.def.type === 'object') {
|
|
17
|
+
let objdef = element._zod.def;
|
|
18
|
+
if (objdef.shape._id) {
|
|
19
|
+
results.set(prefix.length > 0 ? `${prefix}.${key}` : key, element);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return results;
|
|
28
|
+
}
|
|
29
|
+
function distill_zod(zod_definition) {
|
|
30
|
+
switch (zod_definition._zod.def.type) {
|
|
31
|
+
case "nullable":
|
|
32
|
+
return zod_definition._zod.def.innerType;
|
|
33
|
+
case "optional":
|
|
34
|
+
return zod_definition._zod.def.innerType;
|
|
35
|
+
default:
|
|
36
|
+
return zod_definition;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=array_children_from_zod.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"array_children_from_zod.js","sourceRoot":"","sources":["../../src/utils/array_children_from_zod.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAmB,MAAM,yBAAyB,CAAA;AAErE,MAAM,UAAU,uBAAuB,CAAC,cAA2B,EAAE,aAAyC,EAAE,SAAoC,EAAE,SAAiB,EAAE;IACrK,IAAI,KAAK,GAAG,aAAa,IAAI,UAAU,CAAC,cAA2B,CAAC,CAAC;IACrE,IAAI,OAAO,GAAG,SAAS,IAAI,IAAI,GAAG,EAAuB,CAAC;IAE1D,KAAI,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,EAAC,CAAC;QAC1D,IAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAC,CAAC;YAAC,SAAS;QAAC,CAAC;QAC1C,IAAI,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACpC,QAAQ,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAC/B,KAAK,QAAQ;gBACT,uBAAuB,CAAC,UAAyB,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;gBACxH,MAAM;YACV,KAAK,OAAO;gBAER,IAAI,OAAO,GAAG,WAAW,CAAE,UAAyB,CAAC,OAAO,CAAC,CAAC;gBAC9D,IAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACpC,IAAI,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAA2B,CAAC;oBACtD,IAAG,MAAM,CAAC,KAAK,CAAC,GAAG,EAAC,CAAC;wBACjB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,EAAG,OAAsB,CAAC,CAAC;oBACvF,CAAC;gBACL,CAAC;gBACD,MAAM;YACV;gBACI,MAAM;QACd,CAAC;IAEL,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,SAAS,WAAW,CAAC,cAAyB;IAC1C,QAAQ,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACnC,KAAK,UAAU;YAEX,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;QAC7C,KAAK,UAAU;YAEX,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;QAC7C;YACI,OAAO,cAAc,CAAC;IAC9B,CAAC;AACL,CAAC"}
|
|
@@ -49,7 +49,7 @@ export const z_mongodb_id_nullable = z.custom((val) => {
|
|
|
49
49
|
}).meta({ framework_override_type: 'mongodb_id', nullable: true });
|
|
50
50
|
export function mongoose_from_zod(schema_name, zod_definition, database = mongoose) {
|
|
51
51
|
let mongoose_schema = schema_from_zod(zod_definition);
|
|
52
|
-
return database.model(schema_name, new Schema(mongoose_schema, { typeKey: 'mongoose_type' }));
|
|
52
|
+
return database.model(schema_name, new Schema(mongoose_schema, { typeKey: 'mongoose_type', minimize: false }));
|
|
53
53
|
}
|
|
54
54
|
export function schema_from_zod(zod_definition) {
|
|
55
55
|
let loops = find_loops(zod_definition);
|
|
@@ -95,7 +95,6 @@ export function schema_entry_from_zod(zod_definition, loop_detector) {
|
|
|
95
95
|
result.required = !zod_definition.safeParse(undefined).success;
|
|
96
96
|
return result;
|
|
97
97
|
case "nullable":
|
|
98
|
-
return schema_entry_from_zod(zod_definition._zod.def.innerType, loop_detector);
|
|
99
98
|
case "optional":
|
|
100
99
|
return parse_optional(zod_definition._zod.def, loop_detector);
|
|
101
100
|
case "record":
|
|
@@ -158,6 +157,12 @@ function parse_object(def, loop_detector) {
|
|
|
158
157
|
}
|
|
159
158
|
retval[key] = schema_entry_from_zod(value, loop_detector);
|
|
160
159
|
}
|
|
160
|
+
if (!retval._id) {
|
|
161
|
+
retval._id = false;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
delete retval._id;
|
|
165
|
+
}
|
|
161
166
|
return { mongoose_type: retval, required: true };
|
|
162
167
|
}
|
|
163
168
|
function parse_array(def, loop_detector) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongoose_from_zod.js","sourceRoot":"","sources":["../../src/utils/mongoose_from_zod.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"mongoose_from_zod.js","sourceRoot":"","sources":["../../src/utils/mongoose_from_zod.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/F_Collection.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { mongoose_from_zod, schema_from_zod } from "./utils/mongoose_from_zod.js
|
|
|
3
3
|
import mongoose, { Collection, Model, ObjectId } from "mongoose";
|
|
4
4
|
import { F_Security_Model } from "./F_Security_Models/F_Security_Model.js";
|
|
5
5
|
import { query_validator_from_zod } from "./utils/query_validator_from_zod.js";
|
|
6
|
+
import { array_children_from_zod } from "./utils/array_children_from_zod.js";
|
|
6
7
|
|
|
7
8
|
export type CollectionType<Col extends F_Collection<string, Validator>, Validator extends z.ZodObject> = z.output<Col['validator']>;
|
|
8
9
|
|
|
@@ -29,6 +30,9 @@ export class F_Collection<Collection_ID extends string, ZodSchema extends z.ZodO
|
|
|
29
30
|
post_validator: ZodPartial_Return_Type<ZodSchema['partial']>;
|
|
30
31
|
is_compiled: boolean;
|
|
31
32
|
|
|
33
|
+
array_children_map: Map<string, z.ZodType>;
|
|
34
|
+
array_children_post_map: Map<string, z.ZodType>;
|
|
35
|
+
|
|
32
36
|
access_layers: F_Layer<Collection_ID, ZodSchema>[];
|
|
33
37
|
create_hooks: ((session: mongoose.mongo.ClientSession, created_document: z.output<ZodSchema>) => Promise<void>)[];
|
|
34
38
|
update_hooks: ((session: mongoose.mongo.ClientSession, updated_document: z.output<ZodSchema>) => Promise<void>)[];
|
|
@@ -43,8 +47,6 @@ export class F_Collection<Collection_ID extends string, ZodSchema extends z.ZodO
|
|
|
43
47
|
this.validator = validator;
|
|
44
48
|
this.mongoose_schema = schema_from_zod(validator);
|
|
45
49
|
this.mongoose_model = mongoose_from_zod(collection_name, validator, database);
|
|
46
|
-
// TODO: validate that the model doesn't use any fields that have special meaning in the query validator; for example: [param]_gt, [param]_in, sort,
|
|
47
|
-
//@ts-ignore
|
|
48
50
|
this.query_validator_server = query_validator_from_zod(validator, 'server');
|
|
49
51
|
this.query_validator_client = query_validator_from_zod(validator, 'client');
|
|
50
52
|
// TODO: we can make this more closely match the mongoDB PUT operation and allow updates to eg array.3.element fields
|
|
@@ -55,13 +57,22 @@ export class F_Collection<Collection_ID extends string, ZodSchema extends z.ZodO
|
|
|
55
57
|
if(this.validator._zod.def.shape._id.meta()?.framework_override_type !== 'mongodb_id'){
|
|
56
58
|
throw new Error(`_id must be a mongoDB ID. Use the z_mongodb_id special field.`)
|
|
57
59
|
}
|
|
58
|
-
|
|
60
|
+
|
|
61
|
+
this.array_children_map = array_children_from_zod(validator);
|
|
62
|
+
|
|
59
63
|
// TODO: find a more elegant way to do this so that the types don't have a cow
|
|
60
64
|
//@ts-ignore
|
|
61
65
|
this.put_validator = validator.partial();
|
|
62
66
|
// @ts-ignore
|
|
63
67
|
this.post_validator = Object.hasOwn(this.validator._zod.def.shape, '_id') ? validator.partial({ _id: true }) : validator;
|
|
64
68
|
|
|
69
|
+
this.array_children_post_map = new Map<string, z.ZodType>();
|
|
70
|
+
Array.from(this.array_children_map.entries()).forEach((keyval: [string, z.ZodType]) => {
|
|
71
|
+
let [key, value] = keyval;
|
|
72
|
+
// @ts-ignore
|
|
73
|
+
this.array_children_post_map.set(key, value.partial({ _id: true }));
|
|
74
|
+
})
|
|
75
|
+
|
|
65
76
|
this.access_layers = [];
|
|
66
77
|
this.is_compiled = false;
|
|
67
78
|
this.create_hooks = [];
|
package/src/F_Compile.ts
CHANGED
|
@@ -193,6 +193,12 @@ export function compile<Collection_ID extends string, ZodSchema extends z.ZodObj
|
|
|
193
193
|
return;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
if(req.body._id && req.body._id !== req.params.document_id){
|
|
197
|
+
res.status(400);
|
|
198
|
+
res.json({ error: `mismatch between document ID and request body _id` });
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
196
202
|
let find = { '_id': req.params.document_id } as { [key: string]: any } ;
|
|
197
203
|
for(let layer of access_layers.layers){
|
|
198
204
|
find[`${layer}_id`] = req.params[layer];
|
|
@@ -254,19 +260,11 @@ export function compile<Collection_ID extends string, ZodSchema extends z.ZodObj
|
|
|
254
260
|
return;
|
|
255
261
|
}
|
|
256
262
|
}
|
|
257
|
-
|
|
258
|
-
/*let { error: pre_save_error } = await req.schema.handle_pre_save(req, value);
|
|
259
|
-
if (pre_save_error) {
|
|
260
|
-
res.status(400);
|
|
261
|
-
res.json({ error: pre_save_error.message });
|
|
262
|
-
return;
|
|
263
|
-
}*/
|
|
264
263
|
|
|
265
264
|
|
|
266
265
|
let results;
|
|
267
266
|
try {
|
|
268
267
|
results = await collection.perform_update_and_side_effects(find, validated_request_body);
|
|
269
|
-
//results = await collection.mongoose_model.findOneAndUpdate(find, validated_request_body, { returnDocument: 'after', lean: true });
|
|
270
268
|
} catch(err){
|
|
271
269
|
res.status(500);
|
|
272
270
|
res.json({ error: `there was a novel error` });
|
|
@@ -358,17 +356,9 @@ export function compile<Collection_ID extends string, ZodSchema extends z.ZodObj
|
|
|
358
356
|
}
|
|
359
357
|
}
|
|
360
358
|
|
|
361
|
-
/*let { error: pre_save_error } = await req.schema.handle_pre_save(req, validated_request_body);
|
|
362
|
-
if (pre_save_error) {
|
|
363
|
-
res.status(400);
|
|
364
|
-
res.json({ error: pre_save_error.message });
|
|
365
|
-
return;
|
|
366
|
-
}*/
|
|
367
|
-
|
|
368
359
|
let results;
|
|
369
360
|
try {
|
|
370
361
|
results = await collection.perform_create_and_side_effects(validated_request_body);
|
|
371
|
-
//results = await collection.mongoose_model.create(validated_request_body);
|
|
372
362
|
} catch(err){
|
|
373
363
|
res.status(500);
|
|
374
364
|
res.json({ error: `there was a novel error` });
|
|
@@ -428,5 +418,287 @@ export function compile<Collection_ID extends string, ZodSchema extends z.ZodObj
|
|
|
428
418
|
}
|
|
429
419
|
// await req.schema.fire_api_event('delete', req, results);
|
|
430
420
|
});
|
|
421
|
+
|
|
422
|
+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
423
|
+
//////////////////////////////////////// operate on array children /////////////////////////////////////
|
|
424
|
+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
425
|
+
|
|
426
|
+
for(let [array_child_path, array_child_validator] of collection.array_children_map.entries()){
|
|
427
|
+
|
|
428
|
+
let post_validator = collection.array_children_post_map.get(array_child_path);
|
|
429
|
+
|
|
430
|
+
let array_child_post_path = [
|
|
431
|
+
api_prefix,
|
|
432
|
+
...base_layers_path_components,
|
|
433
|
+
`${collection.collection_id}/:document_id`,
|
|
434
|
+
array_child_path
|
|
435
|
+
].join('/');
|
|
436
|
+
|
|
437
|
+
app.post(array_child_post_path, async (req, res) => {
|
|
438
|
+
if (!isValidObjectId(req.params.document_id)) {
|
|
439
|
+
res.status(400);
|
|
440
|
+
res.json({ error: `${req.params.document_id} is not a valid document ID.` });
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
let find = { '_id': req.params.document_id } as { [key: string]: any } ;
|
|
445
|
+
for(let layer of access_layers.layers){
|
|
446
|
+
find[`${layer}_id`] = req.params[layer];
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// I'd like to have a validator here. I think it might need to be a map or record validator?
|
|
450
|
+
let permissive_security_model = await F_Security_Model.model_with_permission(access_layers.security_models, req, res, find, 'update');
|
|
451
|
+
if (!permissive_security_model) {
|
|
452
|
+
res.status(403);
|
|
453
|
+
res.json({ error: `You do not have permission to update documents from ${collection.collection_id}.` });
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
let metadata_updater: any = {};
|
|
458
|
+
if(collection.mongoose_schema.updated_by?.type === String) {
|
|
459
|
+
// if the security schema required the user to be logged in, then req.auth.user_id will not be null
|
|
460
|
+
if((req as Authenticated_Request).auth?.user_id){
|
|
461
|
+
metadata_updater.updated_by = (req as Authenticated_Request).auth?.user_id;
|
|
462
|
+
} else {
|
|
463
|
+
metadata_updater.updated_by = null;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if(collection.mongoose_schema.updated_at?.type === Date) {
|
|
468
|
+
metadata_updater.updated_at = new Date();
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
let validated_request_body;
|
|
472
|
+
try {
|
|
473
|
+
validated_request_body = await post_validator.parse(req.body);
|
|
474
|
+
} catch(err){
|
|
475
|
+
if(err instanceof z.ZodError){
|
|
476
|
+
res.status(400);
|
|
477
|
+
res.json({ error: err.issues });
|
|
478
|
+
return;
|
|
479
|
+
} else {
|
|
480
|
+
console.error(err);
|
|
481
|
+
res.status(500);
|
|
482
|
+
res.json({ error: `there was a novel error` });
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
try {
|
|
488
|
+
detect_malicious_keys(validated_request_body);
|
|
489
|
+
} catch(err){
|
|
490
|
+
res.status(403);
|
|
491
|
+
res.json({ error: `Found an unacceptable JSON key in the request body.` });
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
let results;
|
|
496
|
+
try {
|
|
497
|
+
//array_child_path
|
|
498
|
+
results = await collection.perform_update_and_side_effects(find, {
|
|
499
|
+
$push: {
|
|
500
|
+
[array_child_path]: validated_request_body,
|
|
501
|
+
},
|
|
502
|
+
...metadata_updater
|
|
503
|
+
});
|
|
504
|
+
} catch(err){
|
|
505
|
+
res.status(500);
|
|
506
|
+
res.json({ error: `there was a novel error` });
|
|
507
|
+
console.error(err);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (!results) {
|
|
512
|
+
let sendable = await permissive_security_model.handle_empty_query_results(req, res, 'update');
|
|
513
|
+
res.json(sendable);
|
|
514
|
+
} else {
|
|
515
|
+
res.json({ data: results });
|
|
516
|
+
}
|
|
517
|
+
//await req.schema.fire_api_event('update', req, results);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
let array_child_put_path = [
|
|
523
|
+
api_prefix,
|
|
524
|
+
...base_layers_path_components,
|
|
525
|
+
`${collection.collection_id}/:document_id`,
|
|
526
|
+
array_child_path,
|
|
527
|
+
':array_item_id'
|
|
528
|
+
].join('/')
|
|
529
|
+
|
|
530
|
+
app.put(array_child_put_path, async (req, res) => {
|
|
531
|
+
if (!isValidObjectId(req.params.document_id)) {
|
|
532
|
+
res.status(400);
|
|
533
|
+
res.json({ error: `${req.params.document_id} is not a valid document ID.` });
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (!isValidObjectId(req.params.array_item_id)) {
|
|
538
|
+
res.status(400);
|
|
539
|
+
res.json({ error: `${req.params.array_item_id} is not a valid document ID.` });
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if(req.body._id && req.body._id !== req.params.array_item_id){
|
|
544
|
+
res.status(400);
|
|
545
|
+
res.json({ error: `cannot update element _id.` });
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
let find = { '_id': req.params.document_id } as { [key: string]: any } ;
|
|
550
|
+
for(let layer of access_layers.layers){
|
|
551
|
+
find[`${layer}_id`] = req.params[layer];
|
|
552
|
+
}
|
|
553
|
+
find[`${array_child_path}._id`] = req.params.array_item_id;
|
|
554
|
+
|
|
555
|
+
// I'd like to have a validator here. I think it might need to be a map or record validator?
|
|
556
|
+
let permissive_security_model = await F_Security_Model.model_with_permission(access_layers.security_models, req, res, find, 'update');
|
|
557
|
+
if (!permissive_security_model) {
|
|
558
|
+
res.status(403);
|
|
559
|
+
res.json({ error: `You do not have permission to update documents from ${collection.collection_id}.` });
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
let metadata_updater: any = {};
|
|
564
|
+
if(collection.mongoose_schema.updated_by?.type === String) {
|
|
565
|
+
// if the security schema required the user to be logged in, then req.auth.user_id will not be null
|
|
566
|
+
if((req as Authenticated_Request).auth?.user_id){
|
|
567
|
+
metadata_updater.updated_by = (req as Authenticated_Request).auth?.user_id;
|
|
568
|
+
} else {
|
|
569
|
+
metadata_updater.updated_by = null;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if(collection.mongoose_schema.updated_at?.type === Date) {
|
|
574
|
+
metadata_updater.updated_at = new Date();
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
let validated_request_body;
|
|
578
|
+
try {
|
|
579
|
+
validated_request_body = await array_child_validator.parse(req.body);
|
|
580
|
+
} catch(err){
|
|
581
|
+
if(err instanceof z.ZodError){
|
|
582
|
+
res.status(400);
|
|
583
|
+
res.json({ error: err.issues });
|
|
584
|
+
return;
|
|
585
|
+
} else {
|
|
586
|
+
console.error(err);
|
|
587
|
+
res.status(500);
|
|
588
|
+
res.json({ error: `there was a novel error` });
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
try {
|
|
594
|
+
detect_malicious_keys(validated_request_body);
|
|
595
|
+
} catch(err){
|
|
596
|
+
res.status(403);
|
|
597
|
+
res.json({ error: `Found an unacceptable JSON key in the request body.` });
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
let results;
|
|
602
|
+
try {
|
|
603
|
+
//array_child_path
|
|
604
|
+
results = await collection.perform_update_and_side_effects(find, {
|
|
605
|
+
$set: {
|
|
606
|
+
[`${array_child_path}.$`]: validated_request_body,
|
|
607
|
+
},
|
|
608
|
+
...metadata_updater
|
|
609
|
+
});
|
|
610
|
+
} catch(err){
|
|
611
|
+
res.status(500);
|
|
612
|
+
res.json({ error: `there was a novel error` });
|
|
613
|
+
console.error(err);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (!results) {
|
|
618
|
+
let sendable = await permissive_security_model.handle_empty_query_results(req, res, 'update');
|
|
619
|
+
res.json(sendable);
|
|
620
|
+
} else {
|
|
621
|
+
res.json({ data: results });
|
|
622
|
+
}
|
|
623
|
+
//await req.schema.fire_api_event('update', req, results);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
let array_child_delete_path = [
|
|
627
|
+
api_prefix,
|
|
628
|
+
...base_layers_path_components,
|
|
629
|
+
`${collection.collection_id}/:document_id`,
|
|
630
|
+
array_child_path,
|
|
631
|
+
':array_item_id'
|
|
632
|
+
].join('/')
|
|
633
|
+
|
|
634
|
+
app.delete(array_child_delete_path, async (req, res) => {
|
|
635
|
+
if (!isValidObjectId(req.params.document_id)) {
|
|
636
|
+
res.status(400);
|
|
637
|
+
res.json({ error: `${req.params.document_id} is not a valid document ID.` });
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (!isValidObjectId(req.params.array_item_id)) {
|
|
642
|
+
res.status(400);
|
|
643
|
+
res.json({ error: `${req.params.array_item_id} is not a valid document ID.` });
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
let find = { '_id': req.params.document_id } as { [key: string]: any } ;
|
|
648
|
+
for(let layer of access_layers.layers){
|
|
649
|
+
find[`${layer}_id`] = req.params[layer];
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// I'd like to have a validator here. I think it might need to be a map or record validator?
|
|
653
|
+
let permissive_security_model = await F_Security_Model.model_with_permission(access_layers.security_models, req, res, find, 'update');
|
|
654
|
+
if (!permissive_security_model) {
|
|
655
|
+
res.status(403);
|
|
656
|
+
res.json({ error: `You do not have permission to update documents from ${collection.collection_id}.` });
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
let metadata_updater: any = {};
|
|
661
|
+
if(collection.mongoose_schema.updated_by?.type === String) {
|
|
662
|
+
// if the security schema required the user to be logged in, then req.auth.user_id will not be null
|
|
663
|
+
if((req as Authenticated_Request).auth?.user_id){
|
|
664
|
+
metadata_updater.updated_by = (req as Authenticated_Request).auth?.user_id;
|
|
665
|
+
} else {
|
|
666
|
+
metadata_updater.updated_by = null;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if(collection.mongoose_schema.updated_at?.type === Date) {
|
|
671
|
+
metadata_updater.updated_at = new Date();
|
|
672
|
+
}
|
|
673
|
+
let results;
|
|
674
|
+
try {
|
|
675
|
+
//array_child_path
|
|
676
|
+
results = await collection.perform_update_and_side_effects(find, {
|
|
677
|
+
$pull: {
|
|
678
|
+
[array_child_path]: {_id: req.params.array_item_id},
|
|
679
|
+
},
|
|
680
|
+
...metadata_updater
|
|
681
|
+
});
|
|
682
|
+
} catch(err){
|
|
683
|
+
res.status(500);
|
|
684
|
+
res.json({ error: `there was a novel error` });
|
|
685
|
+
console.error(err);
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
if (!results) {
|
|
690
|
+
let sendable = await permissive_security_model.handle_empty_query_results(req, res, 'update');
|
|
691
|
+
res.json(sendable);
|
|
692
|
+
} else {
|
|
693
|
+
res.json({ data: results });
|
|
694
|
+
}
|
|
695
|
+
//await req.schema.fire_api_event('update', req, results);
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
|
|
431
703
|
}
|
|
432
704
|
}
|
|
@@ -15,14 +15,14 @@ let operation_permission_map = {
|
|
|
15
15
|
export class F_SM_Role_Membership<Collection_ID extends string, ZodSchema extends z.ZodObject> extends F_Security_Model<Collection_ID, ZodSchema> {
|
|
16
16
|
user_id_field: string;
|
|
17
17
|
role_id_field: string;
|
|
18
|
-
layer_collection_id
|
|
18
|
+
layer_collection_id?: string;
|
|
19
19
|
role_membership_collection: F_Collection<string, any>;
|
|
20
20
|
role_membership_cache: Cache<any>;
|
|
21
21
|
role_collection: F_Collection<string, any>;
|
|
22
22
|
role_cache: Cache<any>;
|
|
23
23
|
|
|
24
24
|
constructor(collection: F_Collection<Collection_ID, ZodSchema>,
|
|
25
|
-
layer_collection: F_Collection<string, any
|
|
25
|
+
layer_collection: F_Collection<string, any> | undefined,
|
|
26
26
|
role_membership_collection: F_Collection<string, any>,
|
|
27
27
|
role_collection: F_Collection<string, any>,
|
|
28
28
|
role_membership_cache?: Cache<any>,
|
|
@@ -34,7 +34,7 @@ export class F_SM_Role_Membership<Collection_ID extends string, ZodSchema extend
|
|
|
34
34
|
this.needs_auth_user = true;
|
|
35
35
|
this.user_id_field = user_id_field;
|
|
36
36
|
this.role_id_field = role_id_field;
|
|
37
|
-
this.layer_collection_id = layer_collection
|
|
37
|
+
this.layer_collection_id = layer_collection?.collection_id;
|
|
38
38
|
this.role_membership_collection = role_membership_collection;
|
|
39
39
|
this.role_membership_cache = role_membership_cache ?? new Cache(60);
|
|
40
40
|
this.role_collection = role_collection;
|
|
@@ -49,16 +49,20 @@ export class F_SM_Role_Membership<Collection_ID extends string, ZodSchema extend
|
|
|
49
49
|
let user_id = req.auth.user_id;
|
|
50
50
|
// the only way the layer ID is undefined is if the layer is the document being accessed
|
|
51
51
|
// eg the institution id or the client id
|
|
52
|
-
let layer_document_id = req.params[this.layer_collection_id] ?? req.params.document_id;
|
|
52
|
+
let layer_document_id = this.layer_collection_id ? (req.params[this.layer_collection_id] ?? req.params.document_id) : undefined;
|
|
53
|
+
let cache_key = this.layer_collection_id ? `${user_id}-${layer_document_id}` : user_id;
|
|
53
54
|
|
|
54
55
|
// return the role membership associated with the layer. This uses the cache heavily, so it should be
|
|
55
56
|
// a cheap operation even though it makes an extra database query. Use the cache's first_fetch_then_refresh
|
|
56
57
|
// method so that we aren't keeping out-of-date auth data in the cache.
|
|
57
|
-
let role_membership = await this.role_membership_cache.first_fetch_then_refresh(
|
|
58
|
-
let
|
|
58
|
+
let role_membership = await this.role_membership_cache.first_fetch_then_refresh(cache_key, async () => {
|
|
59
|
+
let find: {[key: string]: any} = {
|
|
59
60
|
[this.user_id_field]: user_id,
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
};
|
|
62
|
+
if(this.layer_collection_id){
|
|
63
|
+
find[`${this.layer_collection_id}_id`] = new mongoose.Types.ObjectId(layer_document_id)
|
|
64
|
+
}
|
|
65
|
+
let role_memberships = await this.role_membership_collection.mongoose_model.find(find, {}, {lean: true})
|
|
62
66
|
|
|
63
67
|
if(role_memberships.length > 1){
|
|
64
68
|
console.warn(`in F_SM_Role_Membership, more than one role membership for user ${user_id} at layer ${this.layer_collection_id} found.`)
|