Skip to content

Commit 9da975d

Browse files
committed
feat(upload): allow setting access ACL to public during upload
Add this to upload request to make files public during upload ```js { ..., "access": { "setPublic": true } } ```
1 parent 94d70bf commit 9da975d

5 files changed

Lines changed: 111 additions & 13 deletions

File tree

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,21 @@
3131
"md5": "^2.1.0",
3232
"ms-amqp-conf": "^0.2.0",
3333
"ms-amqp-transport": "^1.3.0",
34-
"ms-files-gce": "^0.6.3",
34+
"ms-files-gce": "^0.7.0",
3535
"ms-validation": "^1.0.1",
3636
"mservice": "^1.6.0",
3737
"redis-filtered-sort": "^1.1.1"
3838
},
3939
"devDependencies": {
4040
"babel-cli": "^6.4.5",
41-
"babel-eslint": "^5.0.0",
41+
"babel-eslint": "^6.0.0",
4242
"babel-preset-es2015": "^6.3.13",
4343
"babel-preset-stage-0": "^6.3.13",
4444
"babel-register": "^6.4.3",
4545
"chai": "^3.4.1",
4646
"codecov.io": "^0.1.6",
4747
"cz-conventional-changelog": "^1.1.5",
48-
"eslint": "~2.4.0",
48+
"eslint": "^2.5.1",
4949
"eslint-config-airbnb": "^6.0.1",
5050
"eslint-plugin-mocha": "^2.0.0",
5151
"faker": "^3.0.1",

schemas/upload.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@
2525
},
2626
"username": {
2727
"$ref": "common#/definitions/owner"
28+
},
29+
"access": {
30+
"type": "object",
31+
"required": ["setPublic"],
32+
"properties": {
33+
"setPublic": {
34+
"type": "boolean",
35+
"default": false
36+
}
37+
}
2838
}
2939
}
3040
}

src/actions/finish.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
const Promise = require('bluebird');
22
const fetchData = require('../utils/fetchData.js');
33
const { HttpStatusError } = require('common-errors');
4-
const { STATUS_UPLOADED, STATUS_PENDING, UPLOAD_DATA, FILES_INDEX, FILES_DATA, FILES_OWNER_FIELD } = require('../constant.js');
4+
const {
5+
STATUS_UPLOADED, STATUS_PENDING,
6+
UPLOAD_DATA,
7+
FILES_INDEX, FILES_DATA,
8+
FILES_OWNER_FIELD, FILES_PUBLIC_FIELD,
9+
} = require('../constant.js');
510

