Skip to content

Commit a4e5749

Browse files
authored
Merge pull request #150 from bcgov/bugfix/updateBucketDiff
Allow updateBucket to handle differential patch value checking
2 parents ab28243 + fcd8233 commit a4e5749

File tree

5 files changed

+88
-24
lines changed

5 files changed

+88
-24
lines changed

app/src/components/errorToProblem.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ const Problem = require('api-problem');
22

33
const log = require('./log')(module.filename);
44

5+
/**
6+
* @function errorToProblem
7+
* Attempts to interpret and infer the type of Problem to respond with
8+
* @param {string} service A string representing which service the error occured at
9+
* @param {Error} e The raw error exception object
10+
* @returns {Problem} A problem error type
11+
*/
512
function errorToProblem(service, e) {
613
// If already problem type, just return as is
714
if (e instanceof Problem) {
@@ -24,7 +31,10 @@ function errorToProblem(service, e) {
2431
});
2532
}
2633
// Something else happened but there's a response
27-
return new Problem(e.response.status, { detail: e.response.data.toString() });
34+
return new Problem(e.response.status, { detail: e.response.data });
35+
} else if (e.statusCode) {
36+
// Handle errors with Status Codes
37+
return new Problem(e.statusCode, { detail: e.message });
2838
} else if (e.$metadata && e.$metadata.httpStatusCode) {
2939
// Handle S3 promise rejections
3040
if (e.$response && e.$response.body) delete e.$response.body;

app/src/controllers/bucket.js

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,18 @@ const controller = {
6868
/**
6969
* @function _validateCredentials
7070
* Guard against creating or update a bucket with invalid creds
71-
* @param {object} requestBody The body of the request
71+
* @param {object} credentials The body of the request
72+
* @throws A conflict error problem if the bucket is not reachable
7273
*/
73-
async _validateCredentials(requestBody, res) {
74+
async _validateCredentials(credentials) {
7475
try {
7576
const bucketSettings = {
76-
accessKeyId: requestBody.accessKeyId,
77-
bucket: requestBody.bucket,
78-
endpoint: requestBody.endpoint,
79-
key: requestBody.key,
80-
region: requestBody.region || DEFAULTREGION,
81-
secretAccessKey: requestBody.secretAccessKey,
77+
accessKeyId: credentials.accessKeyId,
78+
bucket: credentials.bucket,
79+
endpoint: credentials.endpoint,
80+
key: credentials.key,
81+
region: credentials.region || DEFAULTREGION,
82+
secretAccessKey: credentials.secretAccessKey,
8283
};
8384
await storageService.headBucket(bucketSettings);
8485
} catch (e) {
@@ -87,9 +88,8 @@ const controller = {
8788
function: '_validateCredentials',
8889
});
8990
throw new Problem(409, {
90-
details:
91-
'Unable to validate supplied key/password for the supplied object store or bucket',
92-
}).send(res);
91+
details: 'Unable to validate supplied credentials for the bucket',
92+
});
9393
}
9494
},
9595

