Skip to content

Commit 4b276dd

Browse files
committed
Merge branch 'develop' of github.com:UserOfficeProject/user-office-core into SWAP-5022-uo-co-proposer-email-lookup-fails-when-input-cont
2 parents 090aa66 + 72e676a commit 4b276dd

8 files changed

Lines changed: 355 additions & 101 deletions

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Rebase PRs with develop
2+
3+
on:
4+
push:
5+
branches:
6+
- develop
7+
8+
jobs:
9+
sync-prs:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 0
17+
18+
- name: Rebase all open non-Dependabots PRs with develop branch as base
19+
env:
20+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21+
run: |
22+
# Get all open PRs targeting develop, excluding Dependabot
23+
prs=$(gh pr list --state open --base develop --limit 100 --json number,author --jq '.[] | select(.author.is_bot != true) | .number')
24+
25+
for pr in $prs; do
26+
echo "Processing PR #$pr"
27+
gh pr checkout $pr
28+
git fetch origin develop
29+
30+
# Attempt rebase
31+
if git rebase origin/develop; then
32+
echo "PR #$pr rebased successfully. Pushing changes..."
33+
git push --force-with-lease
34+
else
35+
echo "Conflict in PR #$pr. Rebase aborted."
36+
git rebase --abort
37+
# Optional: Notify author
38+
gh pr comment $pr --body "⚠️ Automatic rebase failed due to conflicts. Please rebase manually."
39+
fi
40+
done

apps/backend/src/eventHandlers/email/essEmailHandler.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,42 @@ describe('essEmailHandler co-proposer invites', () => {
277277
);
278278
});
279279

