Skip to content

Commit 6edf9cd

Browse files
authored
Merge pull request #111 from bcgov/version-metadata
Get metadata for versions
2 parents 873a183 + 4c3a29c commit 6edf9cd

File tree

13 files changed

+197
-17
lines changed

13 files changed

+197
-17
lines changed

app/src/controllers/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ module.exports = {
44
metadataController: require('./metadata'),
55
objectController: require('./object'),
66
objectPermissionController: require('./objectPermission'),
7-
userController: require('./user')
7+
userController: require('./user'),
8+
versionController: require('./version')
89
};

app/src/controllers/object.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ const controller = {
459459
metadata: metadata && Object.keys(metadata).length ? metadata : undefined
460460
};
461461

462-
const response = await metadataService.fetchMetadata(params);
462+
const response = await metadataService.fetchMetadataForObject(params);
463463
res.status(200).json(response);
464464
} catch (error) {
465465
next(error);

app/src/controllers/version.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const errorToProblem = require('../components/errorToProblem');
2+
const { addDashesToUuid, getMetadata, mixedQueryToArray } = require('../components/utils');
3+
const { metadataService } = require('../services');
4+
5+
const SERVICE = 'UserService';
6+
7+
/**
8+
* The Version Controller
9+
*/
10+
const controller = {
11+
/**
12+
* @function fetchMetadata
13+
* Lists metadata for an array of versions
14+
* @param {object} req Express request object
15+
* @param {object} res Express response object
16+
* @param {function} next The next callback function
17+
* @returns {function} Express middleware function
18+
*/
19+
async fetchMetadata(req, res, next) {
20+
try {
21+
const versionIds = mixedQueryToArray(req.query.versionId);
22+
const metadata = getMetadata(req.headers);
23+
24+
const params = {
25+
versionIds: versionIds ? versionIds.map(id => addDashesToUuid(id)) : versionIds,
26+
metadata: metadata && Object.keys(metadata).length ? metadata : undefined,
27+
};
28+
29+
const response = await metadataService.fetchMetadataForVersion(params);
30+
res.status(200).json(response);
31+
} catch (e) {
32+
next(errorToProblem(SERVICE, e));
33+
}
34+
},
35+
36+
};
37+
38+
module.exports = controller;

app/src/db/models/tables/version.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ class Version extends Timestamps(Model) {
5454

5555
static get modifiers() {
5656
return {
57+
filterId(query, value) {
58+
filterOneOrMany(query, value, 'id');
59+
},
5760
filterObjectId(query, value) {
5861
filterOneOrMany(query, value, 'objectId');
5962
},

app/src/docs/v1.api-spec.yaml

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ paths:
331331
- Object
332332
parameters:
333333
- $ref: '#/components/parameters/Path-ObjectId'
334-
- $ref: '#/components/parameters/Query-VersionId'
334+
- $ref: '#/components/parameters/Query-S3VersionId'
335335
responses:
336336
'204':
337337
description: Returns object headers
@@ -374,7 +374,7 @@ paths:
374374
- $ref: '#/components/parameters/Path-ObjectId'
375375
- $ref: '#/components/parameters/Query-Download'
376376
- $ref: '#/components/parameters/Query-ExpiresIn'
377-
- $ref: '#/components/parameters/Query-VersionId'
377+
- $ref: '#/components/parameters/Query-S3VersionId'
378378
responses:
379379
'200':
380380
description: Returns the object
@@ -565,7 +565,7 @@ paths:
565565
parameters:
566566
- $ref: '#/components/parameters/Header-Metadata'
567567
- $ref: '#/components/parameters/Path-ObjectId'
568-
- $ref: '#/components/parameters/Query-VersionId'
568+
- $ref: '#/components/parameters/Query-S3VersionId'
569569
responses:
570570
'204':
571571
$ref: '#/components/responses/NoContent'
@@ -589,7 +589,7 @@ paths:
589589
parameters:
590590
- $ref: '#/components/parameters/Header-Metadata'
591591
- $ref: '#/components/parameters/Path-ObjectId'
592-
- $ref: '#/components/parameters/Query-VersionId'
592+
- $ref: '#/components/parameters/Query-S3VersionId'
593593
responses:
594594
'204':
595595
$ref: '#/components/responses/NoContent'
@@ -615,7 +615,7 @@ paths:
615615
parameters:
616616
- $ref: '#/components/parameters/Header-Metadata'
617617
- $ref: '#/components/parameters/Path-ObjectId'
618-
- $ref: '#/components/parameters/Query-VersionId'
618+
- $ref: '#/components/parameters/Query-S3VersionId'
619619
responses:
620620
'204':
621621
$ref: '#/components/responses/NoContent'
@@ -661,7 +661,7 @@ paths:
661661
parameters:
662662
- $ref: '#/components/parameters/Path-ObjectId'
663663
- $ref: '#/components/parameters/Query-TagSet'
664-
- $ref: '#/components/parameters/Query-VersionId'
664+
- $ref: '#/components/parameters/Query-S3VersionId'
665665
responses:
666666
'204':
667667
$ref: '#/components/responses/NoContent'
@@ -684,7 +684,7 @@ paths:
684684
parameters:
685685
- $ref: '#/components/parameters/Path-ObjectId'
686686
- $ref: '#/components/parameters/Query-TagSet'
687-
- $ref: '#/components/parameters/Query-VersionId'
687+
- $ref: '#/components/parameters/Query-S3VersionId'
688688
responses:
689689
'204':
690690
$ref: '#/components/responses/NoContent'
@@ -708,7 +708,7 @@ paths:
708708
parameters:
709709
- $ref: '#/components/parameters/Path-ObjectId'
710710
- $ref: '#/components/parameters/Query-TagSet'
711-
- $ref: '#/components/parameters/Query-VersionId'
711+
- $ref: '#/components/parameters/Query-S3VersionId'
712712
responses:
713713
'204':
714714
$ref: '#/components/responses/NoContent'
@@ -1043,6 +1043,44 @@ paths:
10431043
$ref: '#/components/responses/Forbidden'
10441044
default:
10451045
$ref: '#/components/responses/Error'
1046+
1047+
/version/metadata:
1048+
get:
1049+
summary: Returns a list of matching metadata for versions
1050+
description: >-
1051+
Gets a metadata matching the given versions. Multiple Key/Value pairs
1052+
can be provided in the headers to narrow down results. If none are provided the full set of metadata will be returned.
1053+
operationId: fetchMetadataForVersion
1054+
tags:
1055+
- Metadata
1056+
parameters:
1057+
- $ref: '#/components/parameters/Header-Metadata'
1058+
- $ref: '#/components/parameters/Query-VersionId'
1059+
responses:
1060+
'200':
1061+
description: Returns an array of versions with matching key/value pairs.
1062+
content:
1063+
application/json:
1064+
schema:
1065+
type: array
1066+
items:
1067+
type: object
1068+
required:
1069+
- versionId
1070+
- metadata
1071+
properties:
1072+
versionId:
1073+
type: string
1074+
format: uuid
1075+
example: ac246e31-c807-496c-bc93-cd8bc2f1b2b4
1076+
metadata:
1077+
type: array
1078+
items:
1079+
$ref: '#/components/schemas/S3-TagSet'
1080+
'422':
1081+
$ref: '#/components/responses/UnprocessableEntity'
1082+
default:
1083+
$ref: '#/components/responses/Error'
10461084
components:
10471085
headers:
10481086
Content-Disposition:
@@ -1371,14 +1409,23 @@ components:
13711409
schema:
13721410
type: string
13731411
example: bobsmith
1374-
Query-VersionId:
1412+
Query-S3VersionId:
13751413
in: query
13761414
name: versionId
13771415
description: Request a specified version
13781416
schema:
13791417
type: string
13801418
description: a version identifier created in S3
13811419
example: '1647462569641'
1420+
Query-VersionId:
1421+
in: query
1422+
name: versionId
1423+
description: Request a specified version
1424+
schema:
1425+
type: string
1426+
description: a version identifier in COMS database
1427+
format: uuid
1428+
example: 00000000-0000-0000-0000-000000000000
13821429
Query-VersionId-Delete:
13831430
in: query
13841431
name: versionId

app/src/routes/v1/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ router.get('/', (_req, res) => {
1212
'/metadata',
1313
'/object',
1414
'/permission',
15-
'/user'
15+
'/user',
16+
'/version'
1617
]
1718
});
1819
});
@@ -35,4 +36,7 @@ router.use('/permission', require('./permission'));
3536
/** User Router */
3637
router.use('/user', require('./user'));
3738

39+
/** Version Router */
40+
router.use('/version', require('./version'));
41+
3842
module.exports = router;

app/src/routes/v1/version.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const router = require('express').Router();
2+
3+
const { versionValidator } = require('../../validators');
4+
const { versionController } = require('../../controllers');
5+
const { checkAppMode } = require('../../middleware/authorization');
6+
const { requireDb, requireSomeAuth } = require('../../middleware/featureToggle');
7+
8+
router.use(checkAppMode);
9+
router.use(requireDb);
10+
router.use(requireSomeAuth);
11+
12+
/** Fetch metadata for specific version */
13+
router.get('/metadata', versionValidator.fetchMetadata, (req, res, next) => {
14+
versionController.fetchMetadata(req, res, next);
15+
});
16+
17+
module.exports = router;

app/src/services/metadata.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,13 @@ const service = {
148148
},
149149

150150
/**
151-
* @function fetchMetadata
151+
* @function fetchMetadataForObject
152152
* Fetch metadata for specific objects
153153
* @param {string[]} params.objId An array of uuids representing the object
154154
* @param {object} [params.metadata] Optional object of metadata key/value pairs
155155
* @returns {Promise<object[]>} The result of running the find operation
156156
*/
157-
fetchMetadata: (params) => {
157+
fetchMetadataForObject: (params) => {
158158
return Promise.all(params.objId.map(objId => {
159159
const q = Version.query()
160160
.allowGraph('metadata')
@@ -172,6 +172,29 @@ const service = {
172172
}));
173173
},
174174

175+
/**
176+
* @function fetchMetadataForVersion
177+
* Fetch metadata for specific versions
178+
* @param {string[]} [params.versionIds] An array of uuids representing versions
179+
* @param {object} [params.metadata] Optional object of metadata key/value pairs
180+
* @returns {Promise<object[]>} The result of running the database select
181+
*/
182+
fetchMetadataForVersion: (params) => {
183+
return Version.query()
184+
.select('id as versionId')
185+
.allowGraph('metadata')
186+
.withGraphFetched('metadata')
187+
.modifyGraph('metadata', builder => {
188+
builder
189+
.select('key', 'value')
190+
.modify('filterKeyValue', { metadata: params.metadata });
191+
})
192+
.modify('filterId', params.versionIds)
193+
.orderBy('createdAt', 'desc');
194+
// .limit(1000); consider limiting result to avoid resource overuse
195+
},
196+
197+
175198
/**
176199
* @function searchMetadata
177200
* Search and filter for specific metadata keys

app/src/validators/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ module.exports = {
55
objectValidator: require('./object'),
66
objectPermissionValidator: require('./objectPermission'),
77
userValidator: require('./user'),
8+
versionValidator: require('./version'),
89
};

app/src/validators/version.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const { validate, Joi } = require('express-validation');
2+
const { scheme, type } = require('./common');
3+
4+
5+
const schema = {
6+
fetchMetadata: {
7+
headers: type.metadata(0),
8+
query: Joi.object({
9+
versionId: scheme.guid,
10+
})
11+
},
12+
};
13+
14+
const validator = {
15+
fetchMetadata: validate(schema.fetchMetadata, { statusCode: 422 }),
16+
};
17+
18+
module.exports = validator;
19+
module.exports.schema = schema;

0 commit comments

Comments
 (0)