Skip to content

Commit b246a4d

Browse files
committed
attachment persmissions challenge
1 parent ecdea1f commit b246a4d

16 files changed

+522
-27
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
--
2+
-- project_attachments
3+
--
4+
ALTER TABLE project_attachments ADD COLUMN "userIds" integer[];

postman.json

Lines changed: 192 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"info": {
3-
"_postman_id": "e810fc27-5518-4cc5-8f90-6b1423c6b0b4",
3+
"_postman_id": "97085cd7-b298-4f1c-9629-24af14ff5f13",
44
"name": "tc-project-service",
55
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
66
},
@@ -278,24 +278,129 @@
278278
],
279279
"body": {
280280
"mode": "raw",
281-
"raw": "{\n\t\"param\": {\n\t\t\"title\": \"first attachment submission\",\n\t\t\"filePath\": \"asdjshdasdas/asdsadj/asdasd.png\",\n\t\t\"s3Bucket\": \"topcoder-project-service\",\n\t\t\"contentType\": \"application/png\"\n\t}\n}"
281+
"raw": "{\n\t\"param\": {\n\t\t\"title\": \"first attachment submission\",\n\t\t\"filePath\": \"asdjshdasdas/asdsadj/asdasd.png\",\n\t\t\"s3Bucket\": \"topcoder-project-service\",\n\t\t\"contentType\": \"application/png\",\n\t\t\"userIds\": [40051331]\n\t}\n}"
282282
},
283283
"url": {
284-
"raw": "{{api-url}}/v4/projects/7/attachments",
284+
"raw": "{{api-url}}/v4/projects/1/attachments",
285285
"host": [
286286
"{{api-url}}"
287287
],
288288
"path": [
289289
"v4",
290290
"projects",
291-
"7",
291+
"1",
292292
"attachments"
293293
]
294294
},
295295
"description": "Create an project attachment"
296296
},
297297
"response": []
298298
},
299+
{
300+
"name": "Download attachment",
301+
"request": {
302+
"method": "GET",
303+
"header": [
304+
{
305+
"key": "Authorization",
306+
"value": "Bearer {{jwt-token}}"
307+
},
308+
{
309+
"key": "Content-Type",
310+
"value": "application/json"
311+
}
312+
],
313+
"body": {
314+
"mode": "raw",
315+
"raw": ""
316+
},
317+
"url": {
318+
"raw": "{{api-url}}/v4/projects/1/attachments/2",
319+
"host": [
320+
"{{api-url}}"
321+
],
322+
"path": [
323+
"v4",
324+
"projects",
325+
"1",
326+
"attachments",
327+
"2"
328+
]
329+
},
330+
"description": "Create an project attachment"
331+
},
332+
"response": []
333+
},
334+
{
335+
"name": "Download attachment admin",
336+
"request": {
337+
"method": "GET",
338+
"header": [
339+
{
340+
"key": "Authorization",
341+
"value": "Bearer {{jwt-token-admin-40051333}}"
342+
},
343+
{
344+
"key": "Content-Type",
345+
"value": "application/json"
346+
}
347+
],
348+
"body": {
349+
"mode": "raw",
350+
"raw": ""
351+
},
352+
"url": {
353+
"raw": "{{api-url}}/v4/projects/1/attachments/2",
354+
"host": [
355+
"{{api-url}}"
356+
],
357+
"path": [
358+
"v4",
359+
"projects",
360+
"1",
361+
"attachments",
362+
"2"
363+
]
364+
},
365+
"description": "Create an project attachment"
366+
},
367+
"response": []
368+
},
369+
{
370+
"name": "Download attachment - No access",
371+
"request": {
372+
"method": "GET",
373+
"header": [
374+
{
375+
"key": "Authorization",
376+
"value": "Bearer {{jwt-token-copilot-40051332}}"
377+
},
378+
{
379+
"key": "Content-Type",
380+
"value": "application/json"
381+
}
382+
],
383+
"body": {
384+
"mode": "raw",
385+
"raw": ""
386+
},
387+
"url": {
388+
"raw": "{{api-url}}/v4/projects/1/attachments/2",
389+
"host": [
390+
"{{api-url}}"
391+
],
392+
"path": [
393+
"v4",
394+
"projects",
395+
"1",
396+
"attachments",
397+
"2"
398+
]
399+
},
400+
"description": "Create an project attachment"
401+
},
402+
"response": []
403+
},
299404
{
300405
"name": "Update attachment",
301406
"request": {
@@ -315,14 +420,49 @@
315420
"raw": "{\n\t\"param\": {\n\t\t\"title\": \"first attachment submission updated\",\n\t\t\"description\": \"updated project attachment\"\n\t}\n}"
316421
},
317422
"url": {
318-
"raw": "{{api-url}}/v4/projects/7/attachments/2",
423+
"raw": "{{api-url}}/v4/projects/1/attachments/2",
319424
"host": [
320425
"{{api-url}}"
321426
],
322427
"path": [
323428
"v4",
324429
"projects",
325-
"7",
430+
"1",
431+
"attachments",
432+
"2"
433+
]
434+
},
435+
"description": "Update project attachment"
436+
},
437+
"response": []
438+
},
439+
{
440+
"name": "Update attachment - No access",
441+
"request": {
442+
"method": "PATCH",
443+
"header": [
444+
{
445+
"key": "Authorization",
446+
"value": "Bearer {{jwt-token-copilot-40051332}}"
447+
},
448+
{
449+
"key": "Content-Type",
450+
"value": "application/json"
451+
}
452+
],
453+
"body": {
454+
"mode": "raw",
455+
"raw": "{\n\t\"param\": {\n\t\t\"title\": \"first attachment submission updated\",\n\t\t\"description\": \"updated project attachment\",\n\t\t\"userIds\": null\n\t}\n}"
456+
},
457+
"url": {
458+
"raw": "{{api-url}}/v4/projects/1/attachments/2",
459+
"host": [
460+
"{{api-url}}"
461+
],
462+
"path": [
463+
"v4",
464+
"projects",
465+
"1",
326466
"attachments",
327467
"2"
328468
]
@@ -365,6 +505,41 @@
365505
"description": "Delete a project attachment"
366506
},
367507
"response": []
508+
},
509+
{
510+
"name": "Delete attachment - No access",
511+
"request": {
512+
"method": "DELETE",
513+
"header": [
514+
{
515+
"key": "Authorization",
516+
"value": "Bearer {{jwt-token-copilot-40051332}}"
517+
},
518+
{
519+
"key": "Content-Type",
520+
"value": "application/json"
521+
}
522+
],
523+
"body": {
524+
"mode": "raw",
525+
"raw": ""
526+
},
527+
"url": {
528+
"raw": "{{api-url}}/v4/projects/1/attachments/2",
529+
"host": [
530+
"{{api-url}}"
531+
],
532+
"path": [
533+
"v4",
534+
"projects",
535+
"1",
536+
"attachments",
537+
"2"
538+
]
539+
},
540+
"description": "Delete a project attachment"
541+
},
542+
"response": []
368543
}
369544
]
370545
},
@@ -1098,7 +1273,7 @@
10981273
],
10991274
"body": {
11001275
"mode": "raw",
1101-
"raw": "{\n\t\"param\": {\n\t\t\"name\": \"test project\",\n\t\t\"description\": \"Hello I am a test project\",\n\t\t\"type\": \"generic\"\n\t}\n}"
1276+
"raw": "{\n \"param\": {\n \"name\": \"Test 3\",\n \"details\": {\n \"utm\": {\n \"code\": \"\"\n },\n \"appDefinition\": {\n \"primaryTarget\": \"phone\",\n \"goal\": {\n \"value\": \"Nothing\"\n },\n \"users\": {\n \"value\": \"No one\"\n },\n \"notes\": \"\"\n },\n \"hideDiscussions\": true\n },\n \"description\": \"Hello this is a sample description... This requires at least 160 characters. I'm trying to satisfy this condition. But I could n't if I don't type this unnecessary message\",\n \"templateId\": 3,\n \"type\": \"app\"\n }\n}"
11021277
},
11031278
"url": {
11041279
"raw": "{{api-url}}/v4/projects",
@@ -2240,17 +2415,17 @@
22402415
],
22412416
"body": {
22422417
"mode": "raw",
2243-
"raw": "{\n\t\"param\": {\n\t\t\"name\": \"test project phase\",\n\t\t\"status\": \"active\",\n\t\t\"startDate\": \"2018-05-15T00:00:00\",\n\t\t\"endDate\": \"2018-05-16T00:00:00\",\n\t\t\"budget\": 20,\n\t\t\"details\": {\n\t\t\t\"aDetails\": \"a details\"\n\t\t}\n\t}\n}"
2418+
"raw": "{\n\t\"param\": {\n\t\t\"name\": \"test project phase\",\n\t\t\"status\": \"active\",\n\t\t\"startDate\": \"2019-02-15T00:00:00\",\n\t\t\"endDate\": \"2019-05-16T00:00:00\",\n\t\t\"budget\": 20,\n\t\t\"details\": {\n\t\t\t\"aDetails\": \"a details\"\n\t\t}\n\t}\n}"
22442419
},
22452420
"url": {
2246-
"raw": "{{api-url}}/v4/projects/1/phases",
2421+
"raw": "{{api-url}}/v4/projects/2/phases",
22472422
"host": [
22482423
"{{api-url}}"
22492424
],
22502425
"path": [
22512426
"v4",
22522427
"projects",
2253-
"1",
2428+
"2",
22542429
"phases"
22552430
]
22562431
}
@@ -2594,7 +2769,7 @@
25942769
"raw": ""
25952770
},
25962771
"url": {
2597-
"raw": "{{api-url}}/v4/projects/1/phases/3",
2772+
"raw": "{{api-url}}/v4/projects/1/phases/2",
25982773
"host": [
25992774
"{{api-url}}"
26002775
],
@@ -2603,7 +2778,7 @@
26032778
"projects",
26042779
"1",
26052780
"phases",
2606-
"3"
2781+
"2"
26072782
]
26082783
}
26092784
},
@@ -2630,7 +2805,7 @@
26302805
],
26312806
"body": {
26322807
"mode": "raw",
2633-
"raw": "{\n\t\"param\": {\n\t\t\"name\": \"test phase product\",\n\t\t\"type\": \"type 1\",\n\t\t\"estimatedPrice\": 10\n\t}\n}"
2808+
"raw": "{\n\t\"param\": {\n\t\t\"name\": \"test phase product\",\n\t\t\"type\": \"application_development\",\n\t\t\"estimatedPrice\": 10000\n\t}\n}"
26342809
},
26352810
"url": {
26362811
"raw": "{{api-url}}/v4/projects/1/phases/1/products",
@@ -3147,7 +3322,7 @@
31473322
],
31483323
"body": {
31493324
"mode": "raw",
3150-
"raw": "{\r\n \"param\":{\r\n \"key\": \"generic\",\r\n \"displayName\": \"new displayName\",\r\n \"icon\": \"http://example.com/icon4.ico\",\r\n \t\"question\": \"question 4\",\r\n \t\"info\": \"info 4\",\r\n \t\"aliases\": [\"key-41\", \"key_42\"],\r\n \t\"metadata\": {}\r\n }\r\n}"
3325+
"raw": "{\r\n \"param\":{\r\n \"key\": \"app\",\r\n \"displayName\": \"new displayName\",\r\n \"icon\": \"http://example.com/icon4.ico\",\r\n \t\"question\": \"question 4\",\r\n \t\"info\": \"info 4\",\r\n \t\"aliases\": [\"key-41\", \"key_42\"],\r\n \t\"metadata\": {}\r\n }\r\n}"
31513326
},
31523327
"url": {
31533328
"raw": "{{api-url}}/v4/projects/metadata/projectTypes",
@@ -3320,7 +3495,7 @@
33203495
],
33213496
"body": {
33223497
"mode": "raw",
3323-
"raw": "{\r\n \"param\":{\r\n \"key\": \"generic\",\r\n \"displayName\": \"new displayName\",\r\n \"icon\": \"icon\",\r\n \"question\": \"question\",\r\n \"info\": \"info\",\r\n \"aliases\": [\"key-1\", \"key-2\"]\r\n }\r\n}"
3498+
"raw": "{\r\n \"param\":{\r\n \"key\": \"app\",\r\n \"displayName\": \"new displayName\",\r\n \"icon\": \"icon\",\r\n \"question\": \"question\",\r\n \"info\": \"info\",\r\n \"aliases\": [\"key-1\", \"key-2\"]\r\n }\r\n}"
33243499
},
33253500
"url": {
33263501
"raw": "{{api-url}}/v4/projects/metadata/productCategories",
@@ -3663,7 +3838,7 @@
36633838
],
36643839
"body": {
36653840
"mode": "raw",
3666-
"raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"description\":\"new description\",\r\n \"startDate\":\"2018-05-29T00:00:00.000Z\",\r\n \"endDate\": \"2018-05-30T00:00:00.000Z\",\r\n \"reference\": \"project\",\r\n \"referenceId\": 1\r\n }\r\n}"
3841+
"raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"description\":\"new description\",\r\n \"startDate\":\"2019-02-29T00:00:00.000Z\",\r\n \"endDate\": \"2019-04-30T00:00:00.000Z\",\r\n \"reference\": \"project\",\r\n \"referenceId\": 1\r\n }\r\n}"
36673842
},
36683843
"url": {
36693844
"raw": "{{api-url}}/v4/timelines",
@@ -3694,7 +3869,7 @@
36943869
],
36953870
"body": {
36963871
"mode": "raw",
3697-
"raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"description\":\"new description\",\r\n \"startDate\":\"2018-05-29T00:00:00.000Z\",\r\n \"endDate\": \"2018-05-30T00:00:00.000Z\",\r\n \"reference\": \"project\",\r\n \"referenceId\": 1,\r\n \"templateId\": 1\r\n }\r\n}"
3872+
"raw": "{\r\n \"param\":{\r\n \"name\":\"new name\",\r\n \"description\":\"new description\",\r\n \"startDate\":\"2019-02-29T00:00:00.000Z\",\r\n \"endDate\": \"2019-04-30T00:00:00.000Z\",\r\n \"reference\": \"project\",\r\n \"referenceId\": 1,\r\n \"templateId\": 1\r\n }\r\n}"
36983873
},
36993874
"url": {
37003875
"raw": "{{api-url}}/v4/timelines",

src/events/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { projectMemberAddedHandler, projectMemberRemovedHandler,
77
import { projectMemberInviteCreatedHandler,
88
projectMemberInviteUpdatedHandler } from './projectMemberInvites';
99
import { projectAttachmentRemovedHandler,
10-
projectAttachmentUpdatedHandler } from './projectAttachments';
10+
projectAttachmentUpdatedHandler, projectAttachmentAddedHandler } from './projectAttachments';
1111
import { projectPhaseAddedHandler, projectPhaseRemovedHandler,
1212
projectPhaseUpdatedHandler } from './projectPhases';
1313
import { phaseProductAddedHandler, phaseProductRemovedHandler,
@@ -35,7 +35,7 @@ export const rabbitHandlers = {
3535
[EVENT.ROUTING_KEY.PROJECT_MEMBER_UPDATED]: projectMemberUpdatedHandler,
3636
[EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED]: projectMemberInviteCreatedHandler,
3737
[EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_UPDATED]: projectMemberInviteUpdatedHandler,
38-
[EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_ADDED]: projectMemberInviteUpdatedHandler,
38+
[EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_ADDED]: projectAttachmentAddedHandler,
3939
[EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_REMOVED]: projectAttachmentRemovedHandler,
4040
[EVENT.ROUTING_KEY.PROJECT_ATTACHMENT_UPDATED]: projectAttachmentUpdatedHandler,
4141
[EVENT.ROUTING_KEY.PROJECT_PHASE_ADDED]: projectPhaseAddedHandler,

src/models/projectAttachment.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = function defineProjectAttachment(sequelize, DataTypes) {
99
description: { type: DataTypes.STRING, allowNull: true },
1010
filePath: { type: DataTypes.STRING, allowNull: false },
1111
contentType: { type: DataTypes.STRING, allowNull: false },
12+
userIds: DataTypes.ARRAY({ type: DataTypes.INTEGER, allowNull: true }),
1213
deletedAt: { type: DataTypes.DATE, allowNull: true },
1314
createdAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
1415
updatedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
@@ -33,6 +34,33 @@ module.exports = function defineProjectAttachment(sequelize, DataTypes) {
3334
raw: true,
3435
});
3536
},
37+
38+
getAttachmentById(projectId, attachmentId) {
39+
return this.findOne({
40+
where: {
41+
projectId,
42+
id: attachmentId,
43+
},
44+
});
45+
},
46+
47+
getAttachmentsForUser(projectId, userId) {
48+
return this.findAll({
49+
where: {
50+
projectId,
51+
$or: [{
52+
createdBy: { $eq: userId },
53+
}, {
54+
userIds: {
55+
$or: [
56+
{ $contains: [userId] },
57+
{ $eq: null },
58+
],
59+
},
60+
}],
61+
},
62+
});
63+
},
3664
},
3765
});
3866

0 commit comments

Comments
 (0)