Skip to content

Commit

Permalink
feat: add and call v3 API for retrieving individual admin form (#2113) (
Browse files Browse the repository at this point in the history
#2201)

* feat: add v3 API for retrieving admin form by id

* feat: remove `@routes` JSDoc line as they do not add info

and can be often wrong, and the real route is already lines away

* feat(AdminViewFormService): call v3 API to retrieve admin form

this allows us to eventually deprecate the old GET /adminform endpoints (and its ilk)

Co-authored-by: Kar Rui Lau <karrui.lau@gmail.com>
  • Loading branch information
mantariksh and karrui committed Jun 17, 2021
1 parent 320caed commit 7908bbc
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,172 @@ describe('admin-form.form.routes', () => {
})
})

describe('GET /admin/forms/:formId', () => {
it('should return 200 with retrieved form when user is admin', async () => {
// Arrange
const ownForm = await EmailFormModel.create({
title: 'Own form',
emails: [defaultUser.email],
admin: defaultUser._id,
})

const response = await request.get(`/admin/forms/${ownForm._id}`)
// Act

// Assert
const expected = await FormModel.findById(ownForm._id)
.populate({
path: 'admin',
populate: {
path: 'agency',
},
})
.lean()
expect(response.status).toEqual(200)
expect(response.body).not.toBeNull()
expect(response.body).toEqual({
form: jsonParseStringify(expected),
})
})

it('should return 200 with retrieved form when user has read permissions', async () => {
// Arrange
// Create separate user
const collabUser = (
await dbHandler.insertFormCollectionReqs({
userId: new ObjectId(),
mailName: 'collab-user',
shortName: 'collabUser',
})
).user

const collabForm = await EncryptFormModel.create({
title: 'Collab form',
publicKey: 'some public key',
admin: collabUser._id,
permissionList: [{ email: defaultUser.email, write: false }],
})

// Act
const response = await request.get(`/admin/forms/${collabForm._id}`)

// Assert
const expected = await FormModel.findById(collabForm._id)
.populate({
path: 'admin',
populate: {
path: 'agency',
},
})
.lean()
expect(response.status).toEqual(200)
expect(response.body).not.toBeNull()
expect(response.body).toEqual({
form: jsonParseStringify(expected),
})
})

it('should return 401 when user is not logged in', async () => {
// Arrange
await logoutSession(request)

// Act
const response = await request.get(`/admin/forms/${new ObjectId()}`)

// Assert
expect(response.status).toEqual(401)
expect(response.body).toEqual({ message: 'User is unauthorized.' })
})

it('should return 403 when user does not have read permissions to form', async () => {
// Arrange
const anotherUser = (
await dbHandler.insertFormCollectionReqs({
userId: new ObjectId(),
mailName: 'some-user',
shortName: 'someUser',
})
).user
// Form that defaultUser has no access to.
const inaccessibleForm = await EncryptFormModel.create({
title: 'Collab form',
publicKey: 'some public key',
admin: anotherUser._id,
permissionList: [],
})

// Act
const response = await request.get(`/admin/forms/${inaccessibleForm._id}`)

// Assert
expect(response.status).toEqual(403)
expect(response.body).toEqual({
message: expect.stringContaining(
'not authorized to perform read operation',
),
})
})

it('should return 404 when form cannot be found', async () => {
// Arrange
const invalidFormId = new ObjectId().toHexString()

// Act
const response = await request.get(`/admin/forms/${invalidFormId}`)

// Assert
expect(response.status).toEqual(404)
expect(response.body).toEqual({ message: 'Form not found' })
})

it('should return 410 when form to retrieve is archived', async () => {
// Arrange
const archivedForm = await EncryptFormModel.create({
title: 'archived form',
status: Status.Archived,
responseMode: ResponseMode.Encrypt,
publicKey: 'does not matter',
admin: defaultUser._id,
})

// Act
const response = await request.get(`/admin/forms/${archivedForm._id}`)

// Assert
expect(response.status).toEqual(410)
expect(response.body).toEqual({ message: 'Form has been archived' })
})

it('should return 422 when user in session cannot be retrieved from the database', async () => {
// Arrange
// Clear user collection
await dbHandler.clearCollection(UserModel.collection.name)

// Act
const response = await request.get(`/admin/forms/${new ObjectId()}`)

// Assert
expect(response.status).toEqual(422)
expect(response.body).toEqual({ message: 'User not found' })
})

it('should return 500 when database error occurs whilst retrieving form', async () => {
// Arrange
jest
.spyOn(FormModel, 'getFullFormById')
.mockRejectedValueOnce(new Error('some error'))

// Act
const response = await request.get(`/admin/forms/${new ObjectId()}`)

// Assert
expect(response.status).toEqual(500)
expect(response.body).toEqual({
message: 'Something went wrong. Please try again.',
})
})
})