280+
it('should send mail when co-proposer invite is accepted', async () => {
281+
const mockInvite = new Invite(
282+
1,
283+
faker.string.alphanumeric(8),
284+
dummyUser.email,
285+
new Date(),
286+
dummyUser.id,
287+
new Date(),
288+
dummyUser.id,
289+
false,
290+
null,
291+
EmailTemplateId.CO_PROPOSER_INVITE_ACCEPTED
292+
);
293+
294+
// Mock userDataSource.getUser to return dummyUser for principal investigator but null for claimer
295+
const getUserMock = jest.spyOn(userDataSourceMock, 'getUser');
296+
getUserMock
297+
.mockResolvedValueOnce(dummyUser) // First call for principal investigator
298+
.mockResolvedValueOnce(dummyUser); // Second call for claimer
299+
300+
const event: ApplicationEvent = {
301+
type: Event.PROPOSAL_CO_PROPOSER_INVITE_ACCEPTED,
302+
invite: mockInvite,
303+
key: 'invite',
304+
loggedInUserId: 3,
305+
isRejection: false,
306+
proposalPKey: 1,
307+
};
308+
309+
const sendMailsSpy = jest.spyOn(mockMailService, 'sendMail');
310+
311+
await essEmailHandler(event);
312+
313+
expect(sendMailsSpy).toHaveBeenCalled();
314+
});
315+
280316
describe('handling PROPOSAL_SUBMITTED event', () => {
281317
it('Should have PI and CoProposals in the payload', async () => {
282318
const event: ApplicationEvent = {

apps/backend/src/eventHandlers/email/essEmailHandler.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,6 @@ export async function essEmailHandler(event: ApplicationEvent) {
4747
Tokens.CoProposerClaimDataSource
4848
);
4949

50-
const inviteDataSource = container.resolve<InviteDataSource>(
51-
Tokens.InviteDataSource
52-
);
53-
5450
const callDataSource = container.resolve<CallDataSource>(
5551
Tokens.CallDataSource
5652
);

apps/backend/src/mutations/InviteMutations.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,16 @@ export default class InviteMutations {
6969
return rejection('Invite code has expired', { invite: code });
7070
}
7171

72-
await this.processAcceptedRoleClaims(agent!.id, invite);
73-
await this.processAcceptedCoProposerClaims(agent!.id, invite);
74-
await this.processAcceptedVisitRegistrationClaims(agent!.id, invite);
75-
7672
const updatedInvite = await this.inviteDataSource.update({
7773
id: invite.id,
7874
claimedAt: new Date(),
7975
claimedByUserId: agent!.id,
8076
});
8177

78+
await this.processAcceptedRoleClaims(agent!.id, updatedInvite);
79+
await this.processAcceptedCoProposerClaims(agent!.id, updatedInvite);
80+
await this.processAcceptedVisitRegistrationClaims(agent!.id, updatedInvite);
81+
8282
return updatedInvite;
8383
}
8484

@@ -282,7 +282,6 @@ export default class InviteMutations {
282282
if (proposalHasUser) {
283283
return;
284284
}
285-
286285
await this.proposalDataSource.addProposalUser(
287286
claim.proposalPk,
288287
claimerUserId

apps/backend/src/workflowEngine/experiment.ts

Lines changed: 64 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const getExperimentWorkflowByCallId = (callId: number) => {
2222

2323
export const getWorkflowConnectionByStatusId = (
2424
workflowId: number,
25-
statusId: number,
25+
statusId?: number,
2626
prevStatusId?: number
2727
) => {
2828
const workflowDataSource = container.resolve<WorkflowDataSource>(
@@ -45,7 +45,6 @@ const shouldMoveToNextStatus = (
4545
experimentSafetyEventsKey as keyof ExperimentSafetyEventsRecord
4646
]
4747
);
48-
4948
const allNextStatusRulesFulfilled = !statusChangingEvents.some(
5049
(statusChangingEvent) =>
5150
allExperimentIncompleteEvents.indexOf(
@@ -203,78 +202,78 @@ export const workflowEngine = async (
203202
* We store one record of FEASIBILITY_REVIEW with nextStatusId = FAP_SELECTION and another one with nextStatusId = NOT_FEASIBLE.
204203
* We go through each record and based on the currentEvent we move the proposal into the right direction
205204
*/
206-
return Promise.all(
205+
const response = await Promise.all(
207206
currentWorkflowConnections.map(async (currentWorkflowConnection) => {
208-
if (!currentWorkflowConnection.nextStatusId) {
209-
return;
210-
}
211-
212-
if (!experimentSafetyWithEvents.experimentSafetyEvents) {
213-
return;
214-
}
215-
216207
const nextWorkflowConnections =
217208
await getWorkflowConnectionByStatusId(
218209
experimentWorkflow.id,
219-
currentWorkflowConnection.nextStatusId,
210+
undefined,
220211
currentWorkflowConnection.statusId
221212
);
222-
if (!nextWorkflowConnections?.length) {
223-
return;
224-
}
225213

226-
const workflowDataSource = container.resolve<WorkflowDataSource>(
227-
Tokens.WorkflowDataSource
214+
return Promise.all(
215+
nextWorkflowConnections.map(async (nextWorkflowConnection) => {
216+
if (!experimentSafetyWithEvents.experimentSafetyEvents) {
217+
return;
218+
}
219+
const workflowDataSource =
220+
container.resolve<WorkflowDataSource>(
221+
Tokens.WorkflowDataSource
222+
);
223+
224+
const statusChangingEvents =
225+
await workflowDataSource.getStatusChangingEventsByConnectionIds(
226+
[nextWorkflowConnection.id]
227+
);
228+
if (!statusChangingEvents) {
229+
return;
230+
}
231+
232+
const eventThatTriggeredStatusChangeIsStatusChangingEvent =
233+
statusChangingEvents.find(
234+
(statusChangingEvent) =>
235+
experimentSafetyWithEvents.currentEvent ===
236+
statusChangingEvent.statusChangingEvent
237+
);
238+
239+
if (!eventThatTriggeredStatusChangeIsStatusChangingEvent) {
240+
return;
241+
}
242+
243+
if (
244+
shouldMoveToNextStatus(
245+
statusChangingEvents,
246+
experimentSafetyWithEvents.experimentSafetyEvents
247+
)
248+
) {
249+
const updatedExperimentSafety =
250+
await experimentDataSource.updateExperimentSafetyStatus(
251+
experimentSafety.experimentSafetyPk,
252+
nextWorkflowConnection.statusId
253+
);
254+
255+
if (updatedExperimentSafety) {
256+
await checkIfConditionsForNextStatusAreMet({
257+
nextWorkflowConnections,
258+
experimentWorkflow,
259+
workflowDataSource,
260+
experimentSafetyWithEvents,
261+
});
262+
263+
return {
264+
...updatedExperimentSafety,
265+
workflowId: experimentWorkflow.id,
266+
prevStatusId: currentWorkflowConnection.statusId,
267+
callShortCode: call.shortCode,
268+
};
269+
}
270+
}
271+
})
228272
);
229-
230-
const statusChangingEvents =
231-
await workflowDataSource.getStatusChangingEventsByConnectionIds(
232-
nextWorkflowConnections.map((connection) => connection.id)
233-
);
234-
if (!statusChangingEvents) {
235-
return;
236-
}
237-
238-
const eventThatTriggeredStatusChangeIsStatusChangingEvent =
239-
statusChangingEvents.find(
240-
(statusChangingEvent) =>
241-
experimentSafetyWithEvents.currentEvent ===
242-
statusChangingEvent.statusChangingEvent
243-
);
244-
if (!eventThatTriggeredStatusChangeIsStatusChangingEvent) {
245-
return;
246-
}
247-
248-
if (
249-
shouldMoveToNextStatus(
250-
statusChangingEvents,
251-
experimentSafetyWithEvents.experimentSafetyEvents
252-
)
253-
) {
254-
const updatedExperimentSafety =
255-
await experimentDataSource.updateExperimentSafetyStatus(
256-
experimentSafety.experimentSafetyPk,
257-
currentWorkflowConnection.nextStatusId
258-
);
259-
260-
if (updatedExperimentSafety) {
261-
await checkIfConditionsForNextStatusAreMet({
262-
nextWorkflowConnections,
263-
experimentWorkflow,
264-
workflowDataSource,
265-
experimentSafetyWithEvents,
266-
});
267-
268-
return {
269-
...updatedExperimentSafety,
270-
workflowId: experimentWorkflow.id,
271-
prevStatusId: currentWorkflowConnection.statusId,
272-
callShortCode: call.shortCode,
273-
};
274-
}
275-
}
276273
})
277-
);
274+
).then((results) => results.flat());
275+
276+
return response;
278277
})
279278
)
280279
).flat();

0 commit comments

Comments
 (0)