Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SIMSBIOHUB-665: Habitat Features Database Schema #1483

Draft
wants to merge 7 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion api/src/database-models/alert.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { z } from 'zod';
import { AlertSeverity } from '../database-units/alert_severity';

/**
* Alert Model.
Expand All @@ -10,7 +11,7 @@ export const AlertModel = z.object({
alert_type_id: z.number(),
name: z.string(),
message: z.string(),
severity: z.enum(['info', 'success', 'error', 'warning']),
severity: AlertSeverity,
data: z.object({}).nullable(),
record_end_date: z.string().nullable(),
create_date: z.string(),
Expand Down
10 changes: 10 additions & 0 deletions api/src/database-units/alert_severity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { z } from 'zod';

/**
* Alert Severity Data Type.
*
* @description Data type for `alert_severity`.
*/
export const AlertSeverity = z.enum(['info', 'warning', 'error', 'success']);
Copy link
Collaborator

@MacQSL MacQSL Feb 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Should we export the actual enum instead of the type? Would this be more useful?

Then at least downstream we would be able to reference it

export enum AlertSeverity {
    info = 'info',
    warning = 'warning',
    ...
}

export const AlertSeverityUnit = z.nativeEnum(AlertSeverity)


export type AlertSeverity = z.infer<typeof AlertSeverity>;
31 changes: 31 additions & 0 deletions api/src/database-units/quantitative_unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { z } from 'zod';

/**
* Quantitative Unit Data Type.
*
* @description Data type for `quantitative_unit`.
*/
export const QuantitativeUnit = z.enum([
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as above

'millimeter',
'centimeter',
'meter',
'milligram',
'gram',
'kilogram',
'percent',
'celsius',
'ppt',
'SCF',
'degrees',
'pH',
'seconds',
'meters squared',
'count',
'GHz',
'Hz',
'amps',
'volts',
'megapixels'
]);

export type QuantitativeUnit = z.infer<typeof QuantitativeUnit>;
9 changes: 4 additions & 5 deletions api/src/models/alert-view.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { z } from 'zod';
import { AlertRecord } from '../database-models/alert';

export const AlertStatus = z.enum(['active', 'expired']);
export type AlertStatus = z.infer<typeof AlertStatus>;

export const AlertRecordWithStatus = AlertRecord.extend({
create_date: z.string(),
status: z.enum(['active', 'expired'])
status: AlertStatus
});
export type AlertRecordWithStatus = z.infer<typeof AlertRecordWithStatus>;

Expand All @@ -17,7 +20,3 @@ export interface IAlertFilterObject {
expiresAfter?: string;
types?: string[];
}

