-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SIMSBIOHUB-117: Delete Artifact (#203)
Added delete functionality to artifacts
- Loading branch information
1 parent
84811b3
commit 7190ce0
Showing
17 changed files
with
648 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import chai, { expect } from 'chai'; | ||
import { describe } from 'mocha'; | ||
import OpenAPIRequestValidator, { OpenAPIRequestValidatorArgs } from 'openapi-request-validator'; | ||
import OpenAPIResponseValidator, { OpenAPIResponseValidatorArgs } from 'openapi-response-validator'; | ||
import sinon from 'sinon'; | ||
import sinonChai from 'sinon-chai'; | ||
import * as db from '../../database/db'; | ||
import { ArtifactService } from '../../services/artifact-service'; | ||
import * as keycloakUtils from '../../utils/keycloak-utils'; | ||
import { getMockDBConnection, getRequestHandlerMocks } from '../../__mocks__/db'; | ||
import { deleteArtifact, POST } from './delete'; | ||
|
||
chai.use(sinonChai); | ||
|
||
describe('delete artifact', () => { | ||
describe('openApiSchema', () => { | ||
describe('request validation', () => { | ||
const requestValidator = new OpenAPIRequestValidator(POST.apiDoc as unknown as OpenAPIRequestValidatorArgs); | ||
it('should have property `artifactUUIDs`', async () => { | ||
const request = { | ||
headers: { 'content-type': 'application/json' }, | ||
body: {} | ||
}; | ||
const response = requestValidator.validateRequest(request); | ||
expect(response.status).to.equal(400); | ||
expect(response.errors.length).to.equal(1); | ||
expect(response.errors[0].message).to.equal("must have required property 'artifactUUIDs'"); | ||
}); | ||
|
||
it('should be an array error', async () => { | ||
const request = { | ||
headers: { 'content-type': 'application/json' }, | ||
body: { | ||
artifactUUIDs: '' | ||
} | ||
}; | ||
const response = requestValidator.validateRequest(request); | ||
expect(response.status).to.equal(400); | ||
expect(response.errors.length).to.equal(1); | ||
expect(response.errors[0].message).to.equal('must be array'); | ||
}); | ||
|
||
it('should match format "uuid" error', async () => { | ||
const request = { | ||
headers: { 'content-type': 'application/json' }, | ||
body: { | ||
artifactUUIDs: ['uuid'] | ||
} | ||
}; | ||
const response = requestValidator.validateRequest(request); | ||
expect(response.status).to.equal(400); | ||
expect(response.errors.length).to.equal(1); | ||
expect(response.errors[0].message).to.equal('must match format "uuid"'); | ||
}); | ||
}); | ||
|
||
describe('response validation', () => { | ||
const responseValidator = new OpenAPIResponseValidator(POST.apiDoc as unknown as OpenAPIResponseValidatorArgs); | ||
describe('should throw an error', () => { | ||
it('has null value', async () => { | ||
const apiResponse = null; | ||
const response = responseValidator.validateResponse(200, apiResponse); | ||
|
||
expect(response.message).to.equal('The response was not valid.'); | ||
expect(response.errors[0].message).to.equal('must be boolean'); | ||
}); | ||
|
||
it('returning wrong response', async () => { | ||
const apiResponse = { wrong_property: 1 }; | ||
const response = responseValidator.validateResponse(200, apiResponse); | ||
|
||
expect(response.message).to.equal('The response was not valid.'); | ||
expect(response.errors[0].message).to.equal('must be boolean'); | ||
}); | ||
}); | ||
|
||
describe('responders properly', () => { | ||
it('has valid values', async () => { | ||
const apiResponse = true; | ||
const response = responseValidator.validateResponse(200, apiResponse); | ||
|
||
expect(response).to.equal(undefined); | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('deleteArtifact', () => { | ||
afterEach(() => { | ||
sinon.restore(); | ||
}); | ||
|
||
it('catches and throws error', async () => { | ||
const dbConnectionObj = getMockDBConnection({ rollback: sinon.stub(), release: sinon.stub() }); | ||
sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); | ||
sinon.stub(keycloakUtils, 'getKeycloakSource').resolves(false); | ||
sinon.stub(ArtifactService.prototype, 'deleteArtifacts').throws('There was an issue deleting an artifact.'); | ||
const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); | ||
mockReq.body = { | ||
artifactUUIDs: ['ff84ecfc-046e-4cac-af59-a597047ce63d'] | ||
}; | ||
const requestHandler = deleteArtifact(); | ||
|
||
try { | ||
await requestHandler(mockReq, mockRes, mockNext); | ||
expect.fail(); | ||
} catch (error: any) { | ||
expect(error.name).to.be.eql('There was an issue deleting an artifact.'); | ||
expect(dbConnectionObj.release).to.have.been.calledOnce; | ||
expect(dbConnectionObj.rollback).to.have.been.calledOnce; | ||
} | ||
}); | ||
|
||
it('responds with proper data', async () => { | ||
const dbConnectionObj = getMockDBConnection({ rollback: sinon.stub(), release: sinon.stub() }); | ||
sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); | ||
sinon.stub(keycloakUtils, 'getKeycloakSource').resolves(false); | ||
sinon.stub(ArtifactService.prototype, 'deleteArtifacts').resolves(); | ||
const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); | ||
mockReq.body = { | ||
artifactUUIDs: ['ff84ecfc-046e-4cac-af59-a597047ce63d'] | ||
}; | ||
const requestHandler = deleteArtifact(); | ||
|
||
await requestHandler(mockReq, mockRes, mockNext); | ||
expect(dbConnectionObj.release).to.have.been.calledOnce; | ||
expect(dbConnectionObj.rollback).to.have.not.been.calledOnce; | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import { RequestHandler } from 'express'; | ||
import { Operation } from 'express-openapi'; | ||
import { SOURCE_SYSTEM } from '../../constants/database'; | ||
import { SYSTEM_ROLE } from '../../constants/roles'; | ||
import { getDBConnection, getServiceAccountDBConnection } from '../../database/db'; | ||
import { authorizeRequestHandler } from '../../request-handlers/security/authorization'; | ||
import { ArtifactService } from '../../services/artifact-service'; | ||
import { getKeycloakSource } from '../../utils/keycloak-utils'; | ||
import { getLogger } from '../../utils/logger'; | ||
|
||
const defaultLog = getLogger('paths/artifact/delete'); | ||
|
||
export const POST: Operation = [ | ||
authorizeRequestHandler(() => { | ||
return { | ||
or: [ | ||
{ | ||
validSystemRoles: [SYSTEM_ROLE.SYSTEM_ADMIN, SYSTEM_ROLE.DATA_ADMINISTRATOR], | ||
discriminator: 'SystemRole' | ||
}, | ||
{ | ||
validServiceClientIDs: [SOURCE_SYSTEM['SIMS-SVC-4464']], | ||
discriminator: 'ServiceClient' | ||
} | ||
] | ||
}; | ||
}), | ||
deleteArtifact() | ||
]; | ||
|
||
POST.apiDoc = { | ||
description: 'Deletes artifacts for a given list of UUIDs.', | ||
tags: ['artifact'], | ||
security: [ | ||
{ | ||
Bearer: [] | ||
} | ||
], | ||
requestBody: { | ||
content: { | ||
'application/json': { | ||
schema: { | ||
title: 'Artifacts to delete', | ||
type: 'object', | ||
required: ['artifactUUIDs'], | ||
properties: { | ||
artifactUUIDs: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
format: 'uuid' | ||
}, | ||
minItems: 1 | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
responses: { | ||
200: { | ||
description: '', | ||
content: { | ||
'application/json': { | ||
schema: { | ||
type: 'boolean', | ||
description: 'A boolean indicating if the delete action was successful or not.' | ||
} | ||
} | ||
} | ||
}, | ||
400: { | ||
$ref: '#/components/responses/400' | ||
}, | ||
401: { | ||
$ref: '#/components/responses/401' | ||
}, | ||
403: { | ||
$ref: '#/components/responses/403' | ||
}, | ||
500: { | ||
$ref: '#/components/responses/500' | ||
}, | ||
default: { | ||
$ref: '#/components/responses/default' | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Deletes artifacts for a given array of UUIDs. | ||
* This will always respond with a JSON object {success: boolean} indicating | ||
* if the artifacts have been successfully removed or not | ||
* | ||
* @returns {RequestHandler} | ||
*/ | ||
export function deleteArtifact(): RequestHandler { | ||
return async (req, res) => { | ||
defaultLog.debug({ label: 'deleteArtifact', message: 'request body', req_body: req.query }); | ||
|
||
const sourceSystem = getKeycloakSource(req['keycloak_token']); | ||
const connection = sourceSystem | ||
? getServiceAccountDBConnection(sourceSystem) | ||
: getDBConnection(req['keycloak_token']); | ||
|
||
try { | ||
await connection.open(); | ||
const service = new ArtifactService(connection); | ||
|
||
await service.deleteArtifacts(req.body.artifactUUIDs); | ||
res.status(200).json(true); | ||
await connection.commit(); | ||
} catch (error: any) { | ||
defaultLog.error({ label: 'deleteArtifact', message: 'error', error }); | ||
await connection.rollback(); | ||
throw error; | ||
} finally { | ||
connection.release(); | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.