-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Filter measurements search by focal and observed species (#1338)
* wip: styling on configure table dialog * wip: configure columns styling * configure obs dialog styling * add applicable label to measurements search * write unit tests * linter * no data overlay in configure columns dialog * rename variable for clarity * add eslint ignore & update prop names * fix dependency array warning * feat: removed log file from git cache --------- Co-authored-by: Mac Deluca <[email protected]>
- Loading branch information
1 parent
b856afd
commit bb198fa
Showing
24 changed files
with
759 additions
and
241 deletions.
There are no files selected for viewing
86 changes: 86 additions & 0 deletions
86
api/src/paths/project/{projectId}/survey/{surveyId}/observations/taxon/index.test.ts
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,86 @@ | ||
import chai, { expect } from 'chai'; | ||
import { describe } from 'mocha'; | ||
import sinon from 'sinon'; | ||
import sinonChai from 'sinon-chai'; | ||
import { getSurveyObservedSpecies } from '.'; | ||
import * as db from '../../../../../../../database/db'; | ||
import { HTTPError } from '../../../../../../../errors/http-error'; | ||
import { ObservationService } from '../../../../../../../services/observation-service'; | ||
import { PlatformService } from '../../../../../../../services/platform-service'; | ||
import { getMockDBConnection, getRequestHandlerMocks } from '../../../../../../../__mocks__/db'; | ||
|
||
chai.use(sinonChai); | ||
|
||
describe('getSurveyObservedSpecies', () => { | ||
afterEach(() => { | ||
sinon.restore(); | ||
}); | ||
|
||
it('gets species observed in a survey', async () => { | ||
const dbConnectionObj = getMockDBConnection(); | ||
|
||
sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); | ||
|
||
const mockSurveyId = 2; | ||
const mockProjectId = 1; | ||
const mockTsns = [1, 2, 3]; | ||
const mockSpecies = mockTsns.map((tsn) => ({ itis_tsn: tsn })); | ||
const mockItisResponse = [ | ||
{ tsn: '1', commonNames: ['common name 1'], scientificName: 'scientific name 1' }, | ||
{ tsn: '2', commonNames: ['common name 2'], scientificName: 'scientific name 2' }, | ||
{ tsn: '3', commonNames: ['common name 3'], scientificName: 'scientific name 3' } | ||
]; | ||
const mockFormattedItisResponse = mockItisResponse.map((species) => ({ ...species, tsn: Number(species.tsn) })); | ||
|
||
const getObservedSpeciesForSurveyStub = sinon | ||
.stub(ObservationService.prototype, 'getObservedSpeciesForSurvey') | ||
.resolves(mockSpecies); | ||
|
||
const getTaxonomyByTsnsStub = sinon.stub(PlatformService.prototype, 'getTaxonomyByTsns').resolves(mockItisResponse); | ||
|
||
const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); | ||
|
||
mockReq.params = { | ||
projectId: String(mockProjectId), | ||
surveyId: String(mockSurveyId) | ||
}; | ||
|
||
const requestHandler = getSurveyObservedSpecies(); | ||
|
||
await requestHandler(mockReq, mockRes, mockNext); | ||
|
||
expect(getObservedSpeciesForSurveyStub).to.have.been.calledOnceWith(mockSurveyId); | ||
expect(getTaxonomyByTsnsStub).to.have.been.calledOnceWith(mockTsns); | ||
expect(mockRes.statusValue).to.equal(200); | ||
expect(mockRes.jsonValue).to.eql(mockFormattedItisResponse); | ||
}); | ||
|
||
it('catches and re-throws error', async () => { | ||
const dbConnectionObj = getMockDBConnection({ release: sinon.stub() }); | ||
|
||
sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); | ||
|
||
const mockSurveyId = 2; | ||
const mockProjectId = 1; | ||
|
||
sinon.stub(ObservationService.prototype, 'getObservedSpeciesForSurvey').rejects(new Error('a test error')); | ||
|
||
const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); | ||
|
||
mockReq.params = { | ||
projectId: String(mockProjectId), | ||
surveyId: String(mockSurveyId) | ||
}; | ||
|
||
try { | ||
const requestHandler = getSurveyObservedSpecies(); | ||
|
||
await requestHandler(mockReq, mockRes, mockNext); | ||
|
||
expect.fail(); | ||
} catch (actualError) { | ||
expect(dbConnectionObj.release).to.have.been.called; | ||
expect((actualError as HTTPError).message).to.equal('a test error'); | ||
} | ||
}); | ||
}); |
140 changes: 140 additions & 0 deletions
140
api/src/paths/project/{projectId}/survey/{surveyId}/observations/taxon/index.ts
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,140 @@ | ||
import { RequestHandler } from 'express'; | ||
import { Operation } from 'express-openapi'; | ||
import { PROJECT_PERMISSION, SYSTEM_ROLE } from '../../../../../../../constants/roles'; | ||
import { getDBConnection } from '../../../../../../../database/db'; | ||
import { authorizeRequestHandler } from '../../../../../../../request-handlers/security/authorization'; | ||
import { ObservationService } from '../../../../../../../services/observation-service'; | ||
import { PlatformService } from '../../../../../../../services/platform-service'; | ||
import { getLogger } from '../../../../../../../utils/logger'; | ||
|
||
const defaultLog = getLogger('/api/project/{projectId}/survey/{surveyId}/observation/taxon'); | ||
|
||
export const GET: Operation = [ | ||
authorizeRequestHandler((req) => { | ||
return { | ||
or: [ | ||
{ | ||
validProjectPermissions: [ | ||
PROJECT_PERMISSION.COORDINATOR, | ||
PROJECT_PERMISSION.COLLABORATOR, | ||
PROJECT_PERMISSION.OBSERVER | ||
], | ||
surveyId: Number(req.params.surveyId), | ||
discriminator: 'ProjectPermission' | ||
}, | ||
{ | ||
validSystemRoles: [SYSTEM_ROLE.DATA_ADMINISTRATOR, SYSTEM_ROLE.SYSTEM_ADMIN], | ||
discriminator: 'SystemRole' | ||
} | ||
] | ||
}; | ||
}), | ||
getSurveyObservedSpecies() | ||
]; | ||
|
||
GET.apiDoc = { | ||
description: 'Get observed species for a survey', | ||
tags: ['observation'], | ||
security: [ | ||
{ | ||
Bearer: [] | ||
} | ||
], | ||
parameters: [ | ||
{ | ||
in: 'path', | ||
name: 'projectId', | ||
schema: { | ||
type: 'integer', | ||
minimum: 1 | ||
}, | ||
required: true | ||
}, | ||
{ | ||
in: 'path', | ||
name: 'surveyId', | ||
schema: { | ||
type: 'integer', | ||
minimum: 1 | ||
}, | ||
required: true | ||
} | ||
], | ||
responses: { | ||
200: { | ||
description: 'Survey observed species get response.', | ||
content: { | ||
'application/json': { | ||
schema: { | ||
type: 'array', | ||
description: 'Array of objects describing observed species in the survey', | ||
items: { | ||
type: 'object', | ||
additionalProperties: false, | ||
properties: { | ||
tsn: { type: 'number', description: 'The TSN of the observed species' }, | ||
commonNames: { | ||
type: 'array', | ||
items: { type: 'string' }, | ||
description: 'Common names of the observed species' | ||
}, | ||
scientificName: { type: 'string', description: 'Scientific name of the observed species' } | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
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' | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Fetch species that were observed in the survey | ||
* | ||
* @export | ||
* @return {*} {RequestHandler} | ||
*/ | ||
export function getSurveyObservedSpecies(): RequestHandler { | ||
return async (req, res) => { | ||
const surveyId = Number(req.params.surveyId); | ||
defaultLog.debug({ label: 'getSurveyObservedSpecies', surveyId }); | ||
|
||
const connection = getDBConnection(req.keycloak_token); | ||
|
||
try { | ||
await connection.open(); | ||
|
||
const observationService = new ObservationService(connection); | ||
const platformService = new PlatformService(connection); | ||
|
||
const observedSpecies = await observationService.getObservedSpeciesForSurvey(surveyId); | ||
|
||
const species = await platformService.getTaxonomyByTsns(observedSpecies.flatMap((species) => species.itis_tsn)); | ||
|
||
const formattedResponse = species.map((taxon) => ({ ...taxon, tsn: Number(taxon.tsn) })); | ||
|
||
return res.status(200).json(formattedResponse); | ||
} catch (error) { | ||
defaultLog.error({ label: 'getSurveyObservedSpecies', 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
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.