// Define severity and status types
export type IAlertSeverity = 'info' | 'success' | 'error' | 'warning';
export type IAlertStatus = 'active' | 'expired';
12 changes: 6 additions & 6 deletions api/src/paths/alert/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { SYSTEM_IDENTITY_SOURCE } from '../../constants/database';
import { SYSTEM_ROLE } from '../../constants/roles';
import * as db from '../../database/db';
import { HTTPError } from '../../errors/http-error';
import { IAlertSeverity, IAlertStatus } from '../../models/alert-view';
import { AlertRecordWithStatus } from '../../models/alert-view';
import { AlertService } from '../../services/alert-service';
import { getMockDBConnection, getRequestHandlerMocks } from '../../__mocks__/db';
import { createAlert, getAlerts } from '../alert';
Expand All @@ -21,14 +21,14 @@ describe('getAlerts', () => {
describe('as a system user', () => {
it('returns a list of system alerts', async () => {
const mockTotal = 10;
const mockAlerts = [
const mockAlerts: AlertRecordWithStatus[] = [
{
alert_id: 1,
name: 'Alert 1',
message: 'Message 1',
alert_type_id: 1,
severity: 'error' as IAlertSeverity,
status: 'active' as IAlertStatus,
severity: 'error',
status: 'active',
data: null,
record_end_date: null,
create_date: '2020-01-01T10:10:10'
Expand All @@ -38,8 +38,8 @@ describe('getAlerts', () => {
name: 'Alert 2',
message: 'Message 2',
alert_type_id: 2,
severity: 'error' as IAlertSeverity,
status: 'active' as IAlertStatus,
severity: 'error',
status: 'active',
data: null,
record_end_date: null,
create_date: '2020-01-01T10:10:10'
Expand Down
8 changes: 4 additions & 4 deletions api/src/paths/alert/{alertId}/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { SYSTEM_IDENTITY_SOURCE } from '../../../constants/database';
import { SYSTEM_ROLE } from '../../../constants/roles';
import * as db from '../../../database/db';
import { HTTPError } from '../../../errors/http-error';
import { IAlertSeverity, IAlertStatus } from '../../../models/alert-view';
import { AlertRecordWithStatus } from '../../../models/alert-view';
import { AlertService } from '../../../services/alert-service';
import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db';

Expand All @@ -20,13 +20,13 @@ describe('getAlerts', () => {

describe('as a system user', () => {
it('returns a single system alert', async () => {
const mockAlert = {
const mockAlert: AlertRecordWithStatus = {
alert_id: 1,
name: 'Alert 1',
message: 'Message 1',
alert_type_id: 1,
severity: 'error' as IAlertSeverity,
status: 'active' as IAlertStatus,
severity: 'error',
status: 'active',
data: null,
record_end_date: null,
create_date: '2020-01-01T10:10:10'
Expand Down
17 changes: 9 additions & 8 deletions api/src/repositories/alert-repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import chai, { expect } from 'chai';
import { QueryResult } from 'pg';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import { IAlertSeverity } from '../models/alert-view';
import { AlertRecordWithStatus, IAlertCreateObject, IAlertUpdateObject } from '../models/alert-view';
import { getMockDBConnection } from '../__mocks__/db';
import { AlertRepository } from './alert-repository';

Expand All @@ -18,16 +18,17 @@ describe('AlertRepository', () => {

describe('getAlerts', () => {
it('should return an array of alerts with empty filters', async () => {
const mockRows = [
const mockRows: AlertRecordWithStatus[] = [
{
alert_id: 1,
name: 'Alert 1',
message: 'This is an alert.',
alert_type_id: 1,
data: {},
severity: 'error' as IAlertSeverity,
severity: 'error',
status: 'active',
record_end_date: null,
status: 'active'
create_date: '2020-01-01'
}
];
const mockQueryResponse = { rows: mockRows, rowCount: 1 } as unknown as QueryResult<any>;
Expand Down Expand Up @@ -109,13 +110,13 @@ describe('AlertRepository', () => {
});

const alertRepository = new AlertRepository(mockDBConnection);
const alert = {
const alert: IAlertUpdateObject = {
alert_id: 1,
name: 'Updated Alert',
message: 'Updated message',
alert_type_id: 1,
data: {},
severity: 'error' as IAlertSeverity,
severity: 'error',
record_end_date: null
};

Expand All @@ -135,12 +136,12 @@ describe('AlertRepository', () => {
});

const alertRepository = new AlertRepository(mockDBConnection);
const alert = {
const alert: IAlertCreateObject = {
name: 'New Alert',
message: 'New alert message',
alert_type_id: 1,
data: {},
severity: 'error' as IAlertSeverity,
severity: 'error',
record_end_date: null
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,9 @@
import SQL from 'sql-template-strings';
import { z } from 'zod';
import { QuantitativeUnit } from '../database-units/quantitative_unit';
import { getKnex } from '../database/db';
import { BaseRepository } from './base-repository';

// Environment unit type definition.
export const EnvironmentUnit = z.enum([
// Should be kept in sync with the database table `environment_unit`
'millimeter',
'centimeter',
'meter',
'milligram',
'gram',
'kilogram',
'percent',
'celsius',
'ppt',
'SCF',
'degrees',
'pH'
]);
export type EnvironmentUnit = z.infer<typeof EnvironmentUnit>;

// Qualitative environment option type definition.
const QualitativeEnvironmentOption = z.object({
environment_qualitative_option_id: z.string().uuid(),
Expand All @@ -46,7 +29,7 @@ const QuantitativeEnvironmentTypeDefinition = z.object({
description: z.string().nullable(),
min: z.number().nullable(),
max: z.number().nullable(),
unit: EnvironmentUnit.nullable()
unit: QuantitativeUnit.nullable()
});
export type QuantitativeEnvironmentTypeDefinition = z.infer<typeof QuantitativeEnvironmentTypeDefinition>;

Expand Down
10 changes: 5 additions & 5 deletions api/src/services/alert-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import chai, { expect } from 'chai';
import { afterEach, describe, it } from 'mocha';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import { AlertRecordWithStatus, IAlertCreateObject, IAlertFilterObject, IAlertSeverity } from '../models/alert-view';
import { AlertRecordWithStatus, IAlertCreateObject, IAlertFilterObject } from '../models/alert-view';
import { AlertRepository } from '../repositories/alert-repository';
import { getMockDBConnection } from '../__mocks__/db';
import { AlertService } from './alert-service';
Expand All @@ -26,7 +26,7 @@ describe('AlertService', () => {
message: 'Message 1',
alert_type_id: 1,
data: {},
severity: 'error' as IAlertSeverity,
severity: 'error',
status: 'active',
record_end_date: null,
create_date: '2020-01-01T10:10:10'
Expand Down Expand Up @@ -73,7 +73,7 @@ describe('AlertService', () => {
message: 'Message 1',
alert_type_id: 1,
data: {},
severity: 'error' as IAlertSeverity,
severity: 'error',
status: 'active',
record_end_date: null,
create_date: '2020-01-01T10:10:10'
Expand All @@ -99,7 +99,7 @@ describe('AlertService', () => {
message: 'New alert message',
alert_type_id: 1,
data: {},
severity: 'error' as IAlertSeverity,
severity: 'error',
record_end_date: null
};

Expand All @@ -124,7 +124,7 @@ describe('AlertService', () => {
message: 'Updated message',
alert_type_id: 1,
data: {},
severity: 'error' as IAlertSeverity,
severity: 'error',
status: 'active',
record_end_date: null,
create_date: '2020-01-01T10:10:10'
Expand Down
26 changes: 23 additions & 3 deletions app/src/interfaces/useReferenceApi.interface.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
/**
* A qualitative environment unit.
* A quantitative unit.
*/
export type EnvironmentUnit = 'millimeter' | 'centimeter' | 'meter' | 'milligram' | 'gram' | 'kilogram';
export type QuantitativeUnit =
| 'millimeter'
| 'centimeter'
| 'meter'
| 'milligram'
| 'gram'
| 'kilogram'
| 'percent'
| 'celsius'
| 'ppt'
| 'SCF'
| 'degrees'
| 'pH'
| 'seconds'
| 'meters squared'
| 'count'
| 'GHz'
| 'Hz'
| 'amps'
| 'volts'
| 'megapixels';

/**
* A quantitative environment type definition.
Expand All @@ -12,7 +32,7 @@ export type EnvironmentQuantitativeTypeDefinition = {
description: string | null;
min: number | null;
max: number | null;
unit: EnvironmentUnit | null;
unit: QuantitativeUnit | null;
};

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Knex } from 'knex';

/**
* Rename the `environment_unit` enum to `quantitative_unit`.
*
* Originally created in migration `20240417000000_obsevation_environment_tables.ts` when only the environment tables
* used it. But, it really contains generic units that can be used in other tables as well.
*
* @export
* @param {Knex} knex
* @return {*} {Promise<void>}
*/
export async function up(knex: Knex): Promise<void> {
await knex.raw(`
----------------------------------------------------------------------------------------
-- Rename the environment_unit enum to quantitative_unit
----------------------------------------------------------------------------------------

SET SEARCH_PATH=biohub;

ALTER TYPE environment_unit RENAME TO quantitative_unit;
`);
}

export async function down(knex: Knex): Promise<void> {
await knex.raw(``);
}
42 changes: 42 additions & 0 deletions database/src/migrations/20250206000002_update_environments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Knex } from 'knex';

/**
* Updates to environment tables:
* - Add missing foreign key constraint for environment_qualitative_option table.
* - Add indexes on 'name' column in environment_qualitative and environment_quantitative tables to improve search
* performance.
*
* @export
* @param {Knex} knex
* @return {*} {Promise<void>}
*/
export async function up(knex: Knex): Promise<void> {
await knex.raw(`--sql
----------------------------------------------------------------------------------------
-- Add missing foreign key
----------------------------------------------------------------------------------------

SET SEARCH_PATH=biohub, public;

-- Add foreign key constraint
ALTER TABLE environment_qualitative_option
ADD CONSTRAINT environment_qualitative_option_fk1
FOREIGN KEY (environment_qualitative_id)
REFERENCES environment_qualitative(environment_qualitative_id);

-- Add indexes for foreign keys
CREATE INDEX environment_qualitative_option_idx1 ON environment_qualitative_option(environment_qualitative_id);

----------------------------------------------------------------------------------------
-- Add indexes to improve search performance
----------------------------------------------------------------------------------------

CREATE INDEX environment_qualitative_idx1 ON environment_qualitative(name);

CREATE INDEX environment_quantitative_idx1 ON environment_quantitative(name);
`);
}

export async function down(knex: Knex): Promise<void> {
await knex.raw(``);
}
Loading