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

refactor(adminform): update form collab #1744

Merged
merged 21 commits into from
May 5, 2021
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
48c2673
feat(form/server/model): adds method to updateFormCollaborators
seaerchin Apr 27, 2021
d161b8a
feat(form/types; admin-form/service): adds service method for updatin…
seaerchin Apr 27, 2021
9734865
feat(admin-forms/settings/routes): adds controller and handler for up…
seaerchin Apr 27, 2021
2edf96f
feat(public/service): adds method on AdminFormService to update colla…
seaerchin Apr 27, 2021
9824906
refactor(collaborator-modal.client.controller): replaces old call wit…
seaerchin Apr 27, 2021
02f7e64
docs(admin-forms): fixed typos in handleUpdateCollaborators
seaerchin Apr 28, 2021
d42744c
test(tests): adds unit tests for form model, admin form service and a…
seaerchin Apr 28, 2021
cf0bff1
refactor(admin-form/controller): changed unknown on joi vaildator to …
seaerchin Apr 28, 2021
9ebfb68
refactor(types): adds _id to the type of Permission
seaerchin Apr 28, 2021
9271b89
refactor(admin-form/collaborators): updated dto to be just an array; …
seaerchin Apr 29, 2021
9ce023e
fix(collaborator-modal/client/controller): fixed fe callsites showing…
seaerchin Apr 29, 2021
3a5b5cf
fix(collaborator-modal/client/controller): fixed error caused by acci…
seaerchin Apr 29, 2021
bf7a6b4
Merge branch 'develop' into refactor/update-form-collab
seaerchin Apr 29, 2021
dba5f22
Merge branch 'develop' into refactor/update-form-collab
seaerchin Apr 29, 2021
32a6fa3
refactor(admin-form/controller): changed to use .json and formCollabo…
seaerchin May 3, 2021
45bd0f0
test(form/server/model/spec): changed test to use .rejects()
seaerchin May 3, 2021
f50f43e
style(collaborator-modal/client/controller): updated error message
seaerchin May 3, 2021
bf2efda
test(admin-forms): integration tests for updating of collaborators (#…
seaerchin May 4, 2021
87119cc
refactor(adminform): extract get collaborators (#1762)
seaerchin May 5, 2021
4188ab9
Merge branch 'develop' into refactor/update-form-collab
seaerchin May 5, 2021
77e6433
chore(admin-forms/settings/routes/spec): removed double import
seaerchin May 5, 2021
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
42 changes: 41 additions & 1 deletion src/app/models/__tests__/form.server.model.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { ObjectId } from 'bson-ext'
import { cloneDeep, merge, omit, orderBy, pick } from 'lodash'
import { cloneDeep, map, merge, omit, orderBy, pick } from 'lodash'
import mongoose, { Types } from 'mongoose'

import getFormModel, {
Expand Down Expand Up @@ -1676,5 +1676,45 @@ describe('Form Model', () => {
expect(updatedForm).toBeNull()
})
})

describe('updateFormCollaborators', () => {
it('should return the form with an updated list of collaborators', async () => {
// Arrange
const newCollaborators = [
{
email: `fakeuser@${MOCK_ADMIN_DOMAIN}`,
write: false,
},
]

// Act
const actual = await validForm.updateFormCollaborators(newCollaborators)

// Assert
const actualPermissionsWithoutId = map(
Copy link
Contributor

Choose a reason for hiding this comment

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

hahahahahaha @ using _.map instead of native map

Copy link
Contributor Author

Choose a reason for hiding this comment

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

y u laff

actual.permissionList,
(collaborator) => pick(collaborator, ['email', 'write']),
)
expect(actualPermissionsWithoutId).toEqual(newCollaborators)
})

it('should return an error if validation fails', async () => {
// Arrange
const newCollaborators = [
{
email: `[email protected]`,
write: false,
},
]

// Act
const actual = await validForm
.updateFormCollaborators(newCollaborators)
.catch((error) => error)

// Assert
expect(actual).toBeInstanceOf(mongoose.Error.ValidationError)
seaerchin marked this conversation as resolved.
Show resolved Hide resolved
})
})
})
})
8 changes: 8 additions & 0 deletions src/app/models/form.server.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,14 @@ const compileFormModel = (db: Mongoose): IFormModel => {
return this.save()
}

