Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:UserOfficeProject/user-office-co…
Browse files Browse the repository at this point in the history
…re into SWAP-4396-user-officer-experiment-safety-review-workflow
  • Loading branch information
yoganandaness committed Jan 30, 2025
2 parents 39060e2 + 9f5019b commit 657061c
Show file tree
Hide file tree
Showing 29 changed files with 513 additions and 57 deletions.
14 changes: 14 additions & 0 deletions apps/backend/db_patches/0168_AddCoProposerClaims.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
DO
$$
BEGIN
IF register_patch('0168_CreateCoProposerInvite', 'Jekabs Karklins', 'Adding co-proposer claims', '2025-01-10') THEN
BEGIN
CREATE TABLE IF NOT EXISTS co_proposer_invites (
invite_code_id INT NOT NULL REFERENCES invite_codes(invite_code_id) ON DELETE CASCADE,
proposal_pk INT NOT NULL REFERENCES proposals(proposal_pk) ON DELETE CASCADE,
PRIMARY KEY (invite_code_id, proposal_pk));
END;
END IF;
END;
$$
LANGUAGE plpgsql;
16 changes: 8 additions & 8 deletions apps/backend/db_patches/0168_ExperimentWorkflow.sql
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ BEGIN

BEGIN
/* Creating enum for entity_type */
CREATE TYPE entity_type AS ENUM ('proposal', 'experiment');
CREATE TYPE entity_type AS ENUM ('PROPOSAL', 'EXPERIMENT');
END;

BEGIN
/* Adding entity_type column to the workflow and status related tables. Set the default to proposal temporarily, so that the existing record get populated with the value as proposal. Remove the default value after this. */
ALTER TABLE statuses ADD COLUMN entity_type entity_type NOT NULL DEFAULT 'proposal';
ALTER TABLE workflows ADD COLUMN entity_type entity_type NOT NULL DEFAULT 'proposal';
ALTER TABLE statuses ADD COLUMN entity_type entity_type NOT NULL DEFAULT 'PROPOSAL';
ALTER TABLE workflows ADD COLUMN entity_type entity_type NOT NULL DEFAULT 'PROPOSAL';
END;

BEGIN
Expand All @@ -43,11 +43,11 @@ BEGIN

BEGIN
-- Insert the Workflow Statuses
INSERT INTO statuses (name, short_code, description, entity_type, is_default) VALUES ('AWAITING ESF', 'AWAITING_ESF', 'When an Experiment is created, the default status will be AWAITING_ESF. This means that the experimenter needs to submit the ESF(Experiment Safety Form).', 'experiment', true);
INSERT INTO statuses (name, short_code, description, entity_type, is_default) VALUES ('ESF IS REVIEW', 'ESF_IS_REVIEW', 'IS(Instrument Scientist) needs to review the ESF.', 'experiment', true);
INSERT INTO statuses (name, short_code, description, entity_type, is_default) VALUES ('ESF ESR REVIEW', 'ESF_ESR_REVIEW', 'ESR(Experiment Safety Reviewer) needs to review the ESF.', 'experiment', true);
INSERT INTO statuses (name, short_code, description, entity_type, is_default) VALUES ('ESF REJECTED', 'ESF_REJECTED', 'ESF rejected.', 'experiment', true);
INSERT INTO statuses (name, short_code, description, entity_type, is_default) VALUES ('ESF APROVED', 'ESF_APROVED', 'ESF approved.', 'experiment', true);
INSERT INTO statuses (name, short_code, description, entity_type, is_default) VALUES ('AWAITING ESF', 'AWAITING_ESF', 'When an Experiment is created, the default status will be AWAITING_ESF. This means that the experimenter needs to submit the ESF(Experiment Safety Form).', 'EXPERIMENT', true);
INSERT INTO statuses (name, short_code, description, entity_type, is_default) VALUES ('ESF IS REVIEW', 'ESF_IS_REVIEW', 'IS(Instrument Scientist) needs to review the ESF.', 'EXPERIMENT', true);
INSERT INTO statuses (name, short_code, description, entity_type, is_default) VALUES ('ESF ESR REVIEW', 'ESF_ESR_REVIEW', 'ESR(Experiment Safety Reviewer) needs to review the ESF.', 'EXPERIMENT', true);
INSERT INTO statuses (name, short_code, description, entity_type, is_default) VALUES ('ESF REJECTED', 'ESF_REJECTED', 'ESF rejected.', 'EXPERIMENT', true);
INSERT INTO statuses (name, short_code, description, entity_type, is_default) VALUES ('ESF APROVED', 'ESF_APROVED', 'ESF approved.', 'EXPERIMENT', true);
END;

