Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion mock-api/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ app.get('/v5/resources', (req, res) => {
const challengeId = req.query.challengeId
winston.info(`Get resources of challenge id ${challengeId}`)

const memberId = req.query.memberId

const resources = [{
id: '22ba038e-48da-487b-96e8-8d3b99b6d181',
challengeId,
Expand All @@ -82,8 +84,16 @@ app.get('/v5/resources', (req, res) => {
roleId: '732339e7-8e30-49d7-9198-cccf9451e221'
}]

let ret
if (memberId) {
// filter with memberId
ret = _.filter(resources, r => r.memberId === memberId)
} else {
ret = resources
}

winston.info(`Challenge resources: ${JSON.stringify(resources, null, 4)}`)
res.json(resources)
res.json(ret)
})

// get challenges member can access to
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"license": "MIT",
"repository": "https://github.com/topcoder-platform/challenge-api",
"devDependencies": {
"aws-sdk-mock": "^6.2.1",
"chai": "^4.2.0",
"chai-http": "^4.2.1",
"mocha": "^11.1.0",
Expand Down
19 changes: 10 additions & 9 deletions src/common/challenge-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -392,15 +392,16 @@ class ChallengeHelper {
if (type) {
challenge.type = type.name;
}

challenge.metadata = challenge.metadata.map((m) => {
try {
m.value = JSON.stringify(JSON.parse(m.value)); // when we update how we index data, make this a JSON field
} catch (err) {
// do nothing
}
return m;
});
if (challenge.metadata) {
challenge.metadata = challenge.metadata.map((m) => {
try {
m.value = JSON.stringify(JSON.parse(m.value)); // when we update how we index data, make this a JSON field
} catch (err) {
// do nothing
}
return m;
});
}
}

static convertDateToISOString(startDate) {
Expand Down
15 changes: 12 additions & 3 deletions src/common/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,16 @@ AWS.config.update({
// secretAccessKey: config.AMAZON.AWS_SECRET_ACCESS_KEY,
region: config.AMAZON.AWS_REGION,
});
const s3 = new AWS.S3();

let s3

// lazy initialization of S3 instance
function getS3() {
if (!s3) {
s3 = new AWS.S3();
}
return s3
}

/**
* Wrap async function to standard express function
Expand Down Expand Up @@ -194,7 +203,7 @@ async function downloadFromFileStack(url) {
* @return {Promise} promise resolved to downloaded data
*/
async function downloadFromS3(bucket, key) {
const file = await s3.getObject({ Bucket: bucket, Key: key }).promise();
const file = await getS3().getObject({ Bucket: bucket, Key: key }).promise();
return {
data: file.Body,
mimetype: file.ContentType,
Expand All @@ -208,7 +217,7 @@ async function downloadFromS3(bucket, key) {
* @return {Promise} promise resolved to deleted data
*/
async function deleteFromS3(bucket, key) {
return s3.deleteObject({ Bucket: bucket, Key: key }).promise();
return getS3().deleteObject({ Bucket: bucket, Key: key }).promise();
}

/**
Expand Down
9 changes: 8 additions & 1 deletion src/services/AttachmentService.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ const helper = require("../common/helper");
const s3ParseUrl = require("../common/s3ParseUrl");
const logger = require("../common/logger");
const constants = require("../../app-constants");
const {
enrichChallengeForResponse
} = require("../common/challenge-helper");
const prismaHelper = require('../common/prisma-helper');

const bucketWhitelist = config.AMAZON.BUCKET_WHITELIST.split(",").map((bucketName) =>
bucketName.trim()
Expand Down Expand Up @@ -43,8 +47,11 @@ async function _getChallengeAttachment(challengeId, attachmentId) {
const challenge = await prisma.challenge.findUnique({ where: { id: challengeId } })
const attachment = await prisma.attachment.findUnique({ where: { id: attachmentId } })
if (!challenge || !challenge.id || !attachment || attachment.challengeId !== challengeId) {
throw errors.NotFoundError(`Attachment ${attachmentId} not found in challenge ${challengeId}`)
throw new errors.NotFoundError(`Attachment ${attachmentId} not found in challenge ${challengeId}`)
}
// convert challenge data
enrichChallengeForResponse(challenge)
prismaHelper.convertModelToResponse(challenge)
return { challenge, attachment };
}

Expand Down
26 changes: 25 additions & 1 deletion test/testHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ let phase
let phase2
let timelineTemplate
let challenge
let taskChallenge

/**
* function to deeply compare arrays regardeless of the order
Expand All @@ -30,6 +31,7 @@ const phase1Id = uuid()
const phase2Id = uuid()
const timelineTemplateId = uuid()
const challengeId = uuid()
const taskChallengeId = uuid()

/**
* Create test data
Expand Down Expand Up @@ -129,6 +131,27 @@ async function createData () {
updatedBy: 'admin'
}
challenge = await prisma.challenge.create({ data: challengeData })

taskChallenge = await prisma.challenge.create({ data: {
id: taskChallengeId,
taskIsTask: true,
taskIsAssigned: true,
name: 'Task',
description: 'desc',
privateDescription: 'private description',
descriptionFormat: 'html',
timelineTemplate: { connect: { id: timelineTemplate.id } },
type: { connect: { id: challengeTypeId } },
track: { connect: { id: challengeTrackId } },
tags: ['tag1'],
projectId: 111,
legacyId: 222,
startDate: new Date(),
status: constants.challengeStatuses.Completed.toUpperCase(),
createdAt: new Date(),
createdBy: 'admin',
updatedBy: 'admin'
}})
}

const defaultProjectTerms = [
Expand Down Expand Up @@ -163,7 +186,7 @@ const additionalTerm = {
*/
async function clearData () {
await prisma.challenge.deleteMany({
where: { id: { in: [challengeId] } }
where: { id: { in: [challengeId, taskChallengeId] } }
})
await prisma.timelineTemplate.deleteMany({ where: { id: timelineTemplateId } })
await prisma.phase.deleteMany({ where: { id: { in: [phase1Id, phase2Id] } } })
Expand All @@ -182,6 +205,7 @@ function getData () {
phase2,
timelineTemplate,
challenge,
taskChallenge,
defaultProjectTerms,
additionalTerm,
mockTerms
Expand Down
121 changes: 37 additions & 84 deletions test/unit/AttachmentService.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,106 +7,61 @@ const fs = require('fs')
const path = require('path')
const uuid = require('uuid/v4')
const chai = require('chai')
const awsMock = require('aws-sdk-mock')
const service = require('../../src/services/AttachmentService')
const testHelper = require('../testHelper')
const prisma = require('../../src/common/prisma').getClient()

const should = chai.should()

const attachmentContent = fs.readFileSync(path.join(__dirname, '../attachment.txt'))

/*
describe('attachment service unit tests', () => {
// created attachment id
let id
// generated data
let data
// attachment for task challenge
let id2
const notFoundId = uuid()

before(async () => {
// mock S3 before creating S3 instance
awsMock.mock('S3', 'getObject', (params, callback) => {
callback(null, { Body: Buffer.from(attachmentContent) });
});
await testHelper.createData()
data = testHelper.getData()
})

after(async () => {
await testHelper.clearData()
})

describe('upload attachment tests', () => {
it('upload attachment successfully', async () => {
const result = await service.uploadAttachment({
isMachine: true
}, data.challenge.id, {
attachment: {
data: attachmentContent,
mimetype: 'text/plain',
name: 'attachment.txt',
size: attachmentContent.length
}
})
should.exist(result.id)
id = result.id
should.equal(result.fileSize, attachmentContent.length)
should.equal(result.fileName, 'attachment.txt')
should.equal(result.challengeId, data.challenge.id)
})

it('upload attachment - forbidden', async () => {
try {
await service.uploadAttachment({
roles: ['user']
}, data.challenge.id, {
attachment: {
data: attachmentContent,
mimetype: 'text/plain',
name: 'attachment.txt',
size: attachmentContent.length
}
})
} catch (e) {
should.equal(e.message, 'You are not allowed to upload attachment of the challenge.')
return
// create attachment
const createdAttachment = await prisma.attachment.create({
data: {
name: 'attachment.txt',
url: 'http://s3.amazonaws.com/topcoder_01/attachment.txt',
fileSize: 1024,
createdBy: 'testdata',
updatedBy: 'testdata',
challenge: { connect: { id: data.challenge.id } }
}
throw new Error('should not reach here')
})

it('upload attachment - file too large', async () => {
try {
await service.uploadAttachment({
isMachine: true
}, data.challenge.id, {
attachment: {
truncated: true,
data: attachmentContent,
mimetype: 'text/plain',
name: 'attachment.txt',
size: attachmentContent.length
}
})
} catch (e) {
should.equal(e.message.indexOf('attachment is too large') >= 0, true)
return
id = createdAttachment.id
const taskAttachment = await prisma.attachment.create({
data: {
name: 'attachment.txt',
url: 'http://s3.amazonaws.com/topcoder_01/attachment.txt',
fileSize: 1024,
createdBy: 'testdata',
updatedBy: 'testdata',
challenge: { connect: { id: data.taskChallenge.id } }
}
throw new Error('should not reach here')
})
id2 = taskAttachment.id
})

it('upload attachment - challenge not found', async () => {
try {
await service.uploadAttachment({
isMachine: true
}, notFoundId, {
attachment: {
data: attachmentContent,
mimetype: 'text/plain',
name: 'attachment.txt',
size: attachmentContent.length
}
})
} catch (e) {
should.equal(e.message, `Challenge with id: ${notFoundId} doesn't exist`)
return
}
throw new Error('should not reach here')
})
after(async () => {
await testHelper.clearData()
await prisma.attachment.deleteMany({ where: { id }})
// restore S3
awsMock.restore('S3');
})

describe('download attachment tests', () => {
Expand All @@ -118,9 +73,9 @@ describe('attachment service unit tests', () => {

it('download attachment - forbidden', async () => {
try {
await service.downloadAttachment({ roles: ['user'], userId: 678678 }, data.challenge.id, id)
await service.downloadAttachment({ roles: ['user'], userId: 678678 }, data.taskChallenge.id, id2)
} catch (e) {
should.equal(e.message, 'You are not allowed to download attachment of the challenge.')
should.equal(e.message, 'You don\'t have access to view this challenge')
return
}
throw new Error('should not reach here')
Expand All @@ -130,7 +85,7 @@ describe('attachment service unit tests', () => {
try {
await service.downloadAttachment({ isMachine: true }, data.challenge.id, notFoundId)
} catch (e) {
should.equal(e.message, `Attachment with id: ${notFoundId} doesn't exist`)
should.equal(e.message, `Attachment ${notFoundId} not found in challenge ${data.challenge.id}`)
return
}
throw new Error('should not reach here')
Expand All @@ -140,7 +95,7 @@ describe('attachment service unit tests', () => {
try {
await service.downloadAttachment({ isMachine: true }, notFoundId, id)
} catch (e) {
should.equal(e.message, 'The attachment challengeId does not match the path challengeId.')
should.equal(e.message, `Attachment ${id} not found in challenge ${notFoundId}`)
return
}
throw new Error('should not reach here')
Expand All @@ -167,5 +122,3 @@ describe('attachment service unit tests', () => {
})
})
})

*/