Skip to content

Commit

Permalink
Add environmental variables & sampling methods to standards page (#1325)
Browse files Browse the repository at this point in the history
* initial commit from transferring otu of method-standards

* quick div to box fix for header not showing

* Moving components around based off of Mac's suggestions.

* update standards page layout

* mock method data

* environment standards

* add backend tests

* replace getApiUserDBConnection with getDBConnection temporarily

* lots of changes, MethodStandards Card and results, API path, sessions wih Macgregor

* formatting, fixing SQL, adding unit chip

* some fies to font, color, chevrons, chip etc

* simplify standard cards

* write tests

* species standards loading state

* fix missing keys

* shade of grey

* ignore-skip

* fix: removed bad import from merge conflict

* remove security from standards endpoints & address PR comments

---------

Co-authored-by: Macgregor Aubertin-Young <macgregor.aubertin-young@gov.bc.ca>
Co-authored-by: Macgregor Aubertin-Young <108430771+mauberti-bc@users.noreply.github.com>
Co-authored-by: Mac Deluca <99926243+MacQSL@users.noreply.github.com>
Co-authored-by: Mac Deluca <Mac.Deluca@quartech.com>
  • Loading branch information
5 people authored Aug 21, 2024
1 parent c276f1b commit d50c70f
Show file tree
Hide file tree
Showing 34 changed files with 1,632 additions and 395 deletions.
53 changes: 53 additions & 0 deletions api/src/models/standards-view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { z } from 'zod';
import {
CBQualitativeMeasurementTypeDefinition,
CBQuantitativeMeasurementTypeDefinition
} from '../services/critterbase-service';

const QualitativeMeasurementSchema = z.object({
name: z.string(),
description: z.string().nullable(),
options: z.array(
z.object({
name: z.string(),
description: z.string().nullable()
})
)
});

const QuantitativeMeasurementSchema = z.object({
name: z.string(),
description: z.string().nullable(),
unit: z.string().nullable()
});

const MethodAttributesSchema = z.object({
qualitative: z.array(QualitativeMeasurementSchema),
quantitative: z.array(QuantitativeMeasurementSchema)
});

export const EnvironmentStandardsSchema = z.object({
qualitative: z.array(QualitativeMeasurementSchema),
quantitative: z.array(QuantitativeMeasurementSchema)
});

export type EnvironmentStandards = z.infer<typeof EnvironmentStandardsSchema>;

export const MethodStandardSchema = z.object({
method_lookup_id: z.number(),
name: z.string(),
description: z.string().nullable(),
attributes: MethodAttributesSchema
});

export type MethodStandard = z.infer<typeof MethodStandardSchema>;

export interface ISpeciesStandards {
tsn: number;
scientificName: string;
measurements: {
quantitative: CBQuantitativeMeasurementTypeDefinition[];
qualitative: CBQualitativeMeasurementTypeDefinition[];
};
markingBodyLocations: { id: string; key: string; value: string }[];
}
137 changes: 137 additions & 0 deletions api/src/openapi/schemas/standards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { OpenAPIV3 } from 'openapi-types';

export const EnvironmentStandardsSchema: OpenAPIV3.SchemaObject = {
type: 'object',
description:
'Environment standards response object showing supported environmental variables and associated information',
additionalProperties: false,
properties: {
qualitative: {
type: 'array',
description: 'Array of qualitative environmental variables',
items: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Name of the environmental variable'
},
description: {
type: 'string',
description: 'Description of the environmental variable',
nullable: true
},
options: {
type: 'array',
description: 'Array of options for the qualitative variable',
items: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Description of the environmental variable option'
},
description: {
type: 'string',
description: 'Description of the environmental variable option',
nullable: true
}
}
}
}
}
}
},
quantitative: {
type: 'array',
description: 'Array of quantitative environmental variables',
items: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Name of the quantitative environmental variable'
},
description: {
type: 'string',
description: 'Description of the quantitative environmental variable',
nullable: true
},
unit: {
type: 'string',
description: 'Unit of measurement of the quantitative environmental variable',
nullable: true
}
}
}
}
}
};