611
/**
712
* Finish upload
@@ -32,7 +37,7 @@ module.exports = function completeFileUpload(opts) {
3237
uploadId,
3338
update: redis
3439
.pipeline()
35-
.hmget(uploadKey, 'status', 'parts', FILES_OWNER_FIELD)
40+
.hmget(uploadKey, 'status', 'parts', FILES_OWNER_FIELD, FILES_PUBLIC_FIELD)
3641
.hincrby(uploadKey, 'uploaded', 1)
3742
.hmset(key, {
3843
status: STATUS_UPLOADED,
@@ -43,10 +48,10 @@ module.exports = function completeFileUpload(opts) {
4348
})
4449
.then(data => {
4550
const { uploadId, update } = data;
46-
const [parts, incr, status] = update;
51+
const [parts, incr, status, pub] = update;
4752

4853
// errors
49-
const err = parts[0] || incr[0] || status[0];
54+
const err = parts[0] || incr[0] || status[0] || pub[0];
5055
if (err) {
5156
throw err;
5257
}
@@ -63,8 +68,7 @@ module.exports = function completeFileUpload(opts) {
6368
}
6469

6570
const uploadKey = `${FILES_DATA}:${uploadId}`;
66-
67-
return redis
71+
const pipeline = redis
6872
.pipeline()
6973
.persist(uploadKey)
7074
.hmset(uploadKey, {
@@ -73,8 +77,16 @@ module.exports = function completeFileUpload(opts) {
7377
})
7478
// now that it is uploaded - add them to index
7579
.sadd(FILES_INDEX, uploadId)
76-
.sadd(`${FILES_INDEX}:${username}`, uploadId)
77-
.exec()
80+
.sadd(`${FILES_INDEX}:${username}`, uploadId);
81+
82+
// convert 1 or undef to Boolean
83+
const isPublic = !!pub[1];
84+
if (isPublic) {
85+
const FILES_INDEX_PUBLIC = `${FILES_INDEX}:${username}:pub`;
86+
pipeline.sadd(FILES_INDEX_PUBLIC, uploadId);
87+
}
88+
89+
return pipeline.exec()
7890
.return(uploadId);
7991
})
8092
.then(uploadId => {

src/actions/upload.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ const Promise = require('bluebird');
22
const uuid = require('node-uuid');
33
const md5 = require('md5');
44
const sumBy = require('lodash/sumBy');
5-
const { STATUS_PENDING, UPLOAD_DATA, FILES_DATA } = require('../constant.js');
5+
const get = require('lodash/get');
6+
const { STATUS_PENDING, UPLOAD_DATA, FILES_DATA, FILES_PUBLIC_FIELD } = require('../constant.js');
67

78
const TYPE_MAP = {
89
'c-bin': '.bin.gz',
@@ -27,6 +28,7 @@ module.exports = function initFileUpload(opts) {
2728
const { provider, redis } = this;
2829
const prefix = md5(username);
2930
const uploadId = uuid.v4();
31+
const isPublic = get(opts, 'access.setPublic', false);
3032

3133
return Promise
3234
.bind(this, ['files:upload:pre', files, username])
@@ -44,7 +46,13 @@ module.exports = function initFileUpload(opts) {
4446
...metadata,
4547
type,
4648
filename,
47-
location: provider.initResumableUpload({ filename, metadata }),
49+
location: provider.initResumableUpload({
50+
filename,
51+
metadata: {
52+
...metadata,
53+
predefinedAcl: isPublic ? 'publicRead' : '',
54+
},
55+
}),
4856
});
4957
})
5058
.then(parts => {
@@ -59,6 +67,10 @@ module.exports = function initFileUpload(opts) {
5967
parts: files.length,
6068
};
6169

70+
if (isPublic) {
71+
fileData[FILES_PUBLIC_FIELD] = 1;
72+
}
73+
6274
const pipeline = redis.pipeline();
6375
const uploadKey = `${FILES_DATA}:${uploadId}`;
6476

test/suites/upload.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const assert = require('assert');
22
const md5 = require('md5');
33
const url = require('url');
4+
const request = require('request-promise');
45

56
// helpers
67
const {
@@ -45,6 +46,7 @@ describe('upload suite', function suite() {
4546
assert.ok(rsp.uploadId);
4647
assert.ok(rsp.startedAt);
4748
assert.ok(rsp.files);
49+
assert.ifError(rsp.public);
4850
assert.equal(rsp.status, STATUS_PENDING);
4951
assert.equal(rsp.parts, message.files.length);
5052

@@ -82,4 +84,66 @@ describe('upload suite', function suite() {
8284
return null;
8385
});
8486
});
87+
88+
it('initiates public upload and returns correct response format', function test() {
89+
const message = modelData.message;
90+
91+
return this
92+
.send({
93+
...message,
94+
access: {
95+
setPublic: true,
96+
},
97+
}, 45000)
98+
.reflect()
99+
.then(inspectPromise())
100+
.then(rsp => {
101+
assert.equal(rsp.name, message.meta.name);
102+
assert.equal(rsp.owner, message.username);
103+
assert.ok(rsp.uploadId);
104+
assert.ok(rsp.startedAt);
105+
assert.ok(rsp.files);
106+
assert.ok(rsp.public);
107+
assert.equal(rsp.status, STATUS_PENDING);
108+
assert.equal(rsp.parts, message.files.length);
109+
110+
// verify that location is present
111+
rsp.files.forEach(part => {
112+
assert.ok(part.location);
113+
114+
// verify upoad link
115+
const location = url.parse(part.location, true);
116+
assert.equal(location.protocol, 'https:');
117+
assert.equal(location.hostname, 'www.googleapis.com');
118+
assert.equal(location.pathname, `/upload/storage/v1/b/${bucketName}/o`);
119+
assert.equal(location.query.name, part.filename);
120+
assert.equal(location.query.uploadType, 'resumable');
121+
assert.ok(location.query.upload_id);
122+
123+
// verify that filename contains multiple parts
124+
const [ownerHash, uploadId, filename] = part.filename.split('/');
125+
assert.equal(md5(owner), ownerHash);
126+
assert.equal(rsp.uploadId, uploadId);
127+
assert.ok(filename);
128+
});
129+
130+
// save for the next
131+
this.response = rsp;
132+
});
133+
});
134+
135+
it('upload is possible based on the returned data: public', function test() {
136+
return uploadFiles(modelData, this.response)
137+
.reflect()
138+
.then(inspectPromise())
139+
.map(resp => {
140+
assert.equal(resp.statusCode, 200);
141+
return null;
142+
});
143+
});
144+
145+
it('able to download public files right away', function test() {
146+
const file = this.response.files[0];
147+
return request.get(`https://www.googleapis.com/${bucketName}/${file.filename}`);
148+
});
85149
});

0 commit comments

Comments
 (0)