@@ -105,10 +105,9 @@ const controller = {
105105
const data = { ...req.body };
106106
let response = undefined;
107107

108-
// Check for credential accessibility/validity first
109-
await controller._validateCredentials(data, res);
110-
111108
try {
109+
// Check for credential accessibility/validity first
110+
await controller._validateCredentials(data);
112111
data.userId = await userService.getCurrentUserId(getCurrentIdentity(req.currentUser, SYSTEM_USER));
113112

114113
response = await bucketService.create(data);
@@ -216,13 +215,17 @@ const controller = {
216215
* @returns {function} Express middleware function
217216
*/
218217
async updateBucket(req, res, next) {
219-
// Check for credential accessibility/validity first
220-
await controller._validateCredentials(req.body, res);
221-
222218
try {
219+
const bucketId = addDashesToUuid(req.params.bucketId);
220+
const currentBucket = await bucketService.read(bucketId);
221+
222+
// Check for credential accessibility/validity first
223+
// Need to cross reference with existing data when partial patch data is provided
224+
await controller._validateCredentials({ ...currentBucket, ...req.body });
225+
223226
const userId = await userService.getCurrentUserId(getCurrentIdentity(req.currentUser, SYSTEM_USER), SYSTEM_USER);
224227
const response = await bucketService.update({
225-
bucketId: req.params.bucketId,
228+
bucketId: bucketId,
226229
bucketName: req.body.bucketName,
227230
accessKeyId: req.body.accessKeyId,
228231
bucket: req.body.bucket,

app/tests/unit/components/errorToProblem.spec.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,55 @@ describe('errorToProblem', () => {
3535
expect(result.errors).toBeUndefined();
3636
});
3737

38+
it('should return a 409 problem given an error', () => {
39+
const e = {
40+
response: {
41+
data: { detail: 'detail' },
42+
status: 409
43+
}
44+
};
45+
const result = errorToProblem(SERVICE, e);
46+
47+
expect(result).toBeTruthy();
48+
expect(result instanceof Problem).toBeTruthy();
49+
expect(result.title).toMatch('Conflict');
50+
expect(result.status).toBe(409);
51+
expect(result.detail).toEqual(expect.objectContaining(e.response.data));
52+
expect(result.errors).toBeUndefined();
53+
});
54+
55+
it('should return a problem given an error with statusCode', () => {
56+
const e = {
57+
statusCode: 404,
58+
message: 'NotFoundError'
59+
};
60+
const result = errorToProblem(SERVICE, e);
61+
62+
expect(result).toBeTruthy();
63+
expect(result instanceof Problem).toBeTruthy();
64+
expect(result.title).toMatch('Not Found');
65+
expect(result.status).toBe(404);
66+
expect(result.detail).toMatch(e.message);
67+
expect(result.errors).toBeUndefined();
68+
});
69+
70+
it('should return a problem given an error with s3 metadata', () => {
71+
const e = {
72+
$metadata: {
73+
httpStatusCode: 404,
74+
},
75+
message: 'NotFoundError'
76+
};
77+
const result = errorToProblem(SERVICE, e);
78+
79+
expect(result).toBeTruthy();
80+
expect(result instanceof Problem).toBeTruthy();
81+
expect(result.title).toMatch('Not Found');
82+
expect(result.status).toBe(404);
83+
expect(result.detail).toEqual(expect.objectContaining({ message: e.message }));
84+
expect(result.errors).toBeUndefined();
85+
});
86+
3887
it('should return a 422 problem with a supplied string response', () => {
3988
const e = {
4089
response: {

app/tests/unit/controllers/bucket.spec.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ describe('createBucket', () => {
107107
);
108108
});
109109

110-
it('excepts if invalid bucket head', async () => {
110+
it('responds with error when invalid bucket head', async () => {
111111
// request object
112112
const req = {
113113
body: { bucket: REQUEST_BUCKET, region: 'test' },
@@ -126,13 +126,15 @@ describe('createBucket', () => {
126126
throw new Error();
127127
});
128128

129-
await expect(controller.createBucket(req, res, next)).rejects.toThrow();
129+
await controller.createBucket(req, res, next);
130130

131131
expect(headBucketSpy).toHaveBeenCalledTimes(1);
132132
expect(headBucketSpy).toHaveBeenCalledWith(req.body);
133133
expect(getCurrentIdentitySpy).toHaveBeenCalledTimes(0);
134134
expect(getCurrentUserIdSpy).toHaveBeenCalledTimes(0);
135135
expect(createSpy).toHaveBeenCalledTimes(0);
136+
expect(next).toHaveBeenCalledTimes(1);
137+
expect(next).toHaveBeenCalledWith(expect.any(Problem));
136138
});
137139

138140
it('nexts an error if the bucket service fails to create', async () => {
@@ -167,7 +169,7 @@ describe('createBucket', () => {
167169
expect(getCurrentUserIdSpy).toHaveBeenCalledWith(USR_IDENTITY);
168170
expect(createSpy).toHaveBeenCalledTimes(1);
169171
expect(createSpy).toHaveBeenCalledWith({ ...req.body, userId: USR_ID });
170-
172+
171173
expect(next).toHaveBeenCalledTimes(1);
172174
expect(next).toHaveBeenCalledWith(
173175
new Problem(502, 'Unknown BucketService Error')
@@ -213,7 +215,7 @@ describe('createBucket', () => {
213215
expect(createSpy).toHaveBeenCalledWith({ ...req.body, userId: USR_ID });
214216
expect(checkGrantPermissionsSpy).toHaveBeenCalledTimes(1);
215217
expect(checkGrantPermissionsSpy).toHaveBeenCalledWith({ ...req.body, userId: USR_ID });
216-
218+
217219
expect(res.status).toHaveBeenCalledWith(201);
218220
});
219221
});

charts/coms/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: common-object-management-service
33
# This is the chart version. This version number should be incremented each time you make changes
44
# to the chart and its templates, including the app version.
55
# Versions are expected to follow Semantic Versioning (https://semver.org/)
6-
version: 0.0.11
6+
version: 0.0.12
77
kubeVersion: ">= 1.13.0"
88
description: A microservice for managing access control to S3 Objects
99
# A chart can be either an 'application' or a 'library' chart.

0 commit comments

Comments
 (0)