END IF;
Expand Down
9 changes: 5 additions & 4 deletions apps/backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"@user-office-software/duo-localisation": "^1.2.0",
"@user-office-software/duo-logger": "^2.1.1",
"@user-office-software/duo-message-broker": "^1.6.0",
"@user-office-software/duo-validation": "^5.1.12",
"@user-office-software/duo-validation": "^5.1.13",
"@user-office-software/openid": "^1.4.0",
"await-to-js": "^2.1.1",
"bcryptjs": "^2.4.3",
Expand Down
26 changes: 26 additions & 0 deletions apps/backend/src/auth/InviteAuthorizer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { inject, injectable } from 'tsyringe';

import { Tokens } from '../config/Tokens';
import { UserRole, UserWithRole } from '../models/User';
import { UserAuthorization } from './UserAuthorization';

@injectable()
export class InviteAuthorization {
constructor(
@inject(Tokens.UserAuthorization) private userAuth: UserAuthorization
) {}

public isRoleInviteAuthorized = async (
agent: UserWithRole | null,
roleIds?: number[]
) => {
// If no roleIds are provided, the invite is considered as authorized
if (roleIds === undefined || roleIds.length === 0) return true;

if (this.userAuth.isUserOfficer(agent)) return true;

const onlyUserRole = roleIds.length === 1 && roleIds[0] === UserRole.USER;

return onlyUserRole;
};
}
2 changes: 2 additions & 0 deletions apps/backend/src/config/Tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const Tokens = {
CallDataSource: Symbol('CallDataSource'),
ConfigureEnvironment: Symbol('ConfigureEnvironment'),
ConfigureLogger: Symbol('ConfigureLogger'),
CoProposerInviteDataSource: Symbol('CoProposerInviteDataSource'),
EmailEventHandler: Symbol('EmailEventHandler'),
EventBus: Symbol('EventBus'),
EventLogsDataSource: Symbol('EventLogsDataSource'),
Expand All @@ -13,6 +14,7 @@ export const Tokens = {
GenericTemplateDataSource: Symbol('GenericTemplateDataSource'),
InstrumentDataSource: Symbol('InstrumentDataSource'),
InviteCodeDataSource: Symbol('InviteCodeDataSource'),
InviteAuthorization: Symbol('InviteAuthorization'),
TechniqueDataSource: Symbol('TechniqueDataSource'),
ListenToMessageQueue: Symbol('ListenToMessageQueue'),
MailService: Symbol('MailService'),
Expand Down
4 changes: 4 additions & 0 deletions apps/backend/src/config/dependencyConfigDefault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import {
} from '@user-office-software/duo-logger';

import 'reflect-metadata';
import { InviteAuthorization } from '../auth/InviteAuthorizer';
import { OAuthAuthorization } from '../auth/OAuthAuthorization';
import { ProposalAuthorization } from '../auth/ProposalAuthorization';
import { PostgresAdminDataSourceWithAutoUpgrade } from '../datasources/postgres/AdminDataSource';
import PostgresCallDataSource from '../datasources/postgres/CallDataSource';
import PostgresCoProposerInviteDataSource from '../datasources/postgres/CoProposerInviteDataSource';
import PostgresEventLogsDataSource from '../datasources/postgres/EventLogsDataSource';
import PostgresFapDataSource from '../datasources/postgres/FapDataSource';
import PostgresFeedbackDataSource from '../datasources/postgres/FeedbackDataSource';
Expand Down Expand Up @@ -64,6 +66,7 @@ async function skipEmailHandler(event: ApplicationEvent) {
}

mapClass(Tokens.AdminDataSource, PostgresAdminDataSourceWithAutoUpgrade);
mapClass(Tokens.CoProposerInviteDataSource, PostgresCoProposerInviteDataSource);
mapClass(Tokens.CallDataSource, PostgresCallDataSource);
mapClass(Tokens.EventLogsDataSource, PostgresEventLogsDataSource);
mapClass(Tokens.FeedbackDataSource, PostgresFeedbackDataSource);
Expand All @@ -72,6 +75,7 @@ mapClass(Tokens.GenericTemplateDataSource, PostgresGenericTemplateDataSource);
mapClass(Tokens.InstrumentDataSource, PostgresInstrumentDataSource);
mapClass(Tokens.InviteCodeDataSource, PostgresInviteCodesDataSource);
mapClass(Tokens.RoleInviteDataSource, PostgresRoleInviteDataSource);
mapClass(Tokens.InviteAuthorization, InviteAuthorization);
mapClass(Tokens.InternalReviewDataSource, PostgresInternalReviewDataSource);
mapClass(Tokens.PdfTemplateDataSource, PostgresPdfTemplateDataSource);
mapClass(Tokens.ProposalDataSource, PostgresProposalDataSource);
Expand Down
4 changes: 4 additions & 0 deletions apps/backend/src/config/dependencyConfigE2E.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { ConsoleLogger, setLogger } from '@user-office-software/duo-logger';

import 'reflect-metadata';
import { InviteAuthorization } from '../auth/InviteAuthorizer';
import { OAuthAuthorization } from '../auth/OAuthAuthorization';
import { ProposalAuthorization } from '../auth/ProposalAuthorization';
import PostgresAdminDataSource from '../datasources/postgres/AdminDataSource';
import PostgresCallDataSource from '../datasources/postgres/CallDataSource';
import PostgresCoProposerInviteDataSource from '../datasources/postgres/CoProposerInviteDataSource';
import PostgresEventLogsDataSource from '../datasources/postgres/EventLogsDataSource';
import PostgresFapDataSource from '../datasources/postgres/FapDataSource';
import PostgresFeedbackDataSource from '../datasources/postgres/FeedbackDataSource';
Expand Down Expand Up @@ -56,13 +58,15 @@ import { Tokens } from './Tokens';
import { mapClass, mapValue } from './utils';

mapClass(Tokens.AdminDataSource, PostgresAdminDataSource);
mapClass(Tokens.CoProposerInviteDataSource, PostgresCoProposerInviteDataSource);
mapClass(Tokens.CallDataSource, PostgresCallDataSource);
mapClass(Tokens.EventLogsDataSource, PostgresEventLogsDataSource);
mapClass(Tokens.FeedbackDataSource, PostgresFeedbackDataSource);
mapClass(Tokens.FileDataSource, PostgresFileDataSource);
mapClass(Tokens.GenericTemplateDataSource, PostgresGenericTemplateDataSource);
mapClass(Tokens.InstrumentDataSource, PostgresInstrumentDataSource);
mapClass(Tokens.InviteCodeDataSource, PostgresInviteCodesDataSource);
mapClass(Tokens.InviteAuthorization, InviteAuthorization);
mapClass(Tokens.RoleInviteDataSource, PostgresRoleInviteDataSource);
mapClass(Tokens.InternalReviewDataSource, PostgresInternalReviewDataSource);
mapClass(Tokens.PdfTemplateDataSource, PostgresPdfTemplateDataSource);
Expand Down
4 changes: 4 additions & 0 deletions apps/backend/src/config/dependencyConfigELI.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'reflect-metadata';

import { InviteAuthorization } from '../auth/InviteAuthorizer';
import { OAuthAuthorization } from '../auth/OAuthAuthorization';
import { ProposalAuthorization } from '../auth/ProposalAuthorization';
import { PostgresAdminDataSourceWithAutoUpgrade } from '../datasources/postgres/AdminDataSource';
import PostgresCallDataSource from '../datasources/postgres/CallDataSource';
import PostgresCoProposerInviteDataSource from '../datasources/postgres/CoProposerInviteDataSource';
import PostgresEventLogsDataSource from '../datasources/postgres/EventLogsDataSource';
import PostgresFapDataSource from '../datasources/postgres/FapDataSource';
import PostgresFeedbackDataSource from '../datasources/postgres/FeedbackDataSource';
Expand Down Expand Up @@ -58,6 +60,7 @@ import { mapClass, mapValue } from './utils';
const isProduction = process.env.NODE_ENV === 'production';

mapClass(Tokens.AdminDataSource, PostgresAdminDataSourceWithAutoUpgrade);
mapClass(Tokens.CoProposerInviteDataSource, PostgresCoProposerInviteDataSource);
mapClass(Tokens.CallDataSource, PostgresCallDataSource);
mapClass(Tokens.EventLogsDataSource, PostgresEventLogsDataSource);
mapClass(Tokens.FeedbackDataSource, PostgresFeedbackDataSource);
Expand All @@ -66,6 +69,7 @@ mapClass(Tokens.GenericTemplateDataSource, PostgresGenericTemplateDataSource);
mapClass(Tokens.InstrumentDataSource, PostgresInstrumentDataSource);
mapClass(Tokens.InviteCodeDataSource, PostgresInviteCodesDataSource);
mapClass(Tokens.RoleInviteDataSource, PostgresRoleInviteDataSource);
mapClass(Tokens.InviteAuthorization, InviteAuthorization);
mapClass(Tokens.PdfTemplateDataSource, PostgresPdfTemplateDataSource);
mapClass(Tokens.ProposalDataSource, PostgresProposalDataSource);
mapClass(Tokens.ProposalEsiDataSource, PostgresProposalEsiDataSource);
Expand Down
4 changes: 4 additions & 0 deletions apps/backend/src/config/dependencyConfigESS.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import 'reflect-metadata';

import { InviteAuthorization } from '../auth/InviteAuthorizer';
import { OAuthAuthorization } from '../auth/OAuthAuthorization';
import { ProposalAuthorization } from '../auth/ProposalAuthorization';
import { PostgresAdminDataSourceWithAutoUpgrade } from '../datasources/postgres/AdminDataSource';
import PostgresCallDataSource from '../datasources/postgres/CallDataSource';
import PostgresCoProposerInviteDataSource from '../datasources/postgres/CoProposerInviteDataSource';
import PostgresEventLogsDataSource from '../datasources/postgres/EventLogsDataSource';
import PostgresFapDataSource from '../datasources/postgres/FapDataSource';
import PostgresFeedbackDataSource from '../datasources/postgres/FeedbackDataSource';
Expand Down Expand Up @@ -58,6 +60,7 @@ import { Tokens } from './Tokens';
import { mapClass, mapValue } from './utils';

mapClass(Tokens.AdminDataSource, PostgresAdminDataSourceWithAutoUpgrade);
mapClass(Tokens.CoProposerInviteDataSource, PostgresCoProposerInviteDataSource);
mapClass(Tokens.CallDataSource, PostgresCallDataSource);
mapClass(Tokens.EventLogsDataSource, PostgresEventLogsDataSource);
mapClass(Tokens.FeedbackDataSource, PostgresFeedbackDataSource);
Expand All @@ -66,6 +69,7 @@ mapClass(Tokens.GenericTemplateDataSource, PostgresGenericTemplateDataSource);
mapClass(Tokens.InstrumentDataSource, PostgresInstrumentDataSource);
mapClass(Tokens.InviteCodeDataSource, PostgresInviteCodesDataSource);
mapClass(Tokens.RoleInviteDataSource, PostgresRoleInviteDataSource);
mapClass(Tokens.InviteAuthorization, InviteAuthorization);
mapClass(Tokens.PdfTemplateDataSource, PostgresPdfTemplateDataSource);
mapClass(Tokens.ProposalDataSource, PostgresProposalDataSource);
mapClass(Tokens.ProposalEsiDataSource, PostgresProposalEsiDataSource);
Expand Down
4 changes: 4 additions & 0 deletions apps/backend/src/config/dependencyConfigSTFC.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { setLogger, ConsoleLogger } from '@user-office-software/duo-logger';
import 'reflect-metadata';

import { InviteAuthorization } from '../auth/InviteAuthorizer';
import { StfcProposalAuthorization } from '../auth/StfcProposalAuthorization';
import { StfcUserAuthorization } from '../auth/StfcUserAuthorization';
import { PostgresAdminDataSourceWithAutoUpgrade } from '../datasources/postgres/AdminDataSource';
import PostgresCallDataSource from '../datasources/postgres/CallDataSource';
import PostgresCoProposerInviteDataSource from '../datasources/postgres/CoProposerInviteDataSource';
import PostgresEventLogsDataSource from '../datasources/postgres/EventLogsDataSource';
import PostgresFeedbackDataSource from '../datasources/postgres/FeedbackDataSource';
import PostgresFileDataSource from '../datasources/postgres/FileDataSource';
Expand Down Expand Up @@ -56,6 +58,7 @@ import { Tokens } from './Tokens';
import { mapClass, mapValue } from './utils';

mapClass(Tokens.AdminDataSource, PostgresAdminDataSourceWithAutoUpgrade);
mapClass(Tokens.CoProposerInviteDataSource, PostgresCoProposerInviteDataSource);
mapClass(Tokens.CallDataSource, PostgresCallDataSource);
mapClass(Tokens.EventLogsDataSource, PostgresEventLogsDataSource);
mapClass(Tokens.FeedbackDataSource, PostgresFeedbackDataSource);
Expand All @@ -64,6 +67,7 @@ mapClass(Tokens.GenericTemplateDataSource, PostgresGenericTemplateDataSource);
mapClass(Tokens.InstrumentDataSource, StfcInstrumentDataSource);
mapClass(Tokens.InviteCodeDataSource, PostgresInviteCodesDataSource);
mapClass(Tokens.RoleInviteDataSource, PostgresRoleInviteDataSource);
mapClass(Tokens.InviteAuthorization, InviteAuthorization);
mapClass(Tokens.PdfTemplateDataSource, PostgresPdfTemplateDataSource);
mapClass(Tokens.ProposalDataSource, StfcProposalDataSource);
mapClass(Tokens.ProposalEsiDataSource, PostgresProposalEsiDataSource);
Expand Down
8 changes: 6 additions & 2 deletions apps/backend/src/config/dependencyConfigTest.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import { setLogger, ConsoleLogger } from '@user-office-software/duo-logger';
import { ConsoleLogger, setLogger } from '@user-office-software/duo-logger';

import { UserAuthorizationMock } from '../auth/mockups/UserAuthorization';
import 'reflect-metadata';
import { InviteAuthorization } from '../auth/InviteAuthorizer';
import { UserAuthorizationMock } from '../auth/mockups/UserAuthorization';
import { ProposalAuthorization } from '../auth/ProposalAuthorization';
import { AdminDataSourceMock } from '../datasources/mockups/AdminDataSource';
import { CallDataSourceMock } from '../datasources/mockups/CallDataSource';
import { CoProposerInviteDataSourceMock } from '../datasources/mockups/CoProposerInviteDataSource';
import { EventLogsDataSourceMock } from '../datasources/mockups/EventLogsDataSource';
import { FapDataSourceMock } from '../datasources/mockups/FapDataSource';
import { FeedbackDataSourceMock } from '../datasources/mockups/FeedbackDataSource';
Expand Down Expand Up @@ -50,6 +52,7 @@ import { Tokens } from './Tokens';
import { mapClass, mapValue } from './utils';

mapClass(Tokens.AdminDataSource, AdminDataSourceMock);
mapClass(Tokens.CoProposerInviteDataSource, CoProposerInviteDataSourceMock);
mapClass(Tokens.CallDataSource, CallDataSourceMock);
mapClass(Tokens.EventLogsDataSource, EventLogsDataSourceMock);
mapClass(Tokens.FeedbackDataSource, FeedbackDataSourceMock);
Expand All @@ -58,6 +61,7 @@ mapClass(Tokens.GenericTemplateDataSource, GenericTemplateDataSourceMock);
mapClass(Tokens.InstrumentDataSource, InstrumentDataSourceMock);
mapClass(Tokens.InviteCodeDataSource, InviteCodesDataSourceMock);
mapClass(Tokens.RoleInviteDataSource, RoleInviteDataSourceMock);
mapClass(Tokens.InviteAuthorization, InviteAuthorization);
mapClass(Tokens.InternalReviewDataSource, InternalReviewDataSourceMock);
mapClass(Tokens.PdfTemplateDataSource, PdfTemplateDataSourceMock);
mapClass(Tokens.ProposalDataSource, ProposalDataSourceMock);
Expand Down
6 changes: 6 additions & 0 deletions apps/backend/src/datasources/CoProposerInviteDataSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { CoProposerInvite } from '../models/CoProposerInvite';

export interface CoProposerInviteDataSource {
create(inviteCodeId: number, proposalPk: number): Promise<CoProposerInvite>;
findByInviteCodeId(inviteCodeId: number): Promise<CoProposerInvite | null>;
}
3 changes: 2 additions & 1 deletion apps/backend/src/datasources/ProposalDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export interface ProposalDataSource {
updateProposalTechnicalReviewer(
args: UpdateTechnicalReviewAssigneeInput
): Promise<TechnicalReview[]>;
setProposalUsers(proposalPk: number, users: number[]): Promise<void>;
setProposalUsers(proposalPk: number, usersIds: number[]): Promise<void>;
addProposalUser(proposalPk: number, userId: number): Promise<void>;
submitProposal(
primaryKey: number,
referenceNumber?: string
Expand Down
36 changes: 36 additions & 0 deletions apps/backend/src/datasources/mockups/CoProposerInviteDataSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { CoProposerInvite } from '../../models/CoProposerInvite';
import { CoProposerInviteDataSource } from '../CoProposerInviteDataSource';

export class CoProposerInviteDataSourceMock
implements CoProposerInviteDataSource
{
private invites: CoProposerInvite[] = [];

init() {
this.invites = [
new CoProposerInvite(1, 1),
new CoProposerInvite(2, 2),
new CoProposerInvite(3, 3),
];
}

async findByInviteCodeId(
inviteCodeId: number
): Promise<CoProposerInvite | null> {
return (
this.invites.find((invite) => invite.inviteCodeId === inviteCodeId) ||
null
);
}

async create(
inviteCodeId: number,
proposalPk: number
): Promise<CoProposerInvite> {
const newInvite = new CoProposerInvite(inviteCodeId, proposalPk);

this.invites.push(newInvite);

return newInvite;
}
}
Loading

0 comments on commit 657061c

Please sign in to comment.