feat: allow inviting Data Access Users to Proposal with email - #1615
feat: allow inviting Data Access Users to Proposal with email#1615shivoomiess wants to merge 51 commits into
Conversation
yoganandaness
left a comment
There was a problem hiding this comment.
@shivoomiess Kindly attach relevant video or screenshot
yoganandaness
left a comment
There was a problem hiding this comment.
- kindly test all pieces of change
- Attach video or pic if possible
- Pls have clean diff
jekabs-karklins
left a comment
There was a problem hiding this comment.
Over all looks very good :raisedhands:
As this is a bigger PR I think you will get a bit more comments :).
I have checked the PR and left some comments, and Happy to chat if any help or clarification needed.
shivoomiess
left a comment
There was a problem hiding this comment.
incorporated feedback
The invites.cy.ts test "Should be able to view and accept the outstanding invites with the proposal information" had failed on every CI run of this branch since 30 June. After clicking accept, the proposal table still read "No records to display" where the test expected the accepted proposal, so it only ever failed after a 30s retry, which made it look like timing noise rather than a defect. The accept helpers fired the mutation and returned immediately, so the caller ran onAccept() while the request was still in flight. The proposal table refetched before the invite had been accepted and came back empty, which is what the e2e test saw. The success snackbar had the same problem, and the .catch() that rethrew inside the promise chain surfaced as an unhandled rejection rather than reaching the component, so a failed acceptance still reported success. That is why nothing in the UI hinted at the real cause. Return the promises and await them at the call sites.
- Renumber the data access claims patch to 0214, as 0213 is taken - Block the data access users modal until the proposal has loaded, and reset the loading flag when the proposal request fails so the modal cannot get stuck on "Loading..."
setDataAccessInvites rebuilds the invite list by diffing the emails the frontend submits against the invites already attached to the proposal. That set included claimed and expired invites, while the read path (Proposal.dataAccessInvites) only returns pending ones, so an accepted invite was never in the submitted list and got deleted on the next update. Take the existing set from the invite data source filtered on isClaimed: false, which is the same set the read path returns. Both halves of the diff now operate on the same invites, so claimed invites are left alone and pending ones stay deletable.
setCoProposerInvites has the same defect as setDataAccessInvites did: it diffed the submitted emails against every invite claimed for the proposal, claimed ones included, while Proposal.coProposerInvites only returns pending invites. An accepted co-proposer invite was therefore absent from the submitted list and deleted on the next update. Take the existing set from the invite data source filtered on isClaimed: false, matching the read path, so both halves of the diff see the same invites.
…n B) The proposal-scoped lookups moved to InviteDataSource.getXInvites, so findByProposalPk has no production caller left. Remove it from both claim data source interfaces and their postgres implementations, and keep it on the mocks only. InviteDataSourceMock held its own copy of the claim rows for the getXInvites proposalPk filter while findXInvites and the mutations wrote through the claim data sources, so the two stores could disagree. Drop the internal copies and resolve the proposal scope through the injected claim mocks, which makes claims created during a test visible to every read path. Reconciling the seeds means CoProposerClaimDataSourceMock also has to claim invite 2 on proposal 1, DataAccessClaimDataSourceMock has to point at invite 4 rather than the non-existent invite 7, and InviteMutations.spec has to init the data access claim mock it never initialised before.
create() derived the new id from the array length, so once a mutation had deleted an invite the next create handed out an id that another invite still held. findById then resolved the older invite, which only became visible now that claims created during a test are read back through the claim mocks.
The read path and the setXInvites mutations each decided for themselves
what a pending invite on a proposal is: InviteQueries called
findXInvites(proposalPk, false) while the mutations called
getXInvites({ proposalPk, isClaimed: false }). Two spellings of the same
rule against two different data source methods is what let them drift
apart in the first place, which is the bug this branch started from.
Replace both with findPendingCoProposerInvites / findPendingDataAccessInvites
on InviteDataSource. Neither caller states the filter now, so neither can
disagree about it. This matches how the visit path already works, where
the query and the mutation share findVisitRegistrationInvites.
Dropping the isClaimed flag also fixes the mock, which took the argument
and ignored it, so mocked reads returned claimed invites the postgres
implementation would have filtered out.
|
I thiks otherwise this looks very good, just let me know your thoughts on the comments I mentioned |
|
@zacharyjhankin @simonfernandes we need one more review before we merge this, could you please take a look? my summer engagement at ESS is concluding next week |
jekabs-karklins
left a comment
There was a problem hiding this comment.
Looks good now, looking forward to have this feature on production 👍🏻
mutambaraf
left a comment
There was a problem hiding this comment.
Looks good and left few comments
|
|
||
| export const dummyCountry = new Country(1, 'Denmark'); | ||
| export const dummyCountry2 = new Country(2, 'United Kingdom'); | ||
| export const dummyCountry3 = new Country(3, 'Belarus'); |
There was a problem hiding this comment.
We can remove dummyCountry3 because it is not being used.
| constructor( | ||
| @inject(Tokens.CoProposerClaimDataSource) | ||
| private coProposerDataSource: CoProposerClaimDataSource | ||
| private coProposerDataSource: CoProposerClaimDataSourceMock, |
There was a problem hiding this comment.
We can improve naming to coProposerClaimDataSource
| coProposerClaims.map((claim) => this.findById(claim.inviteId)) | ||
| ); | ||
| async findPendingCoProposerInvites(proposalPk: number): Promise<Invite[]> { | ||
| return this.getCoProposerInvites({ proposalPk, isClaimed: false }); |
There was a problem hiding this comment.
Maybe lets avoid hard coding this variable by actually passing in through findPendingCoProposerInvites
| } | ||
| }) | ||
| findPendingCoProposerInvites(proposalPk: number): Promise<Invite[]> { | ||
| return this.getCoProposerInvites({ proposalPk, isClaimed: false }); |
There was a problem hiding this comment.
We can also avoid hard coding isClaimed: false
| export interface DataAccessClaimRecord { | ||
| readonly invite_id: number; | ||
| readonly proposal_pk: number; | ||
| } | ||
|
|
There was a problem hiding this comment.
We can remove this if we are not using it.
| .insert({ invite_id: inviteId, proposal_pk: proposalPk }) | ||
| .returning('*') | ||
| .then((rows) => { | ||
| const row = rows[0]; |
There was a problem hiding this comment.
We may need to check if rows is an array and I think this is where we where supposed to use DataAccessClaimRecord
| ): Promise<Invite[]> { | ||
| const inviteIdsOnProposal = filter.proposalPk | ||
| ? ( | ||
| await this.coProposerDataSource.findByProposalPk(filter.proposalPk) |
There was a problem hiding this comment.
It may be more clear if we assign the value of await this.coProposerDataSource.findByProposalPk(filter.proposalPk) to a variable an check if it is returned.
Description
This PR introduces the capability to invite data access users to a proposal by creating a new data access claim.
Motivation and Context
This change is required to allow data access users to be linked directly to proposals, enabling them to view and manipulate data related to the proposal they are invited to. This enhances collaboration and data management in the project.
Changes
Visual Changes
The invite pop-ups have been updated to look clean and show the type of proposal invite (Co-Proposer / Data Access).
Previous
Current
How Has This Been Tested?
Adding and Deleting Invited Users
Screen.Recording.2026-07-02.at.19.51.10.mov
Screen.Recording.2026-07-02.at.19.51.42.mov
Screen.Recording.2026-07-02.at.19.53.53.mov
Invite Popups for New Sign-Ups
Screen.Recording.2026-07-06.at.16.15.20.mov
Fixes Jira Issue
https://jira.ess.eu//browse/SWAP-5408
Depends On
Tests included/Docs Updated?