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
28 changes: 27 additions & 1 deletion docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ paths:
in: body
required: true
schema:
$ref: '#/definitions/ResourceRequestBody'
$ref: '#/definitions/CreateResourceRequestBody'
responses:
'200':
description: OK - the request was successful
Expand Down Expand Up @@ -741,6 +741,32 @@ definitions:
type: string
format: UUID
description: The resource role ID
CreateResourceRequestBody:
type: object
required:
- challengeId
- memberHandle
- memberId
- roleId
properties:
challengeId:
type: string
format: UUID
description: The challenge id
memberHandle:
type: string
description: The member handle
memberId:
type: string
description: The member id
roleId:
type: string
format: UUID
description: The resource role ID
sendEmail:
type: boolean
default: true
description: Set to `false` to create the resource without emitting the registration email event.
ResourceRolePhaseDependencyRequestBody:
type: object
required:
Expand Down
16 changes: 12 additions & 4 deletions src/services/ResourceService.js
Original file line number Diff line number Diff line change
Expand Up @@ -492,12 +492,16 @@ async function init (currentUser, challengeId, resource, isCreated) {
* Create resource for a challenge.
* @param {Object} currentUser the current user
* @param {Object} resource the resource to be created
* @param {Boolean} [resource.sendEmail=true] whether submitter registration should emit
* the registration email event
* @returns {Object} the created resource
*/
async function createResource (currentUser, resource) {
try {
const challengeId = resource.challengeId
const { memberId, handle, email, challenge, closeRegistration } = await init(currentUser, challengeId, resource, true)
const shouldSendRegistrationEmail = resource.sendEmail !== false
const resourceData = _.omit(resource, 'sendEmail')

const timelineTemplateId = _.get(challenge, 'timelineTemplateId', null)

Expand All @@ -507,7 +511,7 @@ async function createResource (currentUser, resource) {
createdBy: _.toString(memberId),
createdAt: moment().utc().format(),
memberHandle: handle
}, resource)
}, resourceData)
const createdResource = await prisma.resource.create({
data: prismaData,
include: {
Expand Down Expand Up @@ -537,7 +541,9 @@ async function createResource (currentUser, resource) {
logger.warn(`Failed to increment numOfRegistrants for challenge ${challengeId}: ${e}`)
}
}
if (!_.get(challenge, 'task.isTask', false) && resource.roleId === config.SUBMITTER_RESOURCE_ROLE_ID) {
if (!_.get(challenge, 'task.isTask', false) &&
resource.roleId === config.SUBMITTER_RESOURCE_ROLE_ID &&
shouldSendRegistrationEmail) {
const forumUrl = _.get(challenge, 'discussions[0].url')
let templateId = config.REGISTRATION_EMAIL.SENDGRID_TEMPLATE_ID
if (_.isUndefined(forumUrl)) {
Expand Down Expand Up @@ -595,13 +601,15 @@ createResource.schema = {
challengeId: Joi.id(),
memberId: Joi.string(),
memberHandle: Joi.string().required(),
roleId: Joi.id()
roleId: Joi.id(),
sendEmail: Joi.boolean().default(true)
}),
Joi.object().keys({
challengeId: Joi.id(),
memberId: Joi.string().required(),
memberHandle: Joi.string(),
roleId: Joi.id()
roleId: Joi.id(),
sendEmail: Joi.boolean().default(true)
})
)
}
Expand Down
76 changes: 76 additions & 0 deletions test/unit/createResource.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,69 @@ module.exports = describe('Create resource', () => {
await assertResource(ret.id, ret)
})

it('create submitter resource sends registration email by default', async () => {
const entity = resources.createBody('emailnotifyuser', submitterRoleId, challengeId3)
const postedEvents = []
const originalPostEvent = helper.postEvent
let createdResourceId

helper.postEvent = async (topic, payload) => {
postedEvents.push({ topic, payload })
}

try {
const ret = await service.createResource(user.m2m, entity)
createdResourceId = ret.id
const emailEvent = postedEvents.find((event) => event.topic === config.EMAIL_NOTIFICATIN_TOPIC)

should.exist(emailEvent)
should.deepEqual(emailEvent.payload.recipients, ['test@topcoder.com'])
should.equal(emailEvent.payload.data.handle, 'emailnotifyuser')
} finally {
helper.postEvent = originalPostEvent
if (createdResourceId) {
await prisma.resource.deleteMany({
where: { id: createdResourceId }
})
}
}
})

it('create submitter resource skips registration email when sendEmail is false', async () => {
const entity = {
...resources.createBody('emailnotifyuser-noemail', submitterRoleId, challengeId3),
sendEmail: false
}
const postedEvents = []
const originalPostEvent = helper.postEvent
let createdResourceId

helper.postEvent = async (topic, payload) => {
postedEvents.push({ topic, payload })
}

try {
const ret = await service.createResource(user.m2m, entity)
createdResourceId = ret.id

should.equal(
postedEvents.some((event) => event.topic === config.EMAIL_NOTIFICATIN_TOPIC),
false
)
should.equal(
postedEvents.some((event) => event.topic === config.RESOURCE_CREATE_TOPIC),
true
)
} finally {
helper.postEvent = originalPostEvent
if (createdResourceId) {
await prisma.resource.deleteMany({
where: { id: createdResourceId }
})
}
}
})

it('copilot can manage resources without full access flags', async () => {
const originalRole = await helper.getById('ResourceRole', copilotRoleId)
await ResourceRoleService.updateResourceRole(user.admin, copilotRoleId, {
Expand Down Expand Up @@ -385,6 +448,19 @@ module.exports = describe('Create resource', () => {
})
}

it('test invalid parameters, sendEmail must be boolean', async () => {
try {
const entity = {
..._.cloneDeep(testBody),
sendEmail: 'invalid'
}
await service.createResource(user.m2m, entity)
throw new Error('should not throw error here')
} catch (err) {
assertValidationError(err, '"sendEmail" must be a boolean')
}
})

for (const requiredField of requiredFields) {
it(`test invalid parameters, required field ${requiredField} is missing`, async () => {
let entity = _.cloneDeep(testBody)
Expand Down
Loading