-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
3372 - adds database changes to support managing IAA subrecipients #3522
Conversation
QA Summary
Test CoverageCoverage report for `packages/client`
Coverage report for `packages/server`
|
Terraform Summary
Hint: If "Terraform Format & Style" failed, run OutputValidation Output
Plan Summary
Pusher: @as1729, Action: |
packages/server/__tests__/arpa_reporter/server/db/arpa-subrecipients.spec.js
Fixed
Show fixed
Hide fixed
packages/server/__tests__/arpa_reporter/server/db/arpa-subrecipients.spec.js
Fixed
Show fixed
Hide fixed
packages/server/__tests__/arpa_reporter/server/db/arpa-subrecipients.spec.js
Fixed
Show fixed
Hide fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@as1729 A few initial comments for you to consider.
Additionally, would you mind filling out the description for this PR when you get a chance? I think that would help clarify the intended changes regarding the behavior of the new and existing fields (name
, uei
, and tin
) for reviewers, especially since #3372 is showing up as closed by #3503.
packages/server/__tests__/arpa_reporter/server/helpers/with-tenant-id.js
Outdated
Show resolved
Hide resolved
packages/server/migrations/20240918183745_drop_constraints_to_arpa_subrecipients.js
Show resolved
Hide resolved
exports.up = function (knex) { | ||
return knex.schema.table('arpa_subrecipients', (table) => { | ||
table.text('name'); | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is any kind of unique constraint/index warranted here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes good-call 👍
We only want to enforce it when both uei
and tin
are missing so as to not interfere with previous data in the database so I'll create an index with the where-clause as described above.
} else if (fieldType === 'tin') { | ||
query.where('arpa_subrecipients.tin', value); | ||
} else if (fieldType === 'name') { | ||
query.where('arpa_subrecipients.name', value); | ||
} else { | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth logging or printing something here or failing if a fieldType is passed in that doesn't match uei, tin or name.
We should also document the expected fieldType
values in a docstring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good call- I updated docstrings as well as updated the method to throw an error instead of returning null
as it can be confusing for the caller.
} | ||
} | ||
if (!recipient.tin && !recipient.uei && recipient.name) { | ||
const existingRecipient = await findRecipient('name', recipient.name, trns); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the same name exist across multiple tenants?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, findRecipient
scopes by tenant by default, so we should only be looking for duplicates within a tenant.
…a wrong field-type
…t null. add unit-tests to ensure indexes work correctly
…constraint or plain index
packages/server/migrations/20240918183915_add_name_to_arpa_subrecipients.js
Show resolved
Hide resolved
table.text('name'); | ||
}); | ||
await knex.raw( | ||
'CREATE UNIQUE INDEX idx_arpa_subrecipients_tenant_id_name_unique ON arpa_subrecipients (tenant_id, name) WHERE name is not null and uei is null and tin is null', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a situation like:
name
is not nulluei
is not nulltin
is null
Should name
still be unique to the tenant_id
? Similarly, could this be simplified to (tenant_id, name) WHERE name is not null
i.e. should duplicate non-null names for the same tenant allowed in any circumstance, regardless of uei
or tin
?
Also we may want an additional constraint to ensure that at least one of uei
/tin
/name
is provided. If that sounds like a good idea to you, I think that could be implemented as a CHECK constraint like:
ALTER TABLE arpa_subrecipients
ADD CONSTRAINT
chk_at_least_one_of_uei_tin_name_not_null
CHECK (num_nonnulls(uei, tin, name) > 0);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i.e. should duplicate non-null names for the same tenant allowed in any circumstance, regardless of uei or tin?
Right now we're working with insufficient data here. Working assumption is that there could be subrecipients in the system that share the same name but have slightly different UEI/TIN since this was never enforced in the past.
Also we may want an additional constraint to ensure that at least one of uei/tin/name is provided. If that sounds like a good idea to you, I think that could be implemented as a CHECK constraint like:
This makes sense to add since we enforce this on the application layer, can translate to a database check as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolved
if (!(recipient.uei || recipient.tin || recipient.name)) { | ||
throw new Error('recipient row must include a `uei`, `tin`, or `name` field'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like how the createRecipient()
function's various if ... throw
blocks make the business logic clear to developers and provide friendly error messages for users; I'm wondering if we could preserve that clarity while making better use of the db-level constraints by checking the cause of a failure after inserting rather than checking ahead of time (which can fail to identify the problem ahead of time in a double-click situation or similar race condition).
I think something like this might work:
async function createRecipient(recipient, trns = knex) {
const tenantId = useTenantId();
try {
return await trns('arpa_subrecipients')
.insert({ ...recipient, tenant_id: tenantId })
.returning('*')
.then((rows) => rows[0]);
} catch (e) {
switch (e.constraint) {
case 'chk_at_least_one_of_uei_tin_name_not_null':
throw new Error('recipient row must include a `uei`, `tin`, or `name` field');
case 'idx_arpa_subrecipients_tenant_id_uei_unique':
throw new Error('A recipient with this UEI already exists');
case 'idx_arpa_subrecipients_tenant_id_tin_unique':
throw new Error('A recipient with this TIN already exists');
// Other `case 'name_of_constraint_or_index'` statements go here...
default:
// Something else
throw e;
}
}
}
(See my previous comment regarding the chk_at_least_one_of_uei_tin_name_not_null
constraint.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is much cleaner since we don't need to duplicate the validation logic in both layers. Will update the code 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added in the latest commit
… receive an error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
Ticket #3372
Description
This PR adds support for the new IAA subrecipient field. The goal here is to ensure that we can add subrecipients that do not have either a UEI or TIN value. In order to do this the PR does the following:
name
field toarpa_subrecipients
table and a correspondingunique index
that ensures that when bothuei
andtin
are missing, that we do not create duplicate subrecipients with the samename
.createSubrecipient
function is called.unique
constraint onuei
andtin
fields and replaces with aunique
index that only applies when these fields areNOT NULL
.Screenshots / Demo Video
State Before migrations
State After Migrations
Confirming that Rollback works
Testing
Automated and Unit Tests
Manual tests for Reviewer
Checklist