diff --git a/README.md b/README.md index f3585d1..48528ab 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,4 @@ To be able to start the connector locally you will need to: 1. Install all needed dependencies with `npm install` 2. Copy the missing matrix-js dependency `npm run fix-olm-dependency` -3. Start the application using `npm run start` +3. Start the application using the command `npm run start` diff --git a/sample.env b/sample.env index ba3de4e..c30f41c 100644 --- a/sample.env +++ b/sample.env @@ -17,6 +17,8 @@ SCICAT_LOGIN_ENDPOINT=/Users/login SCICAT_USERNAME= SCICAT_PASSWORD= SCICAT_PROPOSAL_TRIGGERING_STATUSES="SCHEDULING, ALLOCATED" +SCICAT_EXPERIMENT_TRIGGERING_STATUSES="ACTIVE" +SCICAT_JWT= PROPOSAL_FOLDERS_CREATION_GROUP_PREFIX="" PROPOSAL_FOLDERS_CREATION_TRIGGERING_STATUSES="PROPOSAL_ACCEPTED" @@ -36,6 +38,7 @@ SYNAPSE_OAUTH_ISSUER=https://server-scichat.swap.ess.eu ## The following variables enable the different functionalities available in the connector ## true or false ENABLE_SCICAT_PROPOSAL_UPSERT=false +ENABLE_SCICAT_EXPERIMENT_UPSERT=false ENABLE_SCICHAT_ROOM_CREATION=false ENABLE_NICOS_TO_SCICHAT_MESSAGES=false ENABLE_PROPOSAL_FOLDERS_CREATION=false @@ -59,4 +62,4 @@ ONE_IDENTITY_API_PASSWORD= VISA_QUEUE_NAME=dummy VISA_SYNCING_TRIGGERING_STATUSES="ALLOCATED" -VISA_DATABASE_URL=postgres://postgres:qwerty123@127.0.0.1:5434/visa \ No newline at end of file +VISA_DATABASE_URL=postgres://postgres:qwerty123@127.0.0.1:5434/visa diff --git a/src/queue/consumers/scicat/scicatProposal/consumers/ExperimentCreationQueueConsumer.ts b/src/queue/consumers/scicat/scicatProposal/consumers/ExperimentCreationQueueConsumer.ts index 118dfbe..95888dc 100644 --- a/src/queue/consumers/scicat/scicatProposal/consumers/ExperimentCreationQueueConsumer.ts +++ b/src/queue/consumers/scicat/scicatProposal/consumers/ExperimentCreationQueueConsumer.ts @@ -22,7 +22,7 @@ export class ExperimentCreationQueueConsumer extends QueueConsumer { } getExchangeName(): string { - return process.env.USER_OFFICE_CORE_EXCHANGE_NAME as string; + return process.env.EXPERIMENT_CREATION_EXCHANGE_NAME as string; } onMessage: ConsumerCallback = async (type, message) => { diff --git a/src/queue/consumers/scicat/scicatProposal/mappers/__snapshots__/uoToScicatExperiment.mapper.spec.ts.snap b/src/queue/consumers/scicat/scicatProposal/mappers/__snapshots__/uoToScicatExperiment.mapper.spec.ts.snap index da2fb08..d7008eb 100644 --- a/src/queue/consumers/scicat/scicatProposal/mappers/__snapshots__/uoToScicatExperiment.mapper.spec.ts.snap +++ b/src/queue/consumers/scicat/scicatProposal/mappers/__snapshots__/uoToScicatExperiment.mapper.spec.ts.snap @@ -20,10 +20,42 @@ Test Abstract", "human_name": "Instrument Name", "value": "YMIR", }, + "local_contact_email": { + "human_name": "Local contact email", + "value": "junjie.quan@ess.eu", + }, + "local_contact_firstname": { + "human_name": "Local contact name", + "value": "Junjie", + }, + "local_contact_lastname": { + "human_name": "Local contact surname", + "value": "Quan", + }, "number_of_visitors": { "human_name": "Number of Visitors", "value": 1, }, + "pi_affiliation": { + "human_name": "PI Affiliation", + "value": "ESS", + }, + "pi_email": { + "human_name": "PI Email", + "value": "john.doe@example.com", + }, + "pi_firstname": { + "human_name": "PI First Name", + "value": "John", + }, + "pi_lastname": { + "human_name": "PI Last Name", + "value": "Doe", + }, + "pi_orcid": { + "human_name": "PI ORCID", + "value": "0000-0001-2345-6789", + }, "status_experiment": { "human_name": "UOS Experiment Status", "value": "ACTIVE", @@ -32,6 +64,10 @@ Test Abstract", "human_name": "UOS Proposal Status", "value": "ALLOCATED", }, + "visitor_1_affiliation": { + "human_name": "Visitor 1 Affiliation", + "value": "ESS", + }, "visitor_1_email": { "human_name": "Visitor 1 Email", "value": "test@example.com", @@ -81,10 +117,42 @@ Test Abstract", "human_name": "Instrument Name", "value": "YMIR", }, + "local_contact_email": { + "human_name": "Local contact email", + "value": "junjie.quan@ess.eu", + }, + "local_contact_firstname": { + "human_name": "Local contact name", + "value": "Junjie", + }, + "local_contact_lastname": { + "human_name": "Local contact surname", + "value": "Quan", + }, "number_of_visitors": { "human_name": "Number of Visitors", "value": 0, }, + "pi_affiliation": { + "human_name": "PI Affiliation", + "value": "ESS", + }, + "pi_email": { + "human_name": "PI Email", + "value": "john.doe@example.com", + }, + "pi_firstname": { + "human_name": "PI First Name", + "value": "John", + }, + "pi_lastname": { + "human_name": "PI Last Name", + "value": "Doe", + }, + "pi_orcid": { + "human_name": "PI ORCID", + "value": "0000-0001-2345-6789", + }, "status_experiment": { "human_name": "UOS Experiment Status", "value": "ACTIVE", @@ -126,10 +194,42 @@ Test Abstract", "human_name": "Instrument Name", "value": "YMIR", }, + "local_contact_email": { + "human_name": "Local contact email", + "value": "junjie.quan@ess.eu", + }, + "local_contact_firstname": { + "human_name": "Local contact name", + "value": "Junjie", + }, + "local_contact_lastname": { + "human_name": "Local contact surname", + "value": "Quan", + }, "number_of_visitors": { "human_name": "Number of Visitors", "value": 0, }, + "pi_affiliation": { + "human_name": "PI Affiliation", + "value": "ESS", + }, + "pi_email": { + "human_name": "PI Email", + "value": "john.doe@example.com", + }, + "pi_firstname": { + "human_name": "PI First Name", + "value": "John", + }, + "pi_lastname": { + "human_name": "PI Last Name", + "value": "Doe", + }, + "pi_orcid": { + "human_name": "PI ORCID", + "value": "0000-0001-2345-6789", + }, "status_experiment": { "human_name": "UOS Experiment Status", "value": "ACTIVE", diff --git a/src/queue/consumers/scicat/scicatProposal/mappers/__snapshots__/uoToScicatProposal.mapper.spec.ts.snap b/src/queue/consumers/scicat/scicatProposal/mappers/__snapshots__/uoToScicatProposal.mapper.spec.ts.snap index dce6907..06dd4d3 100644 --- a/src/queue/consumers/scicat/scicatProposal/mappers/__snapshots__/uoToScicatProposal.mapper.spec.ts.snap +++ b/src/queue/consumers/scicat/scicatProposal/mappers/__snapshots__/uoToScicatProposal.mapper.spec.ts.snap @@ -105,6 +105,10 @@ exports[`getCreateScicatProposalDto includes co-PI, DAU and instrument metadata "human_name": "Number of Instruments", "value": 1, }, + "pi_affiliation": { + "human_name": "PI Affiliation", + "value": "ESS", + }, "pi_email": { "human_name": "PI Email", "value": "john.doe@example.com", @@ -178,6 +182,10 @@ exports[`getCreateScicatProposalDto maps proposal to create DTO correctly 1`] = "human_name": "Number of Instruments", "value": 0, }, + "pi_affiliation": { + "human_name": "PI Affiliation", + "value": "ESS", + }, "pi_email": { "human_name": "PI Email", "value": "john.doe@example.com", @@ -251,6 +259,10 @@ exports[`getUpdateScicatProposalDto maps proposal to update DTO correctly 1`] = "human_name": "Number of Instruments", "value": 0, }, + "pi_affiliation": { + "human_name": "PI Affiliation", + "value": "ESS", + }, "pi_email": { "human_name": "PI Email", "value": "john.doe@example.com", diff --git a/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatExperiment.mapper.spec.ts b/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatExperiment.mapper.spec.ts index 118e7f3..90026d1 100644 --- a/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatExperiment.mapper.spec.ts +++ b/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatExperiment.mapper.spec.ts @@ -37,6 +37,14 @@ const createBaseUoExperiment = ( }, }, visit: null, + localContact: { + id: 8, + firstname: 'Junjie', + lastname: 'Quan', + email: 'junjie.quan@ess.eu', + oidcSub: 'junjiequan', + institution: 'Other', + }, ...overrides, }); @@ -77,6 +85,58 @@ describe('getCreateScicatExperimentDto', () => { expect(dto).toMatchSnapshot(); }); + it('maps the proposer institution to the pi_affiliation metadata entry', () => { + const dto = getCreateScicatExperimentDto( + createBaseUoExperiment(), + instrumentIds + ); + + expect(dto.metadata?.['pi_affiliation']).toEqual({ + human_name: 'PI Affiliation', + value: 'ESS', + }); + }); + + it('leaves the pi_affiliation value undefined when the proposer has no institution', () => { + const experiment = createBaseUoExperiment(); + delete experiment.proposal.proposer.institution; + + const dto = getCreateScicatExperimentDto(experiment, instrumentIds); + + expect(dto.metadata?.['pi_affiliation']).toEqual({ + human_name: 'PI Affiliation', + value: undefined, + }); + }); + + it('maps the local contact to metadata entries', () => { + const dto = getCreateScicatExperimentDto( + createBaseUoExperiment(), + instrumentIds + ); + + expect(dto.metadata?.['local_contact_firstname']).toEqual({ + human_name: 'Local contact name', + value: 'Junjie', + }); + expect(dto.metadata?.['local_contact_email']).toEqual({ + human_name: 'Local contact email', + value: 'junjie.quan@ess.eu', + }); + }); + + it('falls back to null local contact entries when no local contact is assigned', () => { + const dto = getCreateScicatExperimentDto( + createBaseUoExperiment({ localContact: null }), + instrumentIds + ); + + expect(dto.metadata?.['local_contact_firstname']).toEqual({ + human_name: 'Local contact name', + value: null, + }); + }); + it('skips visitor entries when user is null', () => { const dto = getCreateScicatExperimentDto( createBaseUoExperiment({ @@ -101,6 +161,18 @@ describe('getUpdateScicatExperimentDto', () => { expect(dto).toMatchSnapshot(); }); + it('maps the proposer institution to the pi_affiliation metadata entry', () => { + const dto = getUpdateScicatExperimentDto( + createBaseUoExperiment(), + instrumentIds + ); + + expect(dto.metadata?.['pi_affiliation']).toEqual({ + human_name: 'PI Affiliation', + value: 'ESS', + }); + }); + it('should not include proposalId', () => { const dto = getUpdateScicatExperimentDto( createBaseUoExperiment(), diff --git a/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatExperiment.mapper.ts b/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatExperiment.mapper.ts index 673ed3c..61c9cc1 100644 --- a/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatExperiment.mapper.ts +++ b/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatExperiment.mapper.ts @@ -19,7 +19,8 @@ import { scicatApi } from '../utils/scicatApi'; // ── Experiment ──────────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────────────────────── const buildMetadata = (experiment: UOExperimentDto): MdEntry => { - const { proposal, instrument, visit } = experiment; + const { proposal, instrument, visit, localContact } = experiment; + const { proposer } = proposal; const registrations = visit?.registrations ?? []; const rows: [string, MdEntryValue][] = [ @@ -33,7 +34,27 @@ const buildMetadata = (experiment: UOExperimentDto): MdEntry => { 'UOS Experiment Status', experiment.status ), + metadataEntry('pi_firstname', 'PI First Name', proposer.firstname), + metadataEntry('pi_lastname', 'PI Last Name', proposer.lastname), + metadataEntry('pi_email', 'PI Email', proposer.email), + metadataEntry('pi_orcid', 'PI ORCID', proposer.oidcSub), + metadataEntry('pi_affiliation', 'PI Affiliation', proposer.institution), metadataEntry('instrument_name', 'Instrument Name', instrument.name), + metadataEntry( + 'local_contact_firstname', + 'Local contact name', + localContact?.firstname || null + ), + metadataEntry( + 'local_contact_lastname', + 'Local contact surname', + localContact?.lastname || null + ), + metadataEntry( + 'local_contact_email', + 'Local contact email', + localContact?.email || null + ), metadataEntry( 'number_of_visitors', 'Number of Visitors', @@ -64,6 +85,11 @@ const buildMetadata = (experiment: UOExperimentDto): MdEntry => { `visitor_${i}_orcid`, `Visitor ${i} ORCID`, registration.user.oidcSub + ), + metadataEntry( + `visitor_${i}_affiliation`, + `Visitor ${i} Affiliation`, + registration.user.institution ) ); }); diff --git a/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatProposal.mapper.ts b/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatProposal.mapper.ts index 186d818..37a8375 100644 --- a/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatProposal.mapper.ts +++ b/src/queue/consumers/scicat/scicatProposal/mappers/uoToScicatProposal.mapper.ts @@ -23,6 +23,7 @@ const buildMetadata = (proposal: UOProposalDto): MdEntry => { metadataEntry('pi_lastname', 'PI Last Name', proposer.lastname), metadataEntry('pi_email', 'PI Email', proposer.email), metadataEntry('pi_orcid', 'PI ORCID', proposer.oidcSub), + metadataEntry('pi_affiliation', 'PI Affiliation', proposer.institution), metadataEntry('number_of_co_is', 'Number of CoIs', users.length), metadataEntry( 'number_of_dau', diff --git a/src/services/userOfficeApi/queries/getExperiment.query.ts b/src/services/userOfficeApi/queries/getExperiment.query.ts index 3a3a62f..746a57c 100644 --- a/src/services/userOfficeApi/queries/getExperiment.query.ts +++ b/src/services/userOfficeApi/queries/getExperiment.query.ts @@ -30,6 +30,9 @@ fragment experiment on Experiment { proposalPk status localContactId + localContact { + ...basicUserDetails + } instrumentId createdAt updatedAt diff --git a/src/services/userOfficeApi/type/uoExperiment.type.ts b/src/services/userOfficeApi/type/uoExperiment.type.ts index 9c4e3a1..1fd85d2 100644 --- a/src/services/userOfficeApi/type/uoExperiment.type.ts +++ b/src/services/userOfficeApi/type/uoExperiment.type.ts @@ -32,6 +32,7 @@ export interface UOExperimentDto { UOProposalDto, 'proposalId' | 'title' | 'abstract' | 'status' | 'proposer' >; + localContact?: UOUser | null; visit?: { registrations: { status: string;