export const MethodStandardSchema: OpenAPIV3.SchemaObject = {
type: 'array',
items: {
type: 'object',
additionalProperties: false,
properties: {
method_lookup_id: { type: 'number' },
name: { type: 'string' },
description: { type: 'string', nullable: true },
attributes: {
type: 'object',
additionalProperties: false,
properties: {
qualitative: {
type: 'array',
items: {
type: 'object',
additionalProperties: false,
properties: {
name: {
type: 'string'
},
description: {
type: 'string',
nullable: true
},
options: {
type: 'array',
items: {
type: 'object',
additionalProperties: false,
properties: {
name: {
type: 'string'
},
description: {
type: 'string',
nullable: true
}
}
}
}
}
}
},
quantitative: {
type: 'array',
items: {
type: 'object',
additionalProperties: false,
properties: {
name: {
type: 'string'
},
description: {
type: 'string',
nullable: true
},
unit: { type: 'string', nullable: true }
}
}
}
}
}
}
}
};
89 changes: 89 additions & 0 deletions api/src/paths/standards/environment/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import chai, { expect } from 'chai';
import { afterEach, describe, it } from 'mocha';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import * as db from '../../../database/db';
import { HTTPError } from '../../../errors/http-error';
import { StandardsService } from '../../../services/standards-service';
import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db';
import { getEnvironmentStandards } from './index'; // Adjust the import path based on your file structure

chai.use(sinonChai);

describe('standards/environment', () => {
describe('getEnvironmentStandards', () => {
afterEach(() => {
sinon.restore();
});

it('should retrieve environment standards successfully', async () => {
const mockResponse = {
quantitative: [
{ name: 'Quantitative Standard 1', description: 'Description 1', unit: 'Unit' },
{ name: 'Quantitative Standard 2', description: 'Description 2', unit: 'Unit' }
],
qualitative: [
{
name: 'Qualitative Standard 1',
description: 'Description 1',
options: [
{ name: 'Option 1', description: 'Option 1 Description' },
{ name: 'Option 2', description: 'Option 2 Description' }
]
},
{
name: 'Qualitative Standard 2',
description: 'Description 2',
options: [
{ name: 'Option 3', description: 'Option 3 Description' },
{ name: 'Option 4', description: 'Option 4 Description' }
]
}
]
};

const mockDBConnection = getMockDBConnection();

sinon.stub(db, 'getAPIUserDBConnection').returns(mockDBConnection);

sinon.stub(StandardsService.prototype, 'getEnvironmentStandards').resolves(mockResponse);

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

const requestHandler = getEnvironmentStandards();

await requestHandler(mockReq, mockRes, mockNext);

expect(mockRes.status).to.have.been.calledWith(200);
expect(mockRes.json).to.have.been.calledWith(mockResponse);
});

it('catches and re-throws error', async () => {
const mockDBConnection = getMockDBConnection({
open: sinon.stub(),
commit: sinon.stub(),
rollback: sinon.stub(),
release: sinon.stub()
});

sinon.stub(db, 'getAPIUserDBConnection').returns(mockDBConnection);

sinon
.stub(StandardsService.prototype, 'getEnvironmentStandards')
.rejects(new Error('Failed to retrieve environment standards'));

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

try {
const requestHandler = getEnvironmentStandards();
await requestHandler(mockReq, mockRes, mockNext);
expect.fail();
} catch (actualError) {
expect(mockDBConnection.open).to.have.been.calledOnce;
expect(mockDBConnection.rollback).to.have.been.calledOnce;
expect(mockDBConnection.release).to.have.been.calledOnce;
expect((actualError as HTTPError).message).to.equal('Failed to retrieve environment standards');
}
});
});
});
83 changes: 83 additions & 0 deletions api/src/paths/standards/environment/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { RequestHandler } from 'express';
import { Operation } from 'express-openapi';
import { getAPIUserDBConnection } from '../../../database/db';
import { EnvironmentStandardsSchema } from '../../../openapi/schemas/standards';
import { StandardsService } from '../../../services/standards-service';
import { getLogger } from '../../../utils/logger';

const defaultLog = getLogger('paths/projects');

export const GET: Operation = [getEnvironmentStandards()];

GET.apiDoc = {
description: 'Gets lookup values for environment variables',
tags: ['standards'],
parameters: [
{
in: 'query',
name: 'keyword',
required: false,
schema: {
type: 'string',
nullable: true
}
}
],
security: [],
responses: {
200: {
description: 'Environment data standards response object.',
content: {
'application/json': {
schema: EnvironmentStandardsSchema
}
}
},
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'
}
}
};

/**
* Get species data standards
*
* @returns {RequestHandler}
*/
export function getEnvironmentStandards(): RequestHandler {
return async (req, res) => {
const connection = getAPIUserDBConnection();

try {
await connection.open();

const standardsService = new StandardsService(connection);

const keyword = (req.query.keyword as string) ?? '';

const response = await standardsService.getEnvironmentStandards(keyword);

await connection.commit();

return res.status(200).json(response);
} catch (error) {
defaultLog.error({ label: 'getEnvironmentStandards', message: 'error', error });
connection.rollback();
throw error;
} finally {
connection.release();
}
};
}
Loading

0 comments on commit d50c70f

Please sign in to comment.