53 lines
3.0 KiB
TypeScript
53 lines
3.0 KiB
TypeScript
import { MigrationInterface, QueryRunner } from 'typeorm';
|
|
|
|
function quoteIdentifier(identifier: string): string {
|
|
return `"${identifier.replaceAll('\"', '\"\"')}"`;
|
|
}
|
|
|
|
export class PhaseD5323FieldDiscoveries1788717600000 implements MigrationInterface {
|
|
name = 'PhaseD5323FieldDiscoveries1788717600000';
|
|
|
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
await queryRunner.query(`
|
|
CREATE TABLE asset_field_discoveries (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
asset_id uuid NOT NULL UNIQUE,
|
|
visit_id uuid NOT NULL,
|
|
status varchar(24) NOT NULL DEFAULT 'PENDING',
|
|
discovery_notes text,
|
|
observed_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by uuid NOT NULL,
|
|
reviewed_at timestamptz,
|
|
reviewed_by uuid,
|
|
review_notes text,
|
|
matched_asset_id uuid,
|
|
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_asset_field_discoveries_asset FOREIGN KEY (asset_id) REFERENCES assets(id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_asset_field_discoveries_visit FOREIGN KEY (visit_id) REFERENCES inspection_visits(id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_asset_field_discoveries_created_by FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_asset_field_discoveries_reviewed_by FOREIGN KEY (reviewed_by) REFERENCES users(id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_asset_field_discoveries_matched_asset FOREIGN KEY (matched_asset_id) REFERENCES assets(id) ON DELETE RESTRICT,
|
|
CONSTRAINT chk_asset_field_discoveries_status CHECK (status IN ('PENDING','APPROVED','MATCHED','REJECTED')),
|
|
CONSTRAINT chk_asset_field_discoveries_match CHECK (
|
|
(status = 'MATCHED' AND matched_asset_id IS NOT NULL)
|
|
OR (status <> 'MATCHED' AND matched_asset_id IS NULL)
|
|
),
|
|
CONSTRAINT chk_asset_field_discoveries_not_self_match CHECK (matched_asset_id IS NULL OR matched_asset_id <> asset_id)
|
|
)
|
|
`);
|
|
await queryRunner.query(`CREATE INDEX idx_asset_field_discoveries_status_created ON asset_field_discoveries (status, created_at DESC)`);
|
|
await queryRunner.query(`CREATE INDEX idx_asset_field_discoveries_visit ON asset_field_discoveries (visit_id, created_at DESC)`);
|
|
await queryRunner.query(`CREATE INDEX idx_asset_field_discoveries_matched ON asset_field_discoveries (matched_asset_id) WHERE matched_asset_id IS NOT NULL`);
|
|
const appRole = process.env.DB_APP_USER;
|
|
if (!appRole) throw new Error('Missing required environment variable: DB_APP_USER');
|
|
const applicationRole = quoteIdentifier(appRole);
|
|
await queryRunner.query(`GRANT SELECT, INSERT, UPDATE ON TABLE asset_field_discoveries TO ${applicationRole}`);
|
|
await queryRunner.query(`REVOKE DELETE ON TABLE asset_field_discoveries FROM ${applicationRole}`);
|
|
}
|
|
|
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
await queryRunner.query(`DROP TABLE IF EXISTS asset_field_discoveries`);
|
|
}
|
|
}
|