Skip to content
Merged
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
80 changes: 63 additions & 17 deletions apps/backend/src/datasources/postgres/ProposalDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ProposalDataSource } from '../ProposalDataSource';
import { TagDataSource } from '../TagDataSource';
import { WorkflowDataSource } from '../WorkflowDataSource';
import {
InstrumentFilterInput,
ProposalsFilter,
QuestionFilterInput,
} from './../../resolvers/queries/ProposalsQuery';
Expand Down Expand Up @@ -94,6 +95,29 @@ export async function calculateReferenceNumber(
return prefix + paddedSequence;
}

/**
* Resolves instrument IDs from an InstrumentFilterInput.
* Supports both `instrumentIds` and `instrumentId`(deprecated).
*/
export function resolveInstrumentIds(
instrumentFilter?: InstrumentFilterInput
): number[] | undefined {
if (!instrumentFilter) {
return undefined;
}
if (
instrumentFilter.instrumentIds &&
instrumentFilter.instrumentIds.length > 0
) {
return instrumentFilter.instrumentIds;
}
if (instrumentFilter.instrumentId) {
return [instrumentFilter.instrumentId];
}

return undefined;
}

@injectable()
export default class PostgresProposalDataSource implements ProposalDataSource {
constructor(
Expand Down Expand Up @@ -497,11 +521,20 @@ export default class PostgresProposalDataSource implements ProposalDataSource {

if (filter?.instrumentFilter?.showMultiInstrumentProposals) {
query.whereRaw('jsonb_array_length(instruments) > 1');
} else if (filter?.instrumentFilter?.instrumentId) {
query.whereRaw(
'jsonb_path_exists(instruments, \'$[*].id \\? (@.type() == "number" && @ == :instrumentId:)\')',
{ instrumentId: filter.instrumentFilter.instrumentId }
} else {
const effectiveInstrumentIds = resolveInstrumentIds(
filter?.instrumentFilter
);
if (effectiveInstrumentIds && effectiveInstrumentIds.length > 0) {
query.where(function () {
effectiveInstrumentIds.forEach((id) => {
this.orWhereRaw(
'jsonb_path_exists(instruments, \'$[*].id \\? (@.type() == "number" && @ == :instrumentId:)\')',
{ instrumentId: id }
);
});
});
}
}

if (filter?.proposalStatusId) {
Expand Down Expand Up @@ -655,16 +688,19 @@ export default class PostgresProposalDataSource implements ProposalDataSource {
);
}

if (filter?.instrumentFilter?.instrumentId) {
const effectiveInstrumentIds = resolveInstrumentIds(
filter?.instrumentFilter
);
if (effectiveInstrumentIds && effectiveInstrumentIds.length > 0) {
query
.leftJoin(
'instrument_has_proposals',
'instrument_has_proposals.proposal_pk',
'proposals.proposal_pk'
)
.where(
.whereIn(
'instrument_has_proposals.instrument_id',
filter.instrumentFilter.instrumentId
effectiveInstrumentIds
);
}

Expand Down Expand Up @@ -814,12 +850,21 @@ export default class PostgresProposalDataSource implements ProposalDataSource {

if (filter?.instrumentFilter?.showMultiInstrumentProposals) {
query.whereRaw('jsonb_array_length(instruments) > 1');
} else if (filter?.instrumentFilter?.instrumentId) {
// NOTE: Using jsonpath we check the jsonb (instruments) field if it contains object with id equal to filter.instrumentId
query.whereRaw(
'jsonb_path_exists(instruments, \'$[*].id \\? (@.type() == "number" && @ == :instrumentId:)\')',
{ instrumentId: filter.instrumentFilter?.instrumentId }
} else {
const effectiveInstrumentIds = resolveInstrumentIds(
filter?.instrumentFilter
);
if (effectiveInstrumentIds && effectiveInstrumentIds.length > 0) {
// NOTE: Using jsonpath we check the jsonb (instruments) field if it contains object with id equal to filter.instrumentId
query.where(function () {
effectiveInstrumentIds.forEach((id) => {
this.orWhereRaw(
'jsonb_path_exists(instruments, \'$[*].id \\? (@.type() == "number" && @ == :instrumentId:)\')',
{ instrumentId: id }
);
});
});
}
}

if (filter?.proposalStatusId) {
Expand Down Expand Up @@ -1143,14 +1188,15 @@ export default class PostgresProposalDataSource implements ProposalDataSource {
'ins.instrument_id'
)
.modify((query) => {
const instrumentId = filter?.instrumentFilter?.instrumentId;
const effectiveInstrumentIds = resolveInstrumentIds(
filter?.instrumentFilter
);

if (instrumentId && !isNaN(instrumentId)) {
if (effectiveInstrumentIds && effectiveInstrumentIds.length > 0) {
query.join('instrument_has_proposals as ihp', function () {
this.on('ihp.proposal_pk', '=', 'proposals.proposal_pk').andOnVal(
this.on('ihp.proposal_pk', '=', 'proposals.proposal_pk').andOnIn(
'ihp.instrument_id',
'=',
instrumentId
effectiveInstrumentIds
);
});
}
Expand Down
26 changes: 19 additions & 7 deletions apps/backend/src/datasources/stfc/StfcProposalDataSource.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { container, injectable } from 'tsyringe';

import { StfcUserDataSource } from './StfcUserDataSource';
import { Tokens } from '../../config/Tokens';
import { Call } from '../../models/Call';
import { Proposal } from '../../models/Proposal';
Expand All @@ -13,6 +14,9 @@ import { PaginationSortDirection } from '../../utils/pagination';
import PostgresAdminDataSource from '../postgres/AdminDataSource';
import PostgresCallDataSource from '../postgres/CallDataSource';
import database from '../postgres/database';
import PostgresProposalDataSource, {
resolveInstrumentIds,
} from '../postgres/ProposalDataSource';
import {
CallRecord,
createCallObject,
Expand All @@ -24,8 +28,6 @@ import PostgresTagDataSource from '../postgres/TagDataSource';
import PostgresUserDataSource from '../postgres/UserDataSource';
import PostgresWorkflowDataSource from '../postgres/WorkflowDataSource';
import { ProposalsFilter } from './../../resolvers/queries/ProposalsQuery';
import PostgresProposalDataSource from './../postgres/ProposalDataSource';
import { StfcUserDataSource } from './StfcUserDataSource';

const postgresProposalDataSource = new PostgresProposalDataSource(
new PostgresWorkflowDataSource(new PostgresStatusDataSource()),
Expand Down Expand Up @@ -163,12 +165,22 @@ export default class StfcProposalDataSource extends PostgresProposalDataSource {
}
if (filter?.instrumentFilter?.showMultiInstrumentProposals) {
query.whereRaw('jsonb_array_length(instruments) > 1');
} else if (filter?.instrumentFilter?.instrumentId) {
// NOTE: Using jsonpath we check the jsonb (instruments) field if it contains object with id equal to filter.instrumentId
query.whereRaw(
'jsonb_path_exists(instruments, \'$[*].id \\? (@.type() == "number" && @ == :instrumentId:)\')',
{ instrumentId: filter?.instrumentFilter?.instrumentId }
} else {
const effectiveInstrumentIds = resolveInstrumentIds(
filter?.instrumentFilter
);

if (effectiveInstrumentIds && effectiveInstrumentIds.length > 0) {
// NOTE: Using jsonpath we check the jsonb (instruments) field if it contains object with id equal to filter.instrumentId
query.where(function () {
effectiveInstrumentIds.forEach((id) => {
this.orWhereRaw(
'jsonb_path_exists(instruments, \'$[*].id \\? (@.type() == "number" && @ == :instrumentId:)\')',
{ instrumentId: id }
);
});
});
}
}

if (filter?.proposalStatusId) {
Expand Down
11 changes: 9 additions & 2 deletions apps/backend/src/resolvers/queries/ProposalsQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,15 @@ export class QuestionFilterInput {

@InputType()
export class InstrumentFilterInput {
@Field(() => Int, { nullable: true })
public instrumentId: number;
/** @deprecated Use instrumentIds instead */
@Field(() => Int, {
nullable: true,
deprecationReason: 'Use instrumentIds instead',
})
public instrumentId?: number;

@Field(() => [Int], { nullable: true })
public instrumentIds?: number[];

@Field(() => Boolean)
public showMultiInstrumentProposals: boolean;
Expand Down
1 change: 1 addition & 0 deletions apps/e2e/cypress/e2e/FAPs.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ context('Fap reviews tests', () => {

cy.get('[data-cy=instrument-filter]').click();
cy.get('[role=presentation]').contains(instrument.name).click();
cy.get('body').type('{esc}');

cy.get('[data-cy="fap-assignments-table"]').contains(instrument.name);
cy.get('[data-cy="fap-assignments-table"]').contains(
Expand Down
Loading
Loading