Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Resource Completion Status Update Endpoint for Cooperations #1005

Merged
merged 14 commits into from
Dec 3, 2024
10 changes: 6 additions & 4 deletions docs/cooperation/cooperation-schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,9 @@ definitions:
format: date-time
default: null
completionStatus:
type: string
enum:
- active
- completed
$ref: '#/definitions/completionStatus'
completionStatus:
type: string
enum:
- completed
- active
102 changes: 102 additions & 0 deletions docs/cooperation/cooperation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,105 @@ paths:
status: 404
code: DOCUMENT_NOT_FOUND
message: Cooperation with the specified id was not found.
/cooperations/{id}/{resourceId}/completionStatus:
patch:
security:
- cookieAuth: []
tags:
- Cooperations
summary: Update completion status of resource in cooperation by ID
description: Finds and update a completion status of resource in cooperation with the specified ID.
produces:
- application/json
parameters:
- name: id
in: path
required: true
description: Cooperation ID
schema:
type: string
- name: resourceId
in: path
required: true
description: Resource ID
schema:
type: string
requestBody:
required: true
description: Provide required data to update completion status of resource in cooperation.
content:
application/json:
schema:
properties:
completionStatus:
$ref: '#/definitions/completionStatus'
example:
completionStatus: completed
responses:
204:
description: No Content
links:
400:
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/Error'
example:
status: 400
code: INVALID_ID
message: ID is invalid.
401:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/Error'
example:
status: 401
code: UNAUTHORIZED
message: The requested URL requires user authorization.
403:
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/Error'
examples:
Forbidden:
value:
status: 403
code: FORBIDDEN
message: You do not have permission to perform this action.
'Role required for action':
value:
status: 403
code: ROLE_REQUIRED_FOR_ACTION
message: Only student can perform this action.
404:
description: Not Found
content:
application/json:
schema:
$ref: '#/components/Error'
example:
status: 404
code: DOCUMENT_NOT_FOUND
message: Resource in Cooperation with the specified ID was not found.
422:
description: Unprocessable Entity
content:
application/json:
schema:
$ref: '#/components/Error'
examples:
'Field is not of proper enum value':
value:
status: 422
code: FIELD_IS_NOT_OF_PROPER_ENUM_VALUE
message: 'completionStatus should be either one of the values: [active, completed]'
'Field is not defined':
value:
status: 422
code: FIELD_IS_NOT_DEFINED
message: completionStatus field should be defined
4 changes: 4 additions & 0 deletions src/consts/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ const errors = {
code: 'FIELD_IS_NOT_OF_PROPER_ENUM_VALUE',
message: `${field} should be either one of the values: [${enumSet.join(', ')}]`
}),
ROLE_REQUIRED_FOR_ACTION: (role) => ({
code: 'ROLE_REQUIRED_FOR_ACTION',
message: `Only ${role} can perform this action.`
}),
CHAT_ALREADY_EXISTS: {
code: 'CHAT_ALREADY_EXISTS',
message: 'The chat with this user already exists.'
Expand Down
13 changes: 12 additions & 1 deletion src/controllers/cooperation.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,20 @@ const updateCooperation = async (req, res) => {
res.status(204).end()
}

const updateResourceCompletionStatus = async (req, res) => {
const { id, resourceId } = req.params
const { completionStatus } = req.body
const currentUser = req.user

await cooperationService.updateResourceCompletionStatus({ id, currentUser, resourceId, completionStatus })

res.status(204).end()
}

module.exports = {
getCooperations,
getCooperationById,
createCooperation,
updateCooperation
updateCooperation,
updateResourceCompletionStatus
}
8 changes: 8 additions & 0 deletions src/routes/cooperation.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ const idValidation = require('~/middlewares/idValidation')
const asyncWrapper = require('~/middlewares/asyncWrapper')
const { authMiddleware } = require('~/middlewares/auth')
const isEntityValid = require('~/middlewares/entityValidation')
const validationMiddleware = require('~/middlewares/validation')

const noteRouter = require('~/routes/note')

const cooperationController = require('~/controllers/cooperation')
const Offer = require('~/models/offer')
const Cooperation = require('~/models/cooperation')
const { updateResourceCompletionStatusValidationSchema } = require('~/validation/schemas/cooperation')

const body = [{ model: Offer, idName: 'offer' }]
const params = [{ model: Cooperation, idName: 'id' }]
Expand All @@ -24,5 +26,11 @@ router.get('/', asyncWrapper(cooperationController.getCooperations))
router.post('/', isEntityValid({ body }), asyncWrapper(cooperationController.createCooperation))
router.get('/:id', isEntityValid({ params }), asyncWrapper(cooperationController.getCooperationById))
router.patch('/:id', isEntityValid({ params }), asyncWrapper(cooperationController.updateCooperation))
router.patch(
'/:id/:resourceId/completionStatus',
isEntityValid({ params }),
validationMiddleware(updateResourceCompletionStatusValidationSchema),
asyncWrapper(cooperationController.updateResourceCompletionStatus)
)

module.exports = router
48 changes: 36 additions & 12 deletions src/services/cooperation.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ const Cooperation = require('~/models/cooperation')
const mergeArraysUniqueValues = require('~/utils/mergeArraysUniqueValues')
const removeArraysUniqueValues = require('~/utils/removeArraysUniqueValues')
const handleResources = require('~/utils/handleResources')
const validateCooperationUser = require('~/utils/cooperations/validateCooperationUser')
const { createError, createForbiddenError } = require('~/utils/errorsHelper')
const { VALIDATION_ERROR, DOCUMENT_NOT_FOUND } = require('~/consts/errors')
const { VALIDATION_ERROR, DOCUMENT_NOT_FOUND, ROLE_REQUIRED_FOR_ACTION } = require('~/consts/errors')
const { roles } = require('~/consts/auth')

const cooperationService = {
getCooperations: async (pipeline) => {
Expand Down Expand Up @@ -69,17 +71,8 @@ const cooperationService = {
throw createError(409, VALIDATION_ERROR('You can change only either the status or the price in one operation'))
}

const cooperation = await Cooperation.findById(id).exec()
if (!cooperation) {
Copy link
Contributor Author

@luiqor luiqor Nov 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this check because we already have middleware for patch coopeation/{id} endpoint that performs the same validation, but returns a 400 error instead:

{
  "status": 400,
  "code": "INVALID_ID",
  "message": "ID is invalid."
}

throw createError(404, DOCUMENT_NOT_FOUND(Cooperation.modelName))
}

const initiator = cooperation.initiator.toString()
const receiver = cooperation.receiver.toString()

if (initiator !== currentUserId && receiver !== currentUserId) {
throw createForbiddenError()
}
const cooperation = await Cooperation.findById(id)
validateCooperationUser(cooperation, currentUserId)

if (price) {
if (currentUserRole !== cooperation.needAction.toString()) {
Expand Down Expand Up @@ -114,6 +107,37 @@ const cooperationService = {
cooperation.availableQuizzes = removeArraysUniqueValues(cooperation.availableQuizzes, cooperation.finishedQuizzes)
await cooperation.save()
}
},

updateResourceCompletionStatus: async ({ id, currentUser, resourceId, completionStatus }) => {
const { id: currentUserId, role: currentUserRole } = currentUser

if (currentUserRole !== roles.STUDENT) {
throw createError(403, ROLE_REQUIRED_FOR_ACTION(roles.STUDENT))
}

const cooperation = await Cooperation.findById(id)
validateCooperationUser(cooperation, currentUserId)

let resourceIdExists = false

cooperation.sections.forEach((section) => {
section.resources.forEach((resource) => {
if (resource.resource.toString() === resourceId) {
resource.completionStatus = completionStatus
resourceIdExists = true
}
})
})
ShadowOfTheSpace marked this conversation as resolved.
Show resolved Hide resolved

if (!resourceIdExists) {
throw createError(404, DOCUMENT_NOT_FOUND([`Resource in ${Cooperation.modelName}`]))
}

cooperation.markModified('sections')

await cooperation.validate()
await cooperation.save()
}
}

Expand Down
Loading
Loading