FormDocumentSchema.methods.updateFormCollaborators = async function (
this: IFormDocument,
updatedPermissions: Permission[],
) {
this.permissionList = updatedPermissions
return this.save()
}

FormDocumentSchema.methods.updateFormFieldById = function (
this: IFormDocument,
fieldId: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { PresignedPost } from 'aws-sdk/clients/s3'
import { ObjectId } from 'bson-ext'
import { StatusCodes } from 'http-status-codes'
import { assignIn, cloneDeep, merge, pick } from 'lodash'
import { err, errAsync, ok, okAsync, Result } from 'neverthrow'
import { PassThrough } from 'stream'
Expand Down Expand Up @@ -8037,4 +8038,180 @@ describe('admin-form.controller', () => {
)
})
})

describe('_handleUpdateCollaborators', () => {
const MOCK_USER_ID = new ObjectId().toHexString()
const MOCK_FORM_ID = new ObjectId().toHexString()
const MOCK_USER = {
_id: MOCK_USER_ID,
email: '[email protected]',
} as IPopulatedUser
const MOCK_FORM = {
admin: MOCK_USER,
_id: MOCK_FORM_ID,
} as IPopulatedForm
const MOCK_COLLABORATORS = [
{
email: `[email protected]`,
write: false,
},
]
const MOCK_REQ = expressHandler.mockRequest({
params: {
formId: MOCK_FORM_ID,
},
body: MOCK_COLLABORATORS,
session: {
user: {
_id: MOCK_USER_ID,
},
},
})

beforeEach(() => {
// Mock various services to return expected results.
MockUserService.getPopulatedUserById.mockReturnValue(okAsync(MOCK_USER))
MockAuthService.getFormAfterPermissionChecks.mockReturnValue(
okAsync(MOCK_FORM),
)
})
it('should return 200 when collaborators are updated successfully', async () => {
// Arrange
MockAdminFormService.updateFormCollaborators.mockReturnValueOnce(
okAsync(MOCK_COLLABORATORS),
)
const mockRes = expressHandler.mockResponse()

// Act
await AdminFormController._handleUpdateCollaborators(
MOCK_REQ,
mockRes,
jest.fn(),
)

// Assert
expect(mockRes.status).toBeCalledWith(StatusCodes.OK)
expect(mockRes.send).toBeCalledWith(MOCK_COLLABORATORS)
})

it('should return 403 when the user does not have sufficient permissions to update the form', async () => {
// Arrange
const ERROR_MESSAGE = 'all your base are belong to us'
MockAuthService.getFormAfterPermissionChecks.mockReturnValueOnce(
errAsync(new ForbiddenFormError(ERROR_MESSAGE)),
)
const mockRes = expressHandler.mockResponse()
const expectedResponse = { message: ERROR_MESSAGE }

// Act
await AdminFormController._handleUpdateCollaborators(
MOCK_REQ,
mockRes,
jest.fn(),
)

// Assert
expect(mockRes.status).toBeCalledWith(StatusCodes.FORBIDDEN)
expect(mockRes.json).toBeCalledWith(expectedResponse)
expect(
MockAdminFormService.updateFormCollaborators,
).not.toHaveBeenCalled()
})

it('should return 404 when the form could not be found', async () => {
// Arrange
const ERROR_MESSAGE = 'all your base are belong to us'
MockAuthService.getFormAfterPermissionChecks.mockReturnValueOnce(
errAsync(new FormNotFoundError(ERROR_MESSAGE)),
)
const mockRes = expressHandler.mockResponse()
const expectedResponse = { message: ERROR_MESSAGE }

// Act
await AdminFormController._handleUpdateCollaborators(
MOCK_REQ,
mockRes,
jest.fn(),
)

// Assert
expect(mockRes.status).toBeCalledWith(StatusCodes.NOT_FOUND)
expect(mockRes.json).toBeCalledWith(expectedResponse)
expect(
MockAdminFormService.updateFormCollaborators,
).not.toHaveBeenCalled()
})

it('should return 410 when the form has been archived', async () => {
// Arrange
const ERROR_MESSAGE = 'all your base are belong to us'
MockAuthService.getFormAfterPermissionChecks.mockReturnValueOnce(
errAsync(new FormDeletedError(ERROR_MESSAGE)),
)
const mockRes = expressHandler.mockResponse()
const expectedResponse = { message: ERROR_MESSAGE }

// Act
await AdminFormController._handleUpdateCollaborators(
MOCK_REQ,
mockRes,
jest.fn(),
)

// Assert
expect(mockRes.status).toBeCalledWith(StatusCodes.GONE)
expect(mockRes.json).toBeCalledWith(expectedResponse)
expect(
MockAdminFormService.updateFormCollaborators,
).not.toHaveBeenCalled()
})

it('should return 422 when the session user could not be retrieved from the database', async () => {
// Arrange
const ERROR_MESSAGE = 'all your base are belong to us'
MockUserService.getPopulatedUserById.mockReturnValueOnce(
errAsync(new MissingUserError(ERROR_MESSAGE)),
)
const expectedResponse = { message: ERROR_MESSAGE }
const mockRes = expressHandler.mockResponse()

// Act
await AdminFormController._handleUpdateCollaborators(
MOCK_REQ,
mockRes,
jest.fn(),
)

// Assert
expect(mockRes.status).toBeCalledWith(StatusCodes.UNPROCESSABLE_ENTITY)
expect(mockRes.json).toBeCalledWith(expectedResponse)
expect(
MockAdminFormService.updateFormCollaborators,
).not.toHaveBeenCalled()
})

it('should return 500 when a database error occurs', async () => {
// Arrange
const ERROR_MESSAGE = 'all your base are belong to us'
MockUserService.getPopulatedUserById.mockReturnValueOnce(
errAsync(new DatabaseError(ERROR_MESSAGE)),
)
const expectedResponse = { message: ERROR_MESSAGE }
const mockRes = expressHandler.mockResponse()

// Act
await AdminFormController._handleUpdateCollaborators(
MOCK_REQ,
mockRes,
jest.fn(),
)

// Assert
expect(mockRes.status).toBeCalledWith(StatusCodes.INTERNAL_SERVER_ERROR)
expect(mockRes.json).toBeCalledWith(expectedResponse)
expect(
MockAdminFormService.updateFormCollaborators,
).not.toHaveBeenCalled()
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import getFormModel, {
getEncryptedFormModel,
} from 'src/app/models/form.server.model'
import {
ApplicationError,
DatabaseConflictError,
DatabaseError,
DatabasePayloadSizeError,
Expand Down Expand Up @@ -72,6 +73,7 @@ import {
reorderFormField,
transferFormOwnership,
updateForm,
updateFormCollaborators,
updateFormField,
updateFormSettings,
} from '../admin-form.service'
Expand Down Expand Up @@ -1549,6 +1551,57 @@ describe('admin-form.service', () => {
})
})

describe('updateFormCollaborators', () => {
it('should return the list of collaborators when update is successful', async () => {
// Arrange
const newCollaborators = [
{
email: `[email protected]`,
write: false,
},
]
const mockForm = ({
title: 'some mock form',
updateFormCollaborators: jest
.fn()
.mockResolvedValue({ permissionList: newCollaborators }),
} as unknown) as IPopulatedForm

// Act
const actual = await updateFormCollaborators(mockForm, newCollaborators)

// Assert
expect(mockForm.updateFormCollaborators).toHaveBeenCalledWith(
newCollaborators,
)
expect(actual._unsafeUnwrap()).toEqual(newCollaborators)
})

it('should return an application error when updating the form model fails', async () => {
// Arrange
const newCollaborators = [
{
email: `[email protected]`,
write: false,
},
]
const mockForm = ({
title: 'some mock form',
updateFormCollaborators: jest
.fn()
.mockRejectedValue(new DatabaseError()),
} as unknown) as IPopulatedForm

// Act
const actual = await updateFormCollaborators(mockForm, newCollaborators)

// Assert
expect(mockForm.updateFormCollaborators).toHaveBeenCalledWith(
newCollaborators,
)
expect(actual._unsafeUnwrapErr()).toBeInstanceOf(ApplicationError)
})
})
describe('deleteFormField', () => {
let deleteSpy: jest.SpyInstance

Expand Down
Loading