@hitchy/plugin-odem-rest 0.6.0 → 0.7.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.
- package/api/services/odem-rest/cors.js +1 -1
- package/index.js +100 -101
- package/package.json +6 -7
- package/readme.md +8 -8
package/index.js
CHANGED
|
@@ -4,7 +4,7 @@ const { posix: { resolve } } = require( "path" );
|
|
|
4
4
|
|
|
5
5
|
module.exports = function() {
|
|
6
6
|
const api = this;
|
|
7
|
-
const {
|
|
7
|
+
const { services: Services, models: Models, utility: { case: Case } } = api;
|
|
8
8
|
|
|
9
9
|
const logDebug = api.log( "hitchy:odem:rest:debug" );
|
|
10
10
|
const logError = api.log( "hitchy:odem:rest:error" );
|
|
@@ -23,10 +23,6 @@ module.exports = function() {
|
|
|
23
23
|
before.set( `ALL ${resolve( urlPrefix, ".schema" )}`, CORS.getRequestFilterForSchemata() );
|
|
24
24
|
after.set( `ALL ${resolve( urlPrefix, ".schema" )}`, reqNotSupported );
|
|
25
25
|
|
|
26
|
-
if ( api.plugins.authentication ) {
|
|
27
|
-
before.set( `GET ${resolve( urlPrefix, ".schema" )}`, Services.AuthorizationPolicyGenerator.mayAccess( "@hitchy.odem.schema" ) );
|
|
28
|
-
}
|
|
29
|
-
|
|
30
26
|
for ( let i = 0, numNames = modelNames.length; i < numNames; i++ ) {
|
|
31
27
|
const name = modelNames[i];
|
|
32
28
|
const routeName = Case.pascalToKebab( name );
|
|
@@ -36,55 +32,6 @@ module.exports = function() {
|
|
|
36
32
|
before.set( `ALL ${resolve( urlPrefix, routeName, ".schema" )}`, CORS.getRequestFilterForModelSchema( model ) );
|
|
37
33
|
before.set( `ALL ${resolve( urlPrefix, routeName, ":uuid" )}`, CORS.getRequestFilterForModelItem( model ) );
|
|
38
34
|
|
|
39
|
-
if ( api.plugins.authentication ) {
|
|
40
|
-
// add policies for additionally checking a requesting user's authorization
|
|
41
|
-
const modelUrl = resolve( urlPrefix, routeName );
|
|
42
|
-
const policy = action => Services.AuthorizationPolicyGenerator.mayAccess( `@hitchy.odem.model.${name}.${action}` );
|
|
43
|
-
|
|
44
|
-
const handlers = {
|
|
45
|
-
schema: policy( "schema" ),
|
|
46
|
-
create: policy( "create" ),
|
|
47
|
-
list: policy( "list" ),
|
|
48
|
-
read: policy( "read" ),
|
|
49
|
-
write: policy( "write" ),
|
|
50
|
-
check: policy( "check" ),
|
|
51
|
-
remove: policy( "remove" ),
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
// convenience routes
|
|
55
|
-
before.set( `GET ${resolve( modelUrl, "create" )}`, handlers.create );
|
|
56
|
-
before.set( `GET ${resolve( modelUrl, "write", ":uuid" )}`, handlers.write );
|
|
57
|
-
before.set( `GET ${resolve( modelUrl, "replace", ":uuid" )}`, handlers.write );
|
|
58
|
-
before.set( `GET ${resolve( modelUrl, "has", ":uuid" )}`, handlers.check );
|
|
59
|
-
before.set( `GET ${resolve( modelUrl, "remove", ":uuid" )}`, handlers.remove );
|
|
60
|
-
|
|
61
|
-
// extended routes
|
|
62
|
-
before.set( `GET ${resolve( modelUrl, ".schema" )}`, policy( "schema" ) );
|
|
63
|
-
|
|
64
|
-
// REST-compliant routes
|
|
65
|
-
before.set( `GET ${modelUrl}`, function( req, res, next ) {
|
|
66
|
-
const tail = req.url.substring( modelUrl.length ).trim();
|
|
67
|
-
|
|
68
|
-
if ( tail && tail !== "/" ) {
|
|
69
|
-
next();
|
|
70
|
-
} else {
|
|
71
|
-
handlers.list.call( this, req, res, next );
|
|
72
|
-
}
|
|
73
|
-
} );
|
|
74
|
-
before.set( `POST ${resolve( modelUrl )}`, handlers.create );
|
|
75
|
-
before.set( `GET ${resolve( modelUrl, ":uuid" )}`, ( req, res, next ) => {
|
|
76
|
-
if ( req.params.uuid === ".schema" ) {
|
|
77
|
-
next();
|
|
78
|
-
} else {
|
|
79
|
-
handlers.read.call( this, req, res, next );
|
|
80
|
-
}
|
|
81
|
-
} );
|
|
82
|
-
before.set( `PATCH ${resolve( modelUrl, ":uuid" )}`, handlers.write );
|
|
83
|
-
before.set( `PUT ${resolve( modelUrl, ":uuid" )}`, handlers.write );
|
|
84
|
-
before.set( `HEAD ${resolve( modelUrl, ":uuid" )}`, handlers.check );
|
|
85
|
-
before.set( `DELETE ${resolve( modelUrl, ":uuid" )}`, handlers.remove );
|
|
86
|
-
}
|
|
87
|
-
|
|
88
35
|
after.set( `ALL ${resolve( urlPrefix, routeName )}`, reqNotSupported );
|
|
89
36
|
}
|
|
90
37
|
|
|
@@ -123,7 +70,7 @@ module.exports = function() {
|
|
|
123
70
|
const routeName = Case.pascalToKebab( name );
|
|
124
71
|
const model = Models[name] || {};
|
|
125
72
|
|
|
126
|
-
addRoutesOnModel( routes, urlPrefix, routeName, model, convenience );
|
|
73
|
+
addRoutesOnModel( routes, urlPrefix, routeName, name, model, convenience );
|
|
127
74
|
}
|
|
128
75
|
|
|
129
76
|
routes.set( "HEAD " + resolve( urlPrefix, ":model" ), ( _, res ) => res.status( 404 ).send() );
|
|
@@ -133,6 +80,20 @@ module.exports = function() {
|
|
|
133
80
|
},
|
|
134
81
|
};
|
|
135
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Generates JSON response indicating rejection of access.
|
|
85
|
+
*
|
|
86
|
+
* @param {Hitchy.Core.ServerResponse} res response manager
|
|
87
|
+
* @returns {void}
|
|
88
|
+
*/
|
|
89
|
+
function resAccessForbidden( res ) {
|
|
90
|
+
res
|
|
91
|
+
.status( 403 )
|
|
92
|
+
.json( {
|
|
93
|
+
error: "access forbidden",
|
|
94
|
+
} );
|
|
95
|
+
}
|
|
96
|
+
|
|
136
97
|
/**
|
|
137
98
|
* Adds routes handling common requests not related to particular model.
|
|
138
99
|
*
|
|
@@ -143,7 +104,7 @@ module.exports = function() {
|
|
|
143
104
|
* @returns {void}
|
|
144
105
|
*/
|
|
145
106
|
function addGlobalRoutes( routes, urlPrefix, models ) {
|
|
146
|
-
const {
|
|
107
|
+
const { services: { Model: BaseModel, OdemSchema }, utility: { case: { pascalToKebab } } } = api;
|
|
147
108
|
|
|
148
109
|
routes.set( `GET ${resolve( urlPrefix, ".schema" )}`, reqFetchSchemata );
|
|
149
110
|
|
|
@@ -154,7 +115,12 @@ module.exports = function() {
|
|
|
154
115
|
* @param {Hitchy.Core.ServerResponse} res response manager
|
|
155
116
|
* @returns {void}
|
|
156
117
|
*/
|
|
157
|
-
function reqFetchSchemata( req, res ) {
|
|
118
|
+
async function reqFetchSchemata( req, res ) {
|
|
119
|
+
if ( api.plugins.authentication && !await Services.Authorization.mayAccess( req.user, "@hitchy.odem.schema" ) ) {
|
|
120
|
+
resAccessForbidden( res );
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
158
124
|
const modelKeys = Object.keys( models );
|
|
159
125
|
const numModels = modelKeys.length;
|
|
160
126
|
|
|
@@ -183,11 +149,12 @@ module.exports = function() {
|
|
|
183
149
|
* route patterns into function handling requests matching that pattern
|
|
184
150
|
* @param {string} urlPrefix common prefix to use on every route regarding any model-related processing
|
|
185
151
|
* @param {string} routeName name of model to be used in path name of request
|
|
152
|
+
* @param {string} modelName name of model derived from name extracted from path name of request in `routeName`
|
|
186
153
|
* @param {class<Model>} Model model class
|
|
187
154
|
* @param {boolean} includeConvenienceRoutes set true to include additional set of routes for controlling all action via GET-requests
|
|
188
155
|
* @returns {void}
|
|
189
156
|
*/
|
|
190
|
-
function addRoutesOnModel( routes, urlPrefix, routeName, Model, includeConvenienceRoutes ) {
|
|
157
|
+
function addRoutesOnModel( routes, urlPrefix, routeName, modelName, Model, includeConvenienceRoutes ) {
|
|
191
158
|
const { Model: BaseModel, OdemSchema: Schema, OdemUtilityUuid: { ptnUuid } } = Services;
|
|
192
159
|
|
|
193
160
|
const modelUrl = resolve( urlPrefix, routeName );
|
|
@@ -235,7 +202,7 @@ module.exports = function() {
|
|
|
235
202
|
* @returns {void}
|
|
236
203
|
*/
|
|
237
204
|
function reqSuccess( req, res ) {
|
|
238
|
-
if (
|
|
205
|
+
if ( Schema.mayBeExposed( req, Model ) ) {
|
|
239
206
|
res.status( 200 ).send();
|
|
240
207
|
} else {
|
|
241
208
|
res.status( 403 ).send();
|
|
@@ -262,14 +229,19 @@ module.exports = function() {
|
|
|
262
229
|
* @param {Hitchy.Core.ServerResponse} res API for creating response
|
|
263
230
|
* @returns {void}
|
|
264
231
|
*/
|
|
265
|
-
function reqFetchSchema( req, res ) {
|
|
232
|
+
async function reqFetchSchema( req, res ) {
|
|
266
233
|
logDebug( "got request fetching schema" );
|
|
267
234
|
|
|
268
|
-
if ( !
|
|
235
|
+
if ( !Schema.mayBeExposed( req, Model ) ) {
|
|
269
236
|
res.status( 403 ).json( { error: "access forbidden by model" } );
|
|
270
237
|
return;
|
|
271
238
|
}
|
|
272
239
|
|
|
240
|
+
if ( api.plugins.authentication && !await Services.Authorization.mayAccess( req.user, `@hitchy.odem.model.${modelName}.schema` ) ) {
|
|
241
|
+
resAccessForbidden( res );
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
273
245
|
res.json( Schema.extractPublicData( Model ) );
|
|
274
246
|
}
|
|
275
247
|
|
|
@@ -281,23 +253,28 @@ module.exports = function() {
|
|
|
281
253
|
* @param {Hitchy.Core.ServerResponse} res API for creating response
|
|
282
254
|
* @returns {Promise|undefined} promises request processed successfully
|
|
283
255
|
*/
|
|
284
|
-
function reqCheckItem( req, res ) {
|
|
256
|
+
async function reqCheckItem( req, res ) {
|
|
285
257
|
logDebug( "got request checking if some item exists" );
|
|
286
258
|
|
|
287
259
|
if ( !Schema.mayBeExposed( req, Model ) ) {
|
|
288
260
|
res.status( 403 ).json( { error: "access forbidden by model" } );
|
|
289
|
-
return
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if ( api.plugins.authentication && !await Services.Authorization.mayAccess( req.user, `@hitchy.odem.model.${modelName}.check` ) ) {
|
|
265
|
+
resAccessForbidden( res );
|
|
266
|
+
return;
|
|
290
267
|
}
|
|
291
268
|
|
|
292
269
|
const { uuid } = req.params;
|
|
293
270
|
if ( !ptnUuid.test( uuid ) ) {
|
|
294
271
|
res.status( 400 ).send();
|
|
295
|
-
return
|
|
272
|
+
return;
|
|
296
273
|
}
|
|
297
274
|
|
|
298
275
|
const item = new Model( uuid ); // eslint-disable-line new-cap
|
|
299
276
|
|
|
300
|
-
|
|
277
|
+
await item.$exists
|
|
301
278
|
.then( exists => {
|
|
302
279
|
res.status( exists ? 200 : 404 ).send();
|
|
303
280
|
} )
|
|
@@ -312,25 +289,30 @@ module.exports = function() {
|
|
|
312
289
|
*
|
|
313
290
|
* @param {Hitchy.Core.IncomingMessage} req description of request
|
|
314
291
|
* @param {Hitchy.Core.ServerResponse} res API for creating response
|
|
315
|
-
* @returns {Promise} promises request processed successfully
|
|
292
|
+
* @returns {Promise<void>} promises request processed successfully
|
|
316
293
|
*/
|
|
317
|
-
function reqFetchItem( req, res ) {
|
|
294
|
+
async function reqFetchItem( req, res ) {
|
|
318
295
|
logDebug( "got request fetching some item" );
|
|
319
296
|
|
|
320
297
|
if ( !Schema.mayBeExposed( req, Model ) ) {
|
|
321
298
|
res.status( 403 ).json( { error: "access forbidden by model" } );
|
|
322
|
-
return
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if ( api.plugins.authentication && !await Services.Authorization.mayAccess( req.user, `@hitchy.odem.model.${modelName}.read` ) ) {
|
|
303
|
+
resAccessForbidden( res );
|
|
304
|
+
return;
|
|
323
305
|
}
|
|
324
306
|
|
|
325
307
|
const { uuid } = req.params;
|
|
326
308
|
if ( !ptnUuid.test( uuid ) ) {
|
|
327
309
|
res.status( 400 ).json( { error: "invalid UUID" } );
|
|
328
|
-
return
|
|
310
|
+
return;
|
|
329
311
|
}
|
|
330
312
|
|
|
331
313
|
const item = new Model( uuid ); // eslint-disable-line new-cap
|
|
332
314
|
|
|
333
|
-
|
|
315
|
+
await item.load()
|
|
334
316
|
.then( loaded => res.json( Schema.filterItem( loaded.toObject( { serialized: true } ), req, Model, "read" ) ) )
|
|
335
317
|
.catch( error => {
|
|
336
318
|
logError( "fetching %s:", routeName, error );
|
|
@@ -340,6 +322,7 @@ module.exports = function() {
|
|
|
340
322
|
res.status( 404 ).json( { error: "selected item not found" } );
|
|
341
323
|
break;
|
|
342
324
|
}
|
|
325
|
+
|
|
343
326
|
default : {
|
|
344
327
|
res.status( 500 ).json( { error: error.message } );
|
|
345
328
|
}
|
|
@@ -355,17 +338,22 @@ module.exports = function() {
|
|
|
355
338
|
* @param {Hitchy.Core.ServerResponse} res response controller
|
|
356
339
|
* @returns {Promise} promises response sent
|
|
357
340
|
*/
|
|
358
|
-
function reqFetchItems( req, res ) {
|
|
341
|
+
async function reqFetchItems( req, res ) {
|
|
359
342
|
logDebug( "got request fetching items" );
|
|
360
343
|
|
|
361
344
|
if ( !Schema.mayBeExposed( req, Model ) ) {
|
|
362
345
|
res.status( 403 ).json( { error: "access forbidden by model" } );
|
|
363
|
-
return
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if ( api.plugins.authentication && !await Services.Authorization.mayAccess( req.user, `@hitchy.odem.model.${modelName}.list` ) ) {
|
|
350
|
+
resAccessForbidden( res );
|
|
351
|
+
return;
|
|
364
352
|
}
|
|
365
353
|
|
|
366
354
|
if ( req.headers["x-list-as-array"] ) {
|
|
367
355
|
res.status( 400 ).json( { error: "fetching items as array is deprecated for security reasons" } );
|
|
368
|
-
return
|
|
356
|
+
return;
|
|
369
357
|
}
|
|
370
358
|
|
|
371
359
|
let handler = reqListAll;
|
|
@@ -374,7 +362,7 @@ module.exports = function() {
|
|
|
374
362
|
handler = reqListMatches;
|
|
375
363
|
}
|
|
376
364
|
|
|
377
|
-
|
|
365
|
+
await handler.call( this, req, res );
|
|
378
366
|
}
|
|
379
367
|
|
|
380
368
|
/**
|
|
@@ -418,11 +406,6 @@ module.exports = function() {
|
|
|
418
406
|
async function reqListMatches( req, res ) {
|
|
419
407
|
logDebug( "got request listing matching items" );
|
|
420
408
|
|
|
421
|
-
if ( !Schema.mayBeExposed( req, Model ) ) {
|
|
422
|
-
res.status( 403 ).json( { error: "access forbidden by model" } );
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
409
|
const {
|
|
427
410
|
q: simpleQuery = "",
|
|
428
411
|
offset = 0,
|
|
@@ -505,17 +488,13 @@ module.exports = function() {
|
|
|
505
488
|
* @param {Hitchy.Core.ServerResponse} res API for creating response
|
|
506
489
|
* @returns {Promise|undefined} promises request processed successfully
|
|
507
490
|
*/
|
|
508
|
-
function reqListAll( req, res ) {
|
|
491
|
+
async function reqListAll( req, res ) {
|
|
509
492
|
logDebug( "got request listing all items" );
|
|
510
493
|
|
|
511
|
-
if ( !Schema.mayBeExposed( req, Model ) ) {
|
|
512
|
-
res.status( 403 ).json( { error: "access forbidden by model" } );
|
|
513
|
-
return undefined;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
494
|
const { offset = 0, limit = Infinity, sortBy = null, descending = false, loadRecords = true, count = false } = req.query;
|
|
517
495
|
const meta = count || req.headers["x-count"] ? {} : null;
|
|
518
|
-
|
|
496
|
+
|
|
497
|
+
await Model.list( {
|
|
519
498
|
offset,
|
|
520
499
|
limit,
|
|
521
500
|
sortBy,
|
|
@@ -547,17 +526,22 @@ module.exports = function() {
|
|
|
547
526
|
* @param {Hitchy.Core.ServerResponse} res API for creating response
|
|
548
527
|
* @returns {Promise|undefined} promises request processed successfully
|
|
549
528
|
*/
|
|
550
|
-
function reqCreateItem( req, res ) {
|
|
529
|
+
async function reqCreateItem( req, res ) {
|
|
551
530
|
logDebug( "got request creating item" );
|
|
552
531
|
|
|
553
532
|
if ( !Schema.mayBeExposed( req, Model ) ) {
|
|
554
533
|
res.status( 403 ).json( { error: "access forbidden by model" } );
|
|
555
|
-
return
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if ( api.plugins.authentication && !await Services.Authorization.mayAccess( req.user, `@hitchy.odem.model.${modelName}.create` ) ) {
|
|
538
|
+
resAccessForbidden( res );
|
|
539
|
+
return;
|
|
556
540
|
}
|
|
557
541
|
|
|
558
542
|
const item = new Model(); // eslint-disable-line new-cap
|
|
559
543
|
|
|
560
|
-
|
|
544
|
+
await ( req.method === "GET" ? Promise.resolve( req.query ) : req.fetchBody() )
|
|
561
545
|
.then( record => {
|
|
562
546
|
if ( record ) {
|
|
563
547
|
if ( record.uuid ) {
|
|
@@ -595,23 +579,28 @@ module.exports = function() {
|
|
|
595
579
|
* @param {Hitchy.Core.ServerResponse} res API for creating response
|
|
596
580
|
* @returns {Promise|undefined} promises request processed successfully
|
|
597
581
|
*/
|
|
598
|
-
function reqModifyItem( req, res ) {
|
|
582
|
+
async function reqModifyItem( req, res ) {
|
|
599
583
|
logDebug( "got request to modify some item" );
|
|
600
584
|
|
|
601
585
|
if ( !Schema.mayBeExposed( req, Model ) ) {
|
|
602
586
|
res.status( 403 ).json( { error: "access forbidden by model" } );
|
|
603
|
-
return
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if ( api.plugins.authentication && !await Services.Authorization.mayAccess( req.user, `@hitchy.odem.model.${modelName}.write` ) ) {
|
|
591
|
+
resAccessForbidden( res );
|
|
592
|
+
return;
|
|
604
593
|
}
|
|
605
594
|
|
|
606
595
|
const { uuid } = req.params;
|
|
607
596
|
if ( !ptnUuid.test( uuid ) ) {
|
|
608
597
|
res.status( 400 ).json( { error: "invalid UUID" } );
|
|
609
|
-
return
|
|
598
|
+
return;
|
|
610
599
|
}
|
|
611
600
|
|
|
612
601
|
const item = new Model( uuid ); // eslint-disable-line new-cap
|
|
613
602
|
|
|
614
|
-
|
|
603
|
+
await item.$exists
|
|
615
604
|
.then( exists => {
|
|
616
605
|
if ( !exists ) {
|
|
617
606
|
res.status( 404 ).json( { error: "selected item not found" } );
|
|
@@ -655,23 +644,28 @@ module.exports = function() {
|
|
|
655
644
|
* @param {Hitchy.Core.ServerResponse} res API for creating response
|
|
656
645
|
* @returns {Promise|undefined} promises request processed successfully
|
|
657
646
|
*/
|
|
658
|
-
function reqReplaceItem( req, res ) {
|
|
647
|
+
async function reqReplaceItem( req, res ) {
|
|
659
648
|
logDebug( "got request replacing some item" );
|
|
660
649
|
|
|
661
650
|
if ( !Schema.mayBeExposed( req, Model ) ) {
|
|
662
651
|
res.status( 403 ).json( { error: "access forbidden by model" } );
|
|
663
|
-
return
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if ( api.plugins.authentication && !await Services.Authorization.mayAccess( req.user, `@hitchy.odem.model.${modelName}.write` ) ) {
|
|
656
|
+
resAccessForbidden( res );
|
|
657
|
+
return;
|
|
664
658
|
}
|
|
665
659
|
|
|
666
660
|
const { uuid } = req.params;
|
|
667
661
|
if ( !ptnUuid.test( uuid ) ) {
|
|
668
662
|
res.status( 400 ).json( { error: "invalid UUID" } );
|
|
669
|
-
return
|
|
663
|
+
return;
|
|
670
664
|
}
|
|
671
665
|
|
|
672
666
|
const item = new Model( uuid, { onUnsaved: false } ); // eslint-disable-line new-cap
|
|
673
667
|
|
|
674
|
-
|
|
668
|
+
await Promise.all( [ item.$exists, req.method === "GET" ? Promise.resolve( req.query ) : req.fetchBody() ] )
|
|
675
669
|
.then( ( [ exists, record ] ) => {
|
|
676
670
|
return ( exists ? item.load() : Promise.resolve() ).then( () => {
|
|
677
671
|
const propNames = Object.keys( Model.schema.props );
|
|
@@ -714,23 +708,28 @@ module.exports = function() {
|
|
|
714
708
|
* @param {Hitchy.Core.ServerResponse} res API for creating response
|
|
715
709
|
* @returns {Promise|undefined} promises request processed successfully
|
|
716
710
|
*/
|
|
717
|
-
function reqRemoveItem( req, res ) {
|
|
711
|
+
async function reqRemoveItem( req, res ) {
|
|
718
712
|
logDebug( "got request removing some item" );
|
|
719
713
|
|
|
720
714
|
if ( !Schema.mayBeExposed( req, Model ) ) {
|
|
721
715
|
res.status( 403 ).json( { error: "access forbidden by model" } );
|
|
722
|
-
return
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if ( api.plugins.authentication && !await Services.Authorization.mayAccess( req.user, `@hitchy.odem.model.${modelName}.remove` ) ) {
|
|
720
|
+
resAccessForbidden( res );
|
|
721
|
+
return;
|
|
723
722
|
}
|
|
724
723
|
|
|
725
724
|
const { uuid } = req.params;
|
|
726
725
|
if ( !ptnUuid.test( uuid ) ) {
|
|
727
726
|
res.status( 400 ).json( { error: "invalid UUID" } );
|
|
728
|
-
return
|
|
727
|
+
return;
|
|
729
728
|
}
|
|
730
729
|
|
|
731
730
|
const item = new Model( uuid ); // eslint-disable-line new-cap
|
|
732
731
|
|
|
733
|
-
|
|
732
|
+
await item.$exists
|
|
734
733
|
.then( exists => {
|
|
735
734
|
if ( exists ) {
|
|
736
735
|
return item.remove()
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hitchy/plugin-odem-rest",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "HTTP REST API for Hitchy's
|
|
3
|
+
"version": "0.7.1",
|
|
4
|
+
"description": "HTTP REST API for Hitchy's document-oriented database",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"lint": "eslint .",
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"repository": "https://gitlab.com/hitchy/plugin-odem-rest.git",
|
|
11
11
|
"keywords": [
|
|
12
|
-
"hitchy"
|
|
13
|
-
"ODM"
|
|
12
|
+
"hitchy"
|
|
14
13
|
],
|
|
15
14
|
"author": "Thomas Urban",
|
|
16
15
|
"license": "MIT",
|
|
@@ -18,7 +17,7 @@
|
|
|
18
17
|
"homepage": "https://gitlab.com/hitchy/plugin-odem-rest#plugin-odem-rest",
|
|
19
18
|
"peerDependencies": {
|
|
20
19
|
"@hitchy/core": "0.8.x",
|
|
21
|
-
"@hitchy/plugin-odem": "0.
|
|
20
|
+
"@hitchy/plugin-odem": "0.9.x",
|
|
22
21
|
"@hitchy/plugin-auth": "0.4.x"
|
|
23
22
|
},
|
|
24
23
|
"devDependencies": {
|
|
@@ -27,8 +26,8 @@
|
|
|
27
26
|
"c8": "^10.1.2",
|
|
28
27
|
"eslint": "^8.57.0",
|
|
29
28
|
"eslint-config-cepharum": "^1.0.14",
|
|
30
|
-
"eslint-plugin-promise": "^6.
|
|
31
|
-
"mocha": "^10.7.
|
|
29
|
+
"eslint-plugin-promise": "^6.6.0",
|
|
30
|
+
"mocha": "^10.7.3",
|
|
32
31
|
"should": "^13.2.3",
|
|
33
32
|
"should-http": "^0.1.1"
|
|
34
33
|
}
|
package/readme.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# plugin-odem-rest [](https://gitlab.com/hitchy/plugin-odem-rest/-/commits/master)
|
|
2
2
|
|
|
3
|
-
HTTP REST API for [Hitchy's](https://core.hitchy.org/) [
|
|
3
|
+
HTTP REST API for [Hitchy's](https://core.hitchy.org/) [document-oriented database](https://odem.hitchy.org/)
|
|
4
4
|
|
|
5
|
-
[Hitchy](http://core.hitchy.org/) is a server-side framework for developing web applications with [Node.js](https://nodejs.org/). [Odem](https://www.npmjs.com/package/@hitchy/plugin-odem) is plugin for Hitchy implementing
|
|
5
|
+
[Hitchy](http://core.hitchy.org/) is a server-side framework for developing web applications with [Node.js](https://nodejs.org/). [Odem](https://www.npmjs.com/package/@hitchy/plugin-odem) is a plugin for Hitchy implementing a document-oriented database using data backends like regular file systems, temporary in-memory databases and third-party key-value stores.
|
|
6
6
|
|
|
7
|
-
This plugin is defining blueprint routes for accessing data managed in
|
|
7
|
+
This plugin is defining blueprint routes for accessing data managed in document-oriented database using REST API.
|
|
8
8
|
|
|
9
9
|
## License
|
|
10
10
|
|
|
@@ -44,7 +44,7 @@ exports.auth = {
|
|
|
44
44
|
|
|
45
45
|
> **Do not use this in a production setup!**
|
|
46
46
|
>
|
|
47
|
-
> This example is granting permissions to create, adjust and remove items of your
|
|
47
|
+
> This example is granting permissions to create, adjust and remove items of your document-oriented database without any authentication. In addition, all _protected_ models and properties get exposed to everyone.
|
|
48
48
|
>
|
|
49
49
|
> See the [section on authorization](#authorization) for additional information!
|
|
50
50
|
|
|
@@ -105,7 +105,7 @@ exports.model = {
|
|
|
105
105
|
|
|
106
106
|
The model's segment in URL `<model>` is derived as the kebab-case version of model's name which is given in PascalCase. Thus, a model definition in a file named **api/models/my-fancy-model.js** is assumed to describe a model named **MyFancyModel** by default, resulting in model's URL segment to be **my-fancy-model** again. So the URL path for the collection of items is `/api/my-fancy-model`.
|
|
107
107
|
|
|
108
|
-
In Hitchy's
|
|
108
|
+
In Hitchy's document-oriented database all model instances or items are uniquely addressable via UUIDs. By appending an item's UUID to the given URL path of a collection you get the URL path of that item, e.g. `/api/my-fancy-model/01234567-1234-1234-1234-56789abcdef0`.
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
### The REST API
|
|
@@ -220,9 +220,9 @@ For example, a GET-request for `/api/localEmployee?q=salary:between:2000:4000` w
|
|
|
220
220
|
|
|
221
221
|
##### Complex tests <Bade type=info text=v0.5.2+></Badge>
|
|
222
222
|
|
|
223
|
-
Hitchy
|
|
223
|
+
Hitchy's document-oriented database supports more complex queries that can't be encoded as such simple queries as described above. Thus, a different way of querying has been added.
|
|
224
224
|
|
|
225
|
-
When using parameter `query` instead of `q`, its value is assumed to be a JSON-encoded query complying with query syntax supported by [Model.find() method of Hitchy
|
|
225
|
+
When using parameter `query` instead of `q`, its value is assumed to be a JSON-encoded query complying with query syntax supported by [Model.find() method of Hitchy's document-oriented database](https://odem.hitchy.org/api/model.html#model-find).
|
|
226
226
|
|
|
227
227
|
```http request
|
|
228
228
|
GET /api/user?query={"in":{"name":["john","jane","jason"]}}
|
|
@@ -272,7 +272,7 @@ Resources are declared to control authorizations for basically interacting with
|
|
|
272
272
|
* When creating a new item, accessing the resource `@hitchy.odem.model.<ModelName>.create` must be granted.
|
|
273
273
|
* When removing an item, accessing the resource `@hitchy.odem.model.<ModelName>.remove` must be granted.
|
|
274
274
|
* Accessing a model's schema requires the resource `@hitchy.odem.model.<ModelName>.schema` to be granted.
|
|
275
|
-
* Accessing the collection of all models' schemata requires the resource `@hitchy.odem.schema` to be granted. This implicitly causes models with their [promote option](https://odem.hitchy.org/guides/defining-models.html#options) being `protected` to be exposed in responses to that request unless
|
|
275
|
+
* Accessing the collection of all models' schemata requires the resource `@hitchy.odem.schema` to be granted. This implicitly causes models with their [promote option](https://odem.hitchy.org/guides/defining-models.html#options) being `protected` to be exposed in responses to that request unless resource `@hitchy.odem.model.<ModelName>.promote` isn't revoked from the user.
|
|
276
276
|
|
|
277
277
|
> In these examples, replace `<ModelName>` with a model's name in PascalCase.
|
|
278
278
|
|