-
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.
Merge branch 'dev' of github.com:bcgov/biohubbc into access-request-seed
- Loading branch information
Showing
20 changed files
with
1,450 additions
and
267 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
161 changes: 161 additions & 0 deletions
161
api/src/paths/project/{projectId}/survey/{surveyId}/critters/measurements/import.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,161 @@ | ||
import { RequestHandler } from 'express'; | ||
import { Operation } from 'express-openapi'; | ||
import { PROJECT_PERMISSION, SYSTEM_ROLE } from '../../../../../../../constants/roles'; | ||
import { getDBConnection } from '../../../../../../../database/db'; | ||
import { HTTP400 } from '../../../../../../../errors/http-error'; | ||
import { csvFileSchema } from '../../../../../../../openapi/schemas/file'; | ||
import { authorizeRequestHandler } from '../../../../../../../request-handlers/security/authorization'; | ||
import { importCSV } from '../../../../../../../services/import-services/import-csv'; | ||
import { ImportMeasurementsStrategy } from '../../../../../../../services/import-services/measurement/import-measurements-strategy'; | ||
import { scanFileForVirus } from '../../../../../../../utils/file-utils'; | ||
import { getLogger } from '../../../../../../../utils/logger'; | ||
import { parseMulterFile } from '../../../../../../../utils/media/media-utils'; | ||
import { getFileFromRequest } from '../../../../../../../utils/request'; | ||
|
||
const defaultLog = getLogger('/api/project/{projectId}/survey/{surveyId}/measurements/import'); | ||
|
||
export const POST: Operation = [ | ||
authorizeRequestHandler((req) => { | ||
return { | ||
or: [ | ||
{ | ||
validProjectPermissions: [PROJECT_PERMISSION.COORDINATOR, PROJECT_PERMISSION.COLLABORATOR], | ||
surveyId: Number(req.params.surveyId), | ||
discriminator: 'ProjectPermission' | ||
}, | ||
{ | ||
validSystemRoles: [SYSTEM_ROLE.DATA_ADMINISTRATOR], | ||
discriminator: 'SystemRole' | ||
} | ||
] | ||
}; | ||
}), | ||
importCsv() | ||
]; | ||
|
||
POST.apiDoc = { | ||
description: 'Upload Critterbase CSV Measurements file', | ||
tags: ['observations'], | ||
security: [ | ||
{ | ||
Bearer: [] | ||
} | ||
], | ||
parameters: [ | ||
{ | ||
in: 'path', | ||
description: 'SIMS survey id', | ||
name: 'projectId', | ||
required: true, | ||
schema: { | ||
type: 'integer', | ||
minimum: 1 | ||
} | ||
}, | ||
{ | ||
in: 'path', | ||
description: 'SIMS survey id', | ||
name: 'surveyId', | ||
required: true, | ||
schema: { | ||
type: 'integer', | ||
minimum: 1 | ||
} | ||
} | ||
], | ||
requestBody: { | ||
description: 'Critterbase Measurements CSV import file.', | ||
content: { | ||
'multipart/form-data': { | ||
schema: { | ||
type: 'object', | ||
additionalProperties: false, | ||
required: ['media'], | ||
properties: { | ||
media: { | ||
description: 'Critterbase Measurements CSV import file.', | ||
type: 'array', | ||
minItems: 1, | ||
maxItems: 1, | ||
items: csvFileSchema | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
responses: { | ||
201: { | ||
description: 'Measurement import success.', | ||
content: { | ||
'application/json': { | ||
schema: { | ||
type: 'object', | ||
additionalProperties: false, | ||
properties: { | ||
measurementsCreated: { | ||
description: 'Number of Critterbase measurements created.', | ||
type: 'integer' | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
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' | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Imports a `Critterbase Measurement CSV` which bulk adds measurements to Critterbase. | ||
* | ||
* @return {*} {RequestHandler} | ||
*/ | ||
export function importCsv(): RequestHandler { | ||
return async (req, res) => { | ||
const surveyId = Number(req.params.surveyId); | ||
const rawFile = getFileFromRequest(req); | ||
|
||
const connection = getDBConnection(req.keycloak_token); | ||
|
||
try { | ||
await connection.open(); | ||
|
||
// Check for viruses / malware | ||
const virusScanResult = await scanFileForVirus(rawFile); | ||
|
||
if (!virusScanResult) { | ||
throw new HTTP400('Malicious content detected, import cancelled.'); | ||
} | ||
|
||
const importCsvMeasurementsStrategy = new ImportMeasurementsStrategy(connection, surveyId); | ||
|
||
// Pass CSV file and importer as dependencies | ||
const measurementsCreated = await importCSV(parseMulterFile(rawFile), importCsvMeasurementsStrategy); | ||
|
||
await connection.commit(); | ||
|
||
return res.status(201).json({ measurementsCreated }); | ||
} catch (error) { | ||
defaultLog.error({ label: 'importMeasurementsCSV', 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
22 changes: 22 additions & 0 deletions
22
api/src/services/import-services/measurement/import-measurements-strategy.interface.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,22 @@ | ||
import { z } from 'zod'; | ||
|
||
export const CsvQualitativeMeasurementSchema = z.object({ | ||
critter_id: z.string().uuid(), | ||
capture_id: z.string().uuid(), | ||
taxon_measurement_id: z.string().uuid(), | ||
qualitative_option_id: z.string().uuid() | ||
}); | ||
|
||
export const CsvQuantitativeMeasurementSchema = z.object({ | ||
critter_id: z.string().uuid(), | ||
capture_id: z.string().uuid(), | ||
taxon_measurement_id: z.string().uuid(), | ||
value: z.number() | ||
}); | ||
|
||
export const CsvMeasurementSchema = CsvQualitativeMeasurementSchema.or(CsvQuantitativeMeasurementSchema); | ||
|
||
// Zod inferred types | ||
export type CsvMeasurement = z.infer<typeof CsvMeasurementSchema>; | ||
export type CsvQuantitativeMeasurement = z.infer<typeof CsvQuantitativeMeasurementSchema>; | ||
export type CsvQualitativeMeasurement = z.infer<typeof CsvQualitativeMeasurementSchema>; |
Oops, something went wrong.