156 lines
6.8 KiB
TypeScript
156 lines
6.8 KiB
TypeScript
import { MigrationInterface, QueryRunner } from 'typeorm';
|
|
|
|
const newPermissions = [
|
|
'surveys.read',
|
|
'surveys.manage',
|
|
'surveys.assign',
|
|
'surveys.execute',
|
|
] as const;
|
|
|
|
const rolePermissionValues = `
|
|
('admin', 'surveys.read'),
|
|
('admin', 'surveys.manage'),
|
|
('admin', 'surveys.assign'),
|
|
('admin', 'surveys.execute'),
|
|
('director', 'surveys.read'),
|
|
('supervisor', 'surveys.read'),
|
|
('supervisor', 'surveys.manage'),
|
|
('supervisor', 'surveys.assign'),
|
|
('supervisor', 'surveys.execute'),
|
|
('inspector', 'surveys.read'),
|
|
('inspector', 'surveys.execute'),
|
|
('auditor', 'surveys.read')
|
|
`;
|
|
|
|
function quoteIdentifier(identifier: string): string {
|
|
return `"${identifier.replaceAll('"', '""')}"`;
|
|
}
|
|
|
|
export class PhaseC1SurveyPlanning1786809600000 implements MigrationInterface {
|
|
name = 'PhaseC1SurveyPlanning1786809600000';
|
|
|
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
await queryRunner.query(`
|
|
CREATE TABLE survey_campaigns (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
code varchar(80) NOT NULL,
|
|
name varchar(200) NOT NULL,
|
|
description text,
|
|
status varchar(24) NOT NULL DEFAULT 'DRAFT',
|
|
planned_start_at timestamptz,
|
|
planned_end_at timestamptz,
|
|
scope_asset_id uuid,
|
|
coordinator_user_id uuid,
|
|
created_by uuid,
|
|
updated_by uuid,
|
|
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT uq_survey_campaigns_code UNIQUE (code),
|
|
CONSTRAINT chk_survey_campaigns_status CHECK (status IN (
|
|
'DRAFT', 'PLANNED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED'
|
|
)),
|
|
CONSTRAINT chk_survey_campaigns_dates CHECK (
|
|
planned_start_at IS NULL OR planned_end_at IS NULL OR planned_end_at >= planned_start_at
|
|
),
|
|
CONSTRAINT fk_survey_campaigns_scope_asset FOREIGN KEY (scope_asset_id)
|
|
REFERENCES assets(id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_survey_campaigns_coordinator FOREIGN KEY (coordinator_user_id)
|
|
REFERENCES users(id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_survey_campaigns_created_by FOREIGN KEY (created_by)
|
|
REFERENCES users(id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_survey_campaigns_updated_by FOREIGN KEY (updated_by)
|
|
REFERENCES users(id) ON DELETE SET NULL
|
|
)
|
|
`);
|
|
await queryRunner.query(`CREATE INDEX idx_survey_campaigns_status ON survey_campaigns (status)`);
|
|
await queryRunner.query(`CREATE INDEX idx_survey_campaigns_scope_asset_id ON survey_campaigns (scope_asset_id)`);
|
|
await queryRunner.query(`CREATE INDEX idx_survey_campaigns_coordinator_user_id ON survey_campaigns (coordinator_user_id)`);
|
|
await queryRunner.query(`CREATE INDEX idx_survey_campaigns_planned_dates ON survey_campaigns (planned_start_at, planned_end_at)`);
|
|
|
|
await queryRunner.query(`
|
|
CREATE TABLE survey_campaign_targets (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
campaign_id uuid NOT NULL,
|
|
asset_id uuid NOT NULL,
|
|
assigned_user_id uuid,
|
|
status varchar(24) NOT NULL DEFAULT 'PENDING',
|
|
due_at timestamptz,
|
|
instructions text,
|
|
created_by uuid,
|
|
updated_by uuid,
|
|
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT uq_survey_campaign_targets_campaign_asset UNIQUE (campaign_id, asset_id),
|
|
CONSTRAINT chk_survey_campaign_targets_status CHECK (status IN (
|
|
'PENDING', 'IN_PROGRESS', 'COMPLETED', 'SKIPPED'
|
|
)),
|
|
CONSTRAINT fk_survey_campaign_targets_campaign FOREIGN KEY (campaign_id)
|
|
REFERENCES survey_campaigns(id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_survey_campaign_targets_asset FOREIGN KEY (asset_id)
|
|
REFERENCES assets(id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_survey_campaign_targets_assignee FOREIGN KEY (assigned_user_id)
|
|
REFERENCES users(id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_survey_campaign_targets_created_by FOREIGN KEY (created_by)
|
|
REFERENCES users(id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_survey_campaign_targets_updated_by FOREIGN KEY (updated_by)
|
|
REFERENCES users(id) ON DELETE SET NULL
|
|
)
|
|
`);
|
|
await queryRunner.query(`CREATE INDEX idx_survey_campaign_targets_campaign_status ON survey_campaign_targets (campaign_id, status)`);
|
|
await queryRunner.query(`CREATE INDEX idx_survey_campaign_targets_asset_id ON survey_campaign_targets (asset_id)`);
|
|
await queryRunner.query(`CREATE INDEX idx_survey_campaign_targets_assigned_user_id ON survey_campaign_targets (assigned_user_id)`);
|
|
|
|
await queryRunner.query(`
|
|
INSERT INTO permissions (code, description)
|
|
VALUES
|
|
('surveys.read', 'Consultar planificación de relevamientos'),
|
|
('surveys.manage', 'Crear y actualizar campañas de relevamiento'),
|
|
('surveys.assign', 'Asignar objetivos de relevamiento'),
|
|
('surveys.execute', 'Actualizar el avance de objetivos asignados')
|
|
ON CONFLICT (code) DO UPDATE SET description = EXCLUDED.description
|
|
`);
|
|
await queryRunner.query(`
|
|
WITH mapping(role_code, permission_code) AS (VALUES ${rolePermissionValues})
|
|
INSERT INTO role_permissions (role_id, permission_id)
|
|
SELECT role.id, permission.id
|
|
FROM mapping
|
|
INNER JOIN roles role ON role.code = mapping.role_code
|
|
INNER JOIN permissions permission ON permission.code = mapping.permission_code
|
|
ON CONFLICT (role_id, permission_id) DO NOTHING
|
|
`);
|
|
|
|
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 survey_campaigns, survey_campaign_targets
|
|
TO ${applicationRole}
|
|
`);
|
|
await queryRunner.query(`
|
|
REVOKE DELETE ON TABLE survey_campaigns, survey_campaign_targets
|
|
FROM ${applicationRole}
|
|
`);
|
|
}
|
|
|
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
await queryRunner.query(`
|
|
WITH mapping(role_code, permission_code) AS (VALUES ${rolePermissionValues})
|
|
DELETE FROM role_permissions role_permission
|
|
USING roles role, permissions permission, mapping
|
|
WHERE role_permission.role_id = role.id
|
|
AND role_permission.permission_id = permission.id
|
|
AND role.code = mapping.role_code
|
|
AND permission.code = mapping.permission_code
|
|
`);
|
|
await queryRunner.query(
|
|
`DELETE FROM permissions WHERE code = ANY($1::varchar[])
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM role_permissions WHERE permission_id = permissions.id
|
|
)`,
|
|
[newPermissions],
|
|
);
|
|
await queryRunner.query(`DROP TABLE survey_campaign_targets`);
|
|
await queryRunner.query(`DROP TABLE survey_campaigns`);
|
|
}
|
|
}
|