diff --git a/app/api-v1/api-doc.js b/app/api-v1/api-doc.js index d2d9cb0..c3faed1 100644 --- a/app/api-v1/api-doc.js +++ b/app/api-v1/api-doc.js @@ -30,6 +30,57 @@ const apiDoc = { }, }, schemas: { + GetClient: { + description: 'Schema for retrieving a client', + type: 'object', + properties: { + id: { + description: 'Id of the client', + type: 'string', + format: 'uuid', + }, + firstName: { + description: 'First name of the client', + type: 'string', + }, + lastName: { + description: 'Last name of the client', + type: 'string', + }, + company: { + description: 'Company of the client', + type: 'string', + }, + role: { + description: 'Role of the client', + type: 'string', + }, + }, + required: ['id', 'firstName', 'lastName', 'company', 'role'], + }, + PostAndPutClient: { + description: 'Schema for creating/updating a client', + type: 'object', + properties: { + firstName: { + description: 'First name of the client', + type: 'string', + }, + lastName: { + description: 'Last name of the client', + type: 'string', + }, + company: { + description: 'Company of the client', + type: 'string', + }, + role: { + description: 'Role of the client', + type: 'string', + }, + }, + required: ['firstName', 'lastName', 'company', 'role'], + }, GetProject: { description: 'GET project', type: 'object', diff --git a/app/api-v1/routes/profiler/client.js b/app/api-v1/routes/profiler/client.js new file mode 100644 index 0000000..4b5247f --- /dev/null +++ b/app/api-v1/routes/profiler/client.js @@ -0,0 +1,62 @@ +const { + POST_CLIENT_RESPONSES, + validatePostClientResponse, +} = require('../../validators/client/postClientResponseValidator') +const { + GET_CLIENTS_RESPONSES, + validateGetClientsResponse, +} = require('../../validators/client/getClientsResponseValidator') + +module.exports = function (apiService) { + const doc = { + GET: async function (req, res) { + const { statusCode, result } = await apiService.getClients() + + const validationErrors = validateGetClientsResponse(statusCode, result) + + if (validationErrors) { + res.status(statusCode).json(validationErrors) + return + } else { + res.status(statusCode).json(result) + return + } + }, + POST: async function (req, res) { + const { statusCode, result } = await apiService.postClient(req.body) + + const validationErrors = validatePostClientResponse(statusCode, result) + + if (validationErrors) { + res.status(statusCode).json(validationErrors) + return + } else { + res.status(statusCode).json(result) + return + } + }, + } + + doc.GET.apiDoc = { + summary: 'Retrieve clients', + responses: GET_CLIENTS_RESPONSES, + tags: ['clients'], + } + + doc.POST.apiDoc = { + summary: 'Create client', + requestBody: { + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/PostAndPutClient', + }, + }, + }, + }, + responses: POST_CLIENT_RESPONSES, + tags: ['clients'], + } + + return doc +} diff --git a/app/api-v1/routes/profiler/client/{id}.js b/app/api-v1/routes/profiler/client/{id}.js new file mode 100644 index 0000000..27a2aac --- /dev/null +++ b/app/api-v1/routes/profiler/client/{id}.js @@ -0,0 +1,123 @@ +const { + validateGetClientByIdResponse, + GET_CLIENT_BY_ID_RESPONSES, +} = require('../../../validators/client/getClientByIdResponseValidator') +const { + PUT_CLIENT_RESPONSES, + validatePutClientResponse, +} = require('../../../validators/client/putClientResponseValidator') +const { + validateDeleteClientResponse, + DELETE_CLIENT_RESPONSES, +} = require('../../../validators/client/deleteClientResponseValidator') + +module.exports = function (apiService) { + const doc = { + GET: async function (req, res) { + const { id } = req.params + const { statusCode, result } = await apiService.getClientById(id) + + const validationErrors = validateGetClientByIdResponse(statusCode, result) + + if (validationErrors) { + res.status(statusCode).json(validationErrors) + return + } else { + res.status(statusCode).json(result) + return + } + }, + PUT: async function (req, res) { + const { id } = req.params + const { statusCode, result } = await apiService.putClient(id, req.body) + + const validationErrors = validatePutClientResponse(statusCode, result) + + if (validationErrors) { + res.status(statusCode).json(validationErrors) + return + } else { + res.status(statusCode).json(result) + return + } + }, + DELETE: async function (req, res) { + const { id } = req.params + const { statusCode, result } = await apiService.deleteClient(id) + + const validationErrors = validateDeleteClientResponse(statusCode, result) + + if (validationErrors) { + res.status(statusCode).json(validationErrors) + return + } else { + res.status(statusCode).send() + } + }, + } + + doc.GET.apiDoc = { + summary: 'Retrieve client by id', + parameters: [ + { + description: 'Id of the client', + in: 'path', + required: true, + name: 'id', + schema: { + type: 'string', + format: 'uuid', + }, + }, + ], + responses: GET_CLIENT_BY_ID_RESPONSES, + tags: ['clients'], + } + + doc.PUT.apiDoc = { + summary: 'Update client', + parameters: [ + { + description: 'Id of the client', + in: 'path', + required: true, + name: 'id', + schema: { + type: 'string', + format: 'uuid', + }, + }, + ], + requestBody: { + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/PostAndPutClient', + }, + }, + }, + }, + responses: PUT_CLIENT_RESPONSES, + tags: ['clients'], + } + + doc.DELETE.apiDoc = { + summary: 'Remove client', + parameters: [ + { + description: 'Id of the client', + in: 'path', + required: true, + name: 'id', + schema: { + type: 'string', + format: 'uuid', + }, + }, + ], + responses: DELETE_CLIENT_RESPONSES, + tags: ['clients'], + } + + return doc +} diff --git a/app/api-v1/services/apiService.js b/app/api-v1/services/apiService.js index 2b73837..43c6664 100644 --- a/app/api-v1/services/apiService.js +++ b/app/api-v1/services/apiService.js @@ -5,8 +5,59 @@ const { getProjectByIdDb, deleteProjectByIdDb, updateProjectDb, + addClientDb, + findClientsDb, + findClientByIdDb, + updateClientDb, + removeClientDb, } = require('../../db') +async function getClients() { + const result = await findClientsDb() + + return { statusCode: 200, result } +} + +async function getClientById(id) { + const result = await findClientByIdDb(id) + + if (result.length === 1) { + return { statusCode: 200, result: result[0] } + } else { + return { statusCode: 404, result: {} } + } +} + +async function postClient(reqBody) { + const result = await addClientDb(reqBody) + + return { statusCode: 201, result: result[0] } +} + +async function putClient(id, reqBody) { + const findResult = await findClientByIdDb(id) + + if (findResult.length === 1) { + const result = await updateClientDb(id, reqBody) + + return { statusCode: 200, result: result[0] } + } else { + return { statusCode: 404, result: {} } + } +} + +async function deleteClient(id) { + const findResult = await findClientByIdDb(id) + + if (findResult.length === 1) { + await removeClientDb(id) + + return { statusCode: 204 } + } else { + return { statusCode: 404, result: {} } + } +} + async function getProjects() { const result = await getProjectsDb() @@ -62,6 +113,11 @@ async function deleteProjectById(id) { } module.exports = { + getClients, + getClientById, + postClient, + putClient, + deleteClient, getProjects, postProject, getProjectById, diff --git a/app/api-v1/validators/client/deleteClientResponseValidator.js b/app/api-v1/validators/client/deleteClientResponseValidator.js new file mode 100644 index 0000000..3cda9a4 --- /dev/null +++ b/app/api-v1/validators/client/deleteClientResponseValidator.js @@ -0,0 +1,22 @@ +const { default: OpenAPIResponseValidator } = require('openapi-response-validator') + +const apiDocResponses = require('../../api-doc-responses') + +const DELETE_CLIENT_RESPONSES = { + 204: { + description: 'Deleted client', + }, + 404: apiDocResponses['404'], + default: apiDocResponses.default, +} + +const validateDeleteClientResponse = (statusCode, result) => { + const responseValidator = new OpenAPIResponseValidator({ responses: DELETE_CLIENT_RESPONSES }) + + return responseValidator.validateResponse(statusCode, result) +} + +module.exports = { + DELETE_CLIENT_RESPONSES, + validateDeleteClientResponse, +} diff --git a/app/api-v1/validators/client/getClientByIdResponseValidator.js b/app/api-v1/validators/client/getClientByIdResponseValidator.js new file mode 100644 index 0000000..b617c66 --- /dev/null +++ b/app/api-v1/validators/client/getClientByIdResponseValidator.js @@ -0,0 +1,28 @@ +const { default: OpenAPIResponseValidator } = require('openapi-response-validator') + +const apiDocResponses = require('../../api-doc-responses') +const apiDoc = require('../../api-doc') + +const GET_CLIENT_BY_ID_RESPONSES = { + 200: { + description: 'Retrieved client by id', + content: { + 'application/json': { + schema: apiDoc.components.schemas.GetClient, + }, + }, + }, + 404: apiDocResponses['404'], + default: apiDocResponses.default, +} + +const validateGetClientByIdResponse = (statusCode, result) => { + const responseValidator = new OpenAPIResponseValidator({ responses: GET_CLIENT_BY_ID_RESPONSES }) + + return responseValidator.validateResponse(statusCode, result) +} + +module.exports = { + GET_CLIENT_BY_ID_RESPONSES, + validateGetClientByIdResponse, +} diff --git a/app/api-v1/validators/client/getClientsResponseValidator.js b/app/api-v1/validators/client/getClientsResponseValidator.js new file mode 100644 index 0000000..8f272a5 --- /dev/null +++ b/app/api-v1/validators/client/getClientsResponseValidator.js @@ -0,0 +1,30 @@ +const { default: OpenAPIResponseValidator } = require('openapi-response-validator') + +const apiDocResponses = require('../../api-doc-responses') +const apiDoc = require('../../api-doc') + +const GET_CLIENTS_RESPONSES = { + 200: { + description: 'Retrieved clients', + content: { + 'application/json': { + schema: { + type: 'array', + items: apiDoc.components.schemas.GetClient, + }, + }, + }, + }, + default: apiDocResponses.default, +} + +const validateGetClientsResponse = (statusCode, result) => { + const responseValidator = new OpenAPIResponseValidator({ responses: GET_CLIENTS_RESPONSES }) + + return responseValidator.validateResponse(statusCode, result) +} + +module.exports = { + GET_CLIENTS_RESPONSES, + validateGetClientsResponse, +} diff --git a/app/api-v1/validators/client/postClientResponseValidator.js b/app/api-v1/validators/client/postClientResponseValidator.js new file mode 100644 index 0000000..d8463bb --- /dev/null +++ b/app/api-v1/validators/client/postClientResponseValidator.js @@ -0,0 +1,28 @@ +const { default: OpenAPIResponseValidator } = require('openapi-response-validator') + +const apiDocResponses = require('../../api-doc-responses') +const apiDoc = require('../../api-doc') + +const POST_CLIENT_RESPONSES = { + 201: { + description: 'Created client', + content: { + 'application/json': { + schema: apiDoc.components.schemas.GetClient, + }, + }, + }, + 400: apiDocResponses['400'], + default: apiDocResponses.default, +} + +const validatePostClientResponse = (statusCode, result) => { + const responseValidator = new OpenAPIResponseValidator({ responses: POST_CLIENT_RESPONSES }) + + return responseValidator.validateResponse(statusCode, result) +} + +module.exports = { + POST_CLIENT_RESPONSES, + validatePostClientResponse, +} diff --git a/app/api-v1/validators/client/putClientResponseValidator.js b/app/api-v1/validators/client/putClientResponseValidator.js new file mode 100644 index 0000000..3ffb8a4 --- /dev/null +++ b/app/api-v1/validators/client/putClientResponseValidator.js @@ -0,0 +1,28 @@ +const { default: OpenAPIResponseValidator } = require('openapi-response-validator') + +const apiDocResponses = require('../../api-doc-responses') +const apiDoc = require('../../api-doc') + +const PUT_CLIENT_RESPONSES = { + 200: { + description: 'Updated client', + content: { + 'application/json': { + schema: apiDoc.components.schemas.GetClient, + }, + }, + }, + 404: apiDocResponses['404'], + default: apiDocResponses.default, +} + +const validatePutClientResponse = (statusCode, result) => { + const responseValidator = new OpenAPIResponseValidator({ responses: PUT_CLIENT_RESPONSES }) + + return responseValidator.validateResponse(statusCode, result) +} + +module.exports = { + PUT_CLIENT_RESPONSES, + validatePutClientResponse, +} diff --git a/app/db.js b/app/db.js index 08ebf20..cc58156 100644 --- a/app/db.js +++ b/app/db.js @@ -17,6 +17,43 @@ const client = knex({ }, }) +async function findClientsDb() { + return client('clients').select(['id', 'first_name AS firstName', 'last_name AS lastName', 'company', 'role']) +} + +async function findClientByIdDb(id) { + return client('clients') + .select(['id', 'first_name AS firstName', 'last_name AS lastName', 'company', 'role']) + .where({ id }) +} + +async function addClientDb(reqBody) { + return client('clients') + .insert({ + first_name: reqBody.firstName, + last_name: reqBody.lastName, + company: reqBody.company, + role: reqBody.role, + }) + .returning(['id', 'first_name AS firstName', 'last_name AS lastName', 'company', 'role']) +} + +async function updateClientDb(id, reqBody) { + return client('clients') + .update({ + first_name: reqBody.firstName, + last_name: reqBody.lastName, + company: reqBody.company, + role: reqBody.role, + }) + .where({ id }) + .returning(['id', 'first_name AS firstName', 'last_name AS lastName', 'company', 'role']) +} + +async function removeClientDb(id) { + return client('clients').del().where({ id }) +} + async function postProjectDb(reqBody) { return client('projects') .insert({ @@ -96,6 +133,11 @@ async function updateProjectDb(id, reqBody) { module.exports = { client, + findClientsDb, + findClientByIdDb, + addClientDb, + updateClientDb, + removeClientDb, getProjectsDb, getProjectByNameDb, postProjectDb, diff --git a/helm/adcp-profiler-service/Chart.yaml b/helm/adcp-profiler-service/Chart.yaml index 2d29935..e241741 100644 --- a/helm/adcp-profiler-service/Chart.yaml +++ b/helm/adcp-profiler-service/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 name: adcp-profiler-service -appVersion: '0.0.2' +appVersion: '0.0.3' description: A Helm chart for adcp-profiler-service -version: '0.0.2' +version: '0.0.3' type: application maintainers: - name: digicatapult diff --git a/helm/adcp-profiler-service/values.yaml b/helm/adcp-profiler-service/values.yaml index 8942988..351ae63 100644 --- a/helm/adcp-profiler-service/values.yaml +++ b/helm/adcp-profiler-service/values.yaml @@ -3,5 +3,5 @@ config: image: repository: ghcr.io/digicatapult/adcp-profiler-service pullPolicy: IfNotPresent - tag: 'v0.0.2' + tag: 'v0.0.3' pullSecrets: ['ghcr-digicatapult'] diff --git a/package-lock.json b/package-lock.json index d27c1fc..32a79bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@digicatapult/adcp-profiler-service", - "version": "0.0.2", + "version": "0.0.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@digicatapult/adcp-profiler-service", - "version": "0.0.2", + "version": "0.0.3", "license": "Apache-2.0", "dependencies": { "body-parser": "^1.20.0", diff --git a/package.json b/package.json index 729415b..af694b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@digicatapult/adcp-profiler-service", - "version": "0.0.2", + "version": "0.0.3", "description": "Insert repo description", "main": "app/index.js", "scripts": { diff --git a/test/helper/appHelper.js b/test/helper/appHelper.js index 9b89f69..2225ff3 100644 --- a/test/helper/appHelper.js +++ b/test/helper/appHelper.js @@ -3,9 +3,7 @@ const { expect } = require('chai') const moment = require('moment') const createProject = (project) => { - const projectObj = { ...project } - - return projectObj + return { ...project } } const createDefaultProject = ({ clientId }) => { @@ -20,10 +18,39 @@ const createDefaultProject = ({ clientId }) => { }) } +const createClient = (client) => { + return { ...client } +} + +const createDefaultClient = () => { + return createClient({ + firstName: 'First name 1', + lastName: 'Last name 1', + company: 'Company 1', + role: 'Role 1', + }) +} + const assertUuidV4 = (id) => { expect(uuidValidate(id) && uuidVersion(id) === 4).to.be.true } +const assertClientParams = (actualResult, expectedResult) => { + assertUuidV4(actualResult.id) + expect(actualResult.firstName).to.equal(expectedResult.firstName) + expect(actualResult.lastName).to.equal(expectedResult.lastName) + expect(actualResult.company).to.equal(expectedResult.company) + expect(actualResult.role).to.equal(expectedResult.role) +} + +const assertGetClients = (actualResults, expectedResults) => { + expect(actualResults.length).to.equal(expectedResults.length) + + actualResults.forEach((actualResult, index) => { + assertClientParams(actualResult, expectedResults[index]) + }) +} + const assertPostProjectRequiredParams = (actualResult, expectedResult) => { assertUuidV4(actualResult.id) expect(actualResult.clientId).to.equal(expectedResult.clientId) @@ -40,15 +67,19 @@ const assertPostProjectParams = (actualResult, expectedResult) => { expect(actualResult.documentUrl).to.equal(expectedResult.documentUrl) } -const assertGetProjects = (actualResult, expectedResult) => { - expect(actualResult.length).to.equal(expectedResult.length) +const assertGetProjects = (actualResults, expectedResults) => { + expect(actualResults.length).to.equal(expectedResults.length) - actualResult.forEach((item, index) => { - assertPostProjectParams(item, expectedResult[index]) + actualResults.forEach((actualResult, index) => { + assertPostProjectParams(actualResult, expectedResults[index]) }) } module.exports = { + createClient, + createDefaultClient, + assertClientParams, + assertGetClients, createProject, createDefaultProject, assertUuidV4, diff --git a/test/helper/clientRouteHelper.js b/test/helper/clientRouteHelper.js new file mode 100644 index 0000000..bfdea33 --- /dev/null +++ b/test/helper/clientRouteHelper.js @@ -0,0 +1,84 @@ +/* eslint no-console: "off" */ +const request = require('supertest') + +const { API_MAJOR_VERSION } = require('../../app/env') + +async function getClientsRoute({ app }) { + return request(app) + .get(`/${API_MAJOR_VERSION}/profiler/client`) + .set('Accept', 'application/json') + .set('Content-Type', 'application/json') + .then((response) => { + return response + }) + .catch((err) => { + console.error(`getClientsErr ${err}`) + return err + }) +} + +async function getClientByIdRoute(id, { app }) { + return request(app) + .get(`/${API_MAJOR_VERSION}/profiler/client/${id}`) + .set('Accept', 'application/json') + .set('Content-Type', 'application/json') + .then((response) => { + return response + }) + .catch((err) => { + console.error(`getClientsErr ${err}`) + return err + }) +} + +async function postClientRoute(client, { app }) { + return request(app) + .post(`/${API_MAJOR_VERSION}/profiler/client`) + .set('Accept', 'application/json') + .set('Content-Type', 'application/json') + .send(client) + .then((response) => { + return response + }) + .catch((err) => { + console.error(`postClientErr ${err}`) + return err + }) +} + +async function putClientRoute(id, client, { app }) { + return request(app) + .put(`/${API_MAJOR_VERSION}/profiler/client/${id}`) + .set('Accept', 'application/json') + .set('Content-Type', 'application/json') + .send(client) + .then((response) => { + return response + }) + .catch((err) => { + console.error(`putClientErr ${err}`) + return err + }) +} + +async function deleteClientRoute(id, { app }) { + return request(app) + .delete(`/${API_MAJOR_VERSION}/profiler/client/${id}`) + .set('Accept', 'application/json') + .set('Content-Type', 'application/json') + .then((response) => { + return response + }) + .catch((err) => { + console.error(`getClientsErr ${err}`) + return err + }) +} + +module.exports = { + getClientsRoute, + postClientRoute, + getClientByIdRoute, + putClientRoute, + deleteClientRoute, +} diff --git a/test/helper/projectRouteHelper.js b/test/helper/projectRouteHelper.js new file mode 100644 index 0000000..a03d7bc --- /dev/null +++ b/test/helper/projectRouteHelper.js @@ -0,0 +1,84 @@ +/* eslint no-console: "off" */ +const request = require('supertest') + +const { API_MAJOR_VERSION } = require('../../app/env') + +async function getProjectsRoute({ app }) { + return request(app) + .get(`/${API_MAJOR_VERSION}/profiler/project`) + .set('Accept', 'application/json') + .set('Content-Type', 'application/json') + .then((response) => { + return response + }) + .catch((err) => { + console.error(`getProjectsErr ${err}`) + return err + }) +} + +async function getProjectByIdRoute(id, { app }) { + return request(app) + .get(`/${API_MAJOR_VERSION}/profiler/project/${id}`) + .set('Accept', 'application/json') + .set('Content-Type', 'application/json') + .then((response) => { + return response + }) + .catch((err) => { + console.error(`getProjectsErr ${err}`) + return err + }) +} + +async function postProjectRoute(project, { app }) { + return request(app) + .post(`/${API_MAJOR_VERSION}/profiler/project`) + .set('Accept', 'application/json') + .set('Content-Type', 'application/json') + .send(project) + .then((response) => { + return response + }) + .catch((err) => { + console.error(`postProjectErr ${err}`) + return err + }) +} + +async function putProjectRoute(id, project, { app }) { + return request(app) + .put(`/${API_MAJOR_VERSION}/profiler/project/${id}`) + .set('Accept', 'application/json') + .set('Content-Type', 'application/json') + .send(project) + .then((response) => { + return response + }) + .catch((err) => { + console.error(`putProjectErr ${err}`) + return err + }) +} + +async function deleteProjectByIdRoute(id, { app }) { + return request(app) + .delete(`/${API_MAJOR_VERSION}/profiler/project/${id}`) + .set('Accept', 'application/json') + .set('Content-Type', 'application/json') + .then((response) => { + return response + }) + .catch((err) => { + console.error(`getProjectsErr ${err}`) + return err + }) +} + +module.exports = { + getProjectsRoute, + postProjectRoute, + getProjectByIdRoute, + putProjectRoute, + deleteProjectByIdRoute, +} diff --git a/test/helper/routeHelper.js b/test/helper/routeHelper.js index eebc028..9ed3807 100644 --- a/test/helper/routeHelper.js +++ b/test/helper/routeHelper.js @@ -31,84 +31,7 @@ async function healthCheck({ app }) { }) } -async function getProjectsRoute({ app }) { - return request(app) - .get(`/${API_MAJOR_VERSION}/profiler/project`) - .set('Accept', 'application/json') - .set('Content-Type', 'application/json') - .then((response) => { - return response - }) - .catch((err) => { - console.error(`getProjectsErr ${err}`) - return err - }) -} - -async function getProjectByIdRoute(id, { app }) { - return request(app) - .get(`/${API_MAJOR_VERSION}/profiler/project/${id}`) - .set('Accept', 'application/json') - .set('Content-Type', 'application/json') - .then((response) => { - return response - }) - .catch((err) => { - console.error(`getProjectsErr ${err}`) - return err - }) -} - -async function postProjectRoute(project, { app }) { - return request(app) - .post(`/${API_MAJOR_VERSION}/profiler/project`) - .set('Accept', 'application/json') - .set('Content-Type', 'application/json') - .send(project) - .then((response) => { - return response - }) - .catch((err) => { - console.error(`postProjectErr ${err}`) - return err - }) -} - -async function putProjectRoute(id, project, { app }) { - return request(app) - .put(`/${API_MAJOR_VERSION}/profiler/project/${id}`) - .set('Accept', 'application/json') - .set('Content-Type', 'application/json') - .send(project) - .then((response) => { - return response - }) - .catch((err) => { - console.error(`putProjectErr ${err}`) - return err - }) -} - -async function deleteProjectByIdRoute(id, { app }) { - return request(app) - .delete(`/${API_MAJOR_VERSION}/profiler/project/${id}`) - .set('Accept', 'application/json') - .set('Content-Type', 'application/json') - .then((response) => { - return response - }) - .catch((err) => { - console.error(`getProjectsErr ${err}`) - return err - }) -} - module.exports = { apiDocs, healthCheck, - getProjectsRoute, - postProjectRoute, - getProjectByIdRoute, - putProjectRoute, - deleteProjectByIdRoute, } diff --git a/test/integration/clientRoutes.test.js b/test/integration/clientRoutes.test.js new file mode 100644 index 0000000..3ce40bc --- /dev/null +++ b/test/integration/clientRoutes.test.js @@ -0,0 +1,141 @@ +const { describe, test, before } = require('mocha') +const { expect } = require('chai') + +const { createDefaultClient, assertClientParams, assertGetClients, createClient } = require('../helper/appHelper') +const { createHttpServer } = require('../../app/server') +const { + postClientRoute, + getClientsRoute, + getClientByIdRoute, + putClientRoute, + deleteClientRoute, +} = require('../helper/clientRouteHelper') +const { cleanupAll, cleanup } = require('../helper/seeds/project') + +describe('Client routes', function () { + let app + let defaultClient + let invalidClientId + + before(async function () { + await cleanupAll() + + app = await createHttpServer() + + defaultClient = createDefaultClient() + invalidClientId = '00000000-0000-0000-0000-000000000000' + }) + + beforeEach(async function () { + await cleanup('clients') + }) + + test('POST Client', async function () { + const expectedResult = defaultClient + + const actualResponse = await postClientRoute(defaultClient, app) + + expect(actualResponse.status).to.equal(201) + assertClientParams(actualResponse.body, expectedResult) + }) + + test('POST client missing fields', async function () { + const actualResponse = await postClientRoute({}, app) + + expect(actualResponse.status).to.equal(400) + expect(actualResponse.body).to.deep.equal({}) + }) + + test('GET clients', async function () { + const expectedResult = [defaultClient] + + await postClientRoute(defaultClient, app) + const actualResponse = await getClientsRoute(app) + + expect(actualResponse.status).to.equal(200) + assertGetClients(actualResponse.body, expectedResult) + }) + + test('GET client by id', async function () { + const expectedResult = defaultClient + + const response = await postClientRoute(defaultClient, app) + const actualResponse = await getClientByIdRoute(response.body.id, app) + + expect(actualResponse.status).to.equal(200) + assertClientParams(actualResponse.body, expectedResult) + }) + + test('GET client by id for non-existing client', async function () { + const actualResponse = await getClientByIdRoute(invalidClientId, app) + + expect(actualResponse.status).to.equal(404) + expect(actualResponse.body).to.deep.equal({}) + }) + + test('GET client by id with invalid path id parameter', async function () { + const actualResponse = await getClientByIdRoute(1, app) + + expect(actualResponse.status).to.equal(400) + expect(actualResponse.body).to.deep.equal({}) + }) + + test('PUT client', async function () { + const client = createClient({ + firstName: 'First name 2', + lastName: 'Last name 2', + company: 'Company 2', + role: 'Role 2', + }) + const expectedResult = client + + const response = await postClientRoute(defaultClient, app) + const actualResponse = await putClientRoute(response.body.id, client, app) + + expect(actualResponse.status).to.equal(200) + assertClientParams(actualResponse.body, expectedResult) + }) + + test('PUT client with missing fields', async function () { + const actualResponse = await putClientRoute(invalidClientId, {}, app) + + expect(actualResponse.status).to.equal(400) + expect(actualResponse.body).to.deep.equal({}) + }) + + test('PUT client for non-existing client', async function () { + const actualResponse = await putClientRoute(invalidClientId, defaultClient, app) + + expect(actualResponse.status).to.equal(404) + expect(actualResponse.body).to.deep.equal({}) + }) + + test('PUT client with invalid id path parameter', async function () { + const actualResponse = await putClientRoute('123', defaultClient, app) + + expect(actualResponse.status).to.equal(400) + expect(actualResponse.body).to.deep.equal({}) + }) + + test('DELETE client', async function () { + const response = await postClientRoute(defaultClient, app) + const actualResponse = await deleteClientRoute(response.body.id, app) + + expect(actualResponse.status).to.equal(204) + expect(actualResponse.body).to.deep.equal({}) + }) + + test('DELETE client for non-existing client', async function () { + const actualResponse = await deleteClientRoute(invalidClientId, app) + + expect(actualResponse.status).to.equal(404) + expect(actualResponse.body).to.deep.equal({}) + }) + + test('DELETE client with invalid id path parameter', async function () { + const actualResponse = await deleteClientRoute('123', app) + + expect(actualResponse.status).to.equal(400) + expect(actualResponse.body).to.deep.equal({}) + }) +}) diff --git a/test/integration/projectRoutes.test.js b/test/integration/projectRoutes.test.js new file mode 100644 index 0000000..4ccf572 --- /dev/null +++ b/test/integration/projectRoutes.test.js @@ -0,0 +1,200 @@ +const { describe, test, before } = require('mocha') +const { expect } = require('chai') + +const { + createDefaultProject, + createProject, + assertPostProjectParams, + assertPostProjectRequiredParams, + assertGetProjects, +} = require('../helper/appHelper') +const { createHttpServer } = require('../../app/server') +const { + getProjectByIdRoute, + getProjectsRoute, + postProjectRoute, + putProjectRoute, + deleteProjectByIdRoute, +} = require('../helper/projectRouteHelper') +const { seed, cleanup } = require('../helper/seeds/project') +const moment = require('moment') + +describe('Project routes', function () { + let app + let clientId + let invalidId + let defaultProject + + before(async function () { + await seed() + + app = await createHttpServer() + clientId = 'c7b9e848-e2bb-456d-8eaa-129c1cb3580c' + invalidId = '00000000-0000-0000-0000-000000000000' + defaultProject = createDefaultProject({ clientId }) + }) + + beforeEach(async function () { + await cleanup('projects') + }) + + test('POST Project with all fields', async function () { + const expectedResult = defaultProject + + const response = await postProjectRoute(defaultProject, app) + + expect(response.status).to.equal(201) + assertPostProjectParams(response.body, expectedResult) + }) + + test('POST Project with only required request body parameters', async function () { + const project = createProject({ clientId, name: 'Project 1', description: 'Project 1 description' }) + const expectedResult = { + ...project, + startDate: null, + endDate: null, + budget: null, + documentUrl: null, + } + + const response = await postProjectRoute(project, app) + + expect(response.status).to.equal(201) + assertPostProjectRequiredParams(response.body, expectedResult) + }) + + test('POST Project missing client id', async function () { + const project = createProject({ name: 'Project 1', description: 'Project 1 description' }) + + const response = await postProjectRoute(project, app) + + expect(response.status).to.equal(400) + expect(response.body).deep.equal({}) + }) + + test('POST invalid project', async function () { + const response = await postProjectRoute({}, app) + + expect(response.status).to.equal(400) + expect(response.body).deep.equal({}) + }) + + test('POST duplicate project', async function () { + await postProjectRoute(defaultProject, app) + const response = await postProjectRoute(defaultProject, app) + + expect(response.status).to.equal(409) + expect(response.body).deep.equal({}) + }) + + test('GET projects', async function () { + const expectedResult = [defaultProject] + + await postProjectRoute(defaultProject, app) + const response = await getProjectsRoute(app) + + expect(response.status).to.equal(200) + assertGetProjects(response.body, expectedResult) + }) + + test('GET project by id', async function () { + const expectedResult = defaultProject + + const projectResponse = await postProjectRoute(defaultProject, app) + const response = await getProjectByIdRoute(projectResponse.body.id, app) + + expect(response.status).to.equal(200) + assertPostProjectRequiredParams(response.body, expectedResult) + }) + + test('GET project by id with invalid path id parameter', async function () { + const response = await getProjectByIdRoute(invalidId, app) + + expect(response.status).to.equal(404) + }) + + test('PUT project with only required fields', async function () { + const project = createProject({ clientId, name: 'Project 1', description: 'Project 1 description' }) + const projectUpdate = createProject({ clientId, name: 'Project 2', description: 'Project 2 description' }) + const expectedResult = projectUpdate + + const projectResponse = await postProjectRoute(project, app) + const response = await putProjectRoute(projectResponse.body.id, projectUpdate, app) + + expect(response.status).to.equal(200) + assertPostProjectRequiredParams(response.body, expectedResult) + }) + + test('PUT project with existing name', async function () { + const project = createProject({ clientId, name: 'Project 1', description: 'Project 1 description' }) + const projectUpdate = createProject({ clientId, name: 'Project 1', description: 'Project 2 description' }) + + const projectResponse = await postProjectRoute(project, app) + const response = await putProjectRoute(projectResponse.body.id, projectUpdate, app) + + expect(response.status).to.equal(409) + expect(response.body).to.deep.equal({}) + }) + + test('PUT project with invalid id path parameter', async function () { + const project = createProject({ + clientId, + name: 'Project 2', + description: 'Project 2 description', + startDate: moment().startOf('day').toISOString(), + endDate: moment().endOf('day').toISOString(), + budget: 200000.0, + documentUrl: 'http://digitalcatapult.org.uk/document/url', + }) + + const response = await putProjectRoute(invalidId, project, app) + + expect(response.status).to.equal(404) + }) + + test('PUT project with missing required client id path parameter', async function () { + const project = createProject({ + name: 'Project 2', + description: 'Project 2 description', + startDate: moment().startOf('day').toISOString(), + endDate: moment().endOf('day').toISOString(), + budget: 200000.0, + documentUrl: 'http://digitalcatapult.org.uk/document/url', + }) + + const response = await putProjectRoute(invalidId, project, app) + + expect(response.status).to.equal(400) + expect(response.body).deep.equal({}) + }) + + test('PUT project with all request body parameters', async function () { + const project = createProject({ + name: 'Project 2', + description: 'Project 2 description', + startDate: moment().startOf('day').toISOString(), + endDate: moment().endOf('day').toISOString(), + budget: 200000.0, + documentUrl: 'http://digitalcatapult.org.uk/document/url', + }) + + const response = await putProjectRoute(invalidId, project, app) + + expect(response.status).to.equal(400) + expect(response.body).deep.equal({}) + }) + + test('DELETE project', async function () { + const projectResponse = await postProjectRoute(defaultProject, app) + const response = await deleteProjectByIdRoute(projectResponse.body.id, app) + + expect(response.status).to.equal(204) + expect(response.body).deep.equal({}) + }) + + test('DELETE project with invalid id path parameter', async function () { + const response = await deleteProjectByIdRoute(invalidId, app) + + expect(response.status).to.equal(404) + }) +}) diff --git a/test/integration/routes.test.js b/test/integration/routes.test.js deleted file mode 100644 index 916f847..0000000 --- a/test/integration/routes.test.js +++ /dev/null @@ -1,202 +0,0 @@ -const { describe, test, before } = require('mocha') -const { expect } = require('chai') - -const { - createDefaultProject, - createProject, - assertPostProjectParams, - assertPostProjectRequiredParams, - assertGetProjects, -} = require('../helper/appHelper') -const { createHttpServer } = require('../../app/server') -const { - getProjectByIdRoute, - getProjectsRoute, - postProjectRoute, - putProjectRoute, - deleteProjectByIdRoute, -} = require('../helper/routeHelper') -const { seed, cleanup } = require('../helper/seeds/project') -const moment = require('moment') - -describe('routes', function () { - describe('Project routes', function () { - let app - let clientId - let invalidId - let defaultProject - - before(async function () { - await seed() - - app = await createHttpServer() - clientId = 'c7b9e848-e2bb-456d-8eaa-129c1cb3580c' - invalidId = '00000000-0000-0000-0000-000000000000' - defaultProject = createDefaultProject({ clientId }) - }) - - beforeEach(async function () { - await cleanup('projects') - }) - - test('POST Project with all fields', async function () { - const expectedResult = defaultProject - - const response = await postProjectRoute(defaultProject, app) - - expect(response.status).to.equal(201) - assertPostProjectParams(response.body, expectedResult) - }) - - test('POST Project with only required request body parameters', async function () { - const project = createProject({ clientId, name: 'Project 1', description: 'Project 1 description' }) - const expectedResult = { - ...project, - startDate: null, - endDate: null, - budget: null, - documentUrl: null, - } - - const response = await postProjectRoute(project, app) - - expect(response.status).to.equal(201) - assertPostProjectRequiredParams(response.body, expectedResult) - }) - - test('POST Project missing client id', async function () { - const project = createProject({ name: 'Project 1', description: 'Project 1 description' }) - - const response = await postProjectRoute(project, app) - - expect(response.status).to.equal(400) - expect(response.body).deep.equal({}) - }) - - test('POST invalid project', async function () { - const response = await postProjectRoute({}, app) - - expect(response.status).to.equal(400) - expect(response.body).deep.equal({}) - }) - - test('POST duplicate project', async function () { - await postProjectRoute(defaultProject, app) - const response = await postProjectRoute(defaultProject, app) - - expect(response.status).to.equal(409) - expect(response.body).deep.equal({}) - }) - - test('GET projects', async function () { - const expectedResult = [defaultProject] - - await postProjectRoute(defaultProject, app) - const response = await getProjectsRoute(app) - - expect(response.status).to.equal(200) - assertGetProjects(response.body, expectedResult) - }) - - test('GET project by id', async function () { - const expectedResult = defaultProject - - const projectResponse = await postProjectRoute(defaultProject, app) - const response = await getProjectByIdRoute(projectResponse.body.id, app) - - expect(response.status).to.equal(200) - assertPostProjectRequiredParams(response.body, expectedResult) - }) - - test('GET project by id with invalid path id parameter', async function () { - const response = await getProjectByIdRoute(invalidId, app) - - expect(response.status).to.equal(404) - }) - - test('PUT project with only required fields', async function () { - const project = createProject({ clientId, name: 'Project 1', description: 'Project 1 description' }) - const projectUpdate = createProject({ clientId, name: 'Project 2', description: 'Project 2 description' }) - const expectedResult = projectUpdate - - const projectResponse = await postProjectRoute(project, app) - const response = await putProjectRoute(projectResponse.body.id, projectUpdate, app) - - expect(response.status).to.equal(200) - assertPostProjectRequiredParams(response.body, expectedResult) - }) - - test('PUT project with existing name', async function () { - const project = createProject({ clientId, name: 'Project 1', description: 'Project 1 description' }) - const projectUpdate = createProject({ clientId, name: 'Project 1', description: 'Project 2 description' }) - - const projectResponse = await postProjectRoute(project, app) - const response = await putProjectRoute(projectResponse.body.id, projectUpdate, app) - - expect(response.status).to.equal(409) - expect(response.body).to.deep.equal({}) - }) - - test('PUT project with invalid id path parameter', async function () { - const project = createProject({ - clientId, - name: 'Project 2', - description: 'Project 2 description', - startDate: moment().startOf('day').toISOString(), - endDate: moment().endOf('day').toISOString(), - budget: 200000.0, - documentUrl: 'http://digitalcatapult.org.uk/document/url', - }) - - const response = await putProjectRoute(invalidId, project, app) - - expect(response.status).to.equal(404) - }) - - test('PUT project with missing required client id path parameter', async function () { - const project = createProject({ - name: 'Project 2', - description: 'Project 2 description', - startDate: moment().startOf('day').toISOString(), - endDate: moment().endOf('day').toISOString(), - budget: 200000.0, - documentUrl: 'http://digitalcatapult.org.uk/document/url', - }) - - const response = await putProjectRoute(invalidId, project, app) - - expect(response.status).to.equal(400) - expect(response.body).deep.equal({}) - }) - - test('PUT project with all request body parameters', async function () { - const project = createProject({ - name: 'Project 2', - description: 'Project 2 description', - startDate: moment().startOf('day').toISOString(), - endDate: moment().endOf('day').toISOString(), - budget: 200000.0, - documentUrl: 'http://digitalcatapult.org.uk/document/url', - }) - - const response = await putProjectRoute(invalidId, project, app) - - expect(response.status).to.equal(400) - expect(response.body).deep.equal({}) - }) - - test('DELETE project', async function () { - const projectResponse = await postProjectRoute(defaultProject, app) - const response = await deleteProjectByIdRoute(projectResponse.body.id, app) - - expect(response.status).to.equal(204) - expect(response.body).deep.equal({}) - }) - - test('DELETE project with invalid id path parameter', async function () { - const response = await deleteProjectByIdRoute(invalidId, app) - - expect(response.status).to.equal(404) - }) - }) -})