describe('DELETE /admin/forms/:formId', () => {
it('should return 200 with success message when form is successfully archived', async () => {
// Arrange
Expand Down
52 changes: 27 additions & 25 deletions src/app/routes/api/v3/admin/forms/admin-forms.form.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export const AdminFormsFormRouter = Router()
AdminFormsFormRouter.route('/')
/**
* List the forms managed by the user
* @route GET /adminform
* @security session
*
* @returns 200 with a list of forms managed by the user
Expand All @@ -19,7 +18,6 @@ AdminFormsFormRouter.route('/')

/**
* Create a new form
* @route POST /adminform
* @security session
*
* @returns 200 with newly created form
Expand All @@ -33,27 +31,36 @@ AdminFormsFormRouter.route('/')
*/
.post(AdminFormController.handleCreateForm)

/**
* Archive the specified form.
* @route DELETE /:formId/adminform
* @security session
*
* @returns 200 with success message when successfully archived
* @returns 401 when user does not exist in session
* @returns 403 when user does not have permissions to archive form
* @returns 404 when form cannot be found
* @returns 410 when form is already archived
* @returns 422 when user in session cannot be retrieved from the database
* @returns 500 when database error occurs
*/
AdminFormsFormRouter.delete(
'/:formId([a-fA-F0-9]{24})',
AdminFormController.handleArchiveForm,
)
AdminFormsFormRouter.route('/:formId([a-fA-F0-9]{24})')
/**
* Return the specified form to the user.
* @security session
*
* @returns 200 with retrieved form with formId if user has read permissions
* @returns 401 when user does not exist in session
* @returns 403 when user does not have permissions to access form
* @returns 404 when form cannot be found
* @returns 410 when form is archived
* @returns 422 when user in session cannot be retrieved from the database
* @returns 500 when database error occurs
*/
.get(AdminFormController.handleGetAdminForm)
/**
* Archive the specified form.
* @security session
*
* @returns 200 with success message when successfully archived
* @returns 401 when user does not exist in session
* @returns 403 when user does not have permissions to archive form
* @returns 404 when form cannot be found
* @returns 410 when form is already archived
* @returns 422 when user in session cannot be retrieved from the database
* @returns 500 when database error occurs
*/
.delete(AdminFormController.handleArchiveForm)

/**
* Duplicate the specified form.
* @route POST /:formId/adminform
* @security session
*
* @returns 200 with the duplicate form dashboard view
Expand All @@ -72,7 +79,6 @@ AdminFormsFormRouter.post(

/**
* Transfer form ownership to another user
* @route POST /:formId/adminform/transfer-owner
* @security session
*
* @returns 200 with updated form with transferred owners
Expand All @@ -99,7 +105,6 @@ AdminFormsFormRouter.route(
)
/**
* Update form field according to given new body.
* @route PUT /admin/forms/:formId/fields/:fieldId
*
* @param body the new field to override current field
* @returns 200 with updated form field
Expand All @@ -117,7 +122,6 @@ AdminFormsFormRouter.route(

/**
* Delete form field by fieldId of form corresponding to formId.
* @route DELETE /admin/forms/:formId/fields/:fieldId
* @security session
*
* @returns 204 when deletion is successful
Expand All @@ -131,7 +135,6 @@ AdminFormsFormRouter.route(
.delete(AdminFormController.handleDeleteFormField)
/**
* Retrives the form field using the fieldId from the specified form
* @route GET /admin/forms/:formId/fields/:fieldId
* @security session
*
* @returns 200 with form field when retrieval is successful
Expand All @@ -146,7 +149,6 @@ AdminFormsFormRouter.route(

/**
* Duplicates the form field with the fieldId from the specified form
* @route POST /:formId/fields/:fieldId/duplicate
* @security session
*
* @returns 200 with duplicated field
Expand Down
4 changes: 3 additions & 1 deletion src/public/services/AdminViewFormService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export const getDashboardView = async (): Promise<FormMetaView[]> => {
export const getAdminFormView = async (
formId: string,
): Promise<FormViewDto> => {
return axios.get<FormViewDto>(`/${formId}/adminform`).then(({ data }) => data)
return axios
.get<FormViewDto>(`${ADMIN_FORM_ENDPOINT}/${formId}`)
.then(({ data }) => data)
}

/**
Expand Down
8 changes: 6 additions & 2 deletions src/public/services/__tests__/AdminViewFormService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ describe('AdminViewFormService', () => {

// Assert
expect(actual).toEqual(expected)
expect(MockAxios.get).toHaveBeenCalledWith(`/${expected._id}/adminform`)
expect(MockAxios.get).toHaveBeenCalledWith(
`${ADMIN_FORM_ENDPOINT}/${expected._id}`,
)
})

it('should reject with error message when GET request fails', async () => {
Expand All @@ -96,7 +98,9 @@ describe('AdminViewFormService', () => {

// Assert
await expect(actualPromise).rejects.toEqual(expected)
expect(MockAxios.get).toHaveBeenCalledWith(`/${MOCK_FORM._id}/adminform`)
expect(MockAxios.get).toHaveBeenCalledWith(
`${ADMIN_FORM_ENDPOINT}/${MOCK_FORM._id}`,
)
})
})

Expand Down

0 comments on commit 7908bbc

Please sign in to comment.