Skip to content
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

Issue #3200 Add grant_notes and grant_notes_revisions table migration #3209

Merged
merged 5 commits into from
Jul 1, 2024

Conversation

thucdtran
Copy link
Contributor

Ticket #3200

Description

Add a knex migration for issue #3200.

Screenshots / Demo Video

Screenshot 2024-06-22 at 10 12 36 PM

Testing

Checked the table schemas in postgres CLI.

Checklist

  • Provided ticket and description
  • Provided screenshots/demo
  • Provided testing information
  • Provided adequate test coverage for all new code
  • Added PR reviewers

@github-actions github-actions bot added database-changes Includes schema migrations or other critical changes javascript Pull requests that update Javascript code labels Jun 23, 2024
Copy link

github-actions bot commented Jun 23, 2024

QA Summary

QA Check Result
🌐 Client Tests
🔗 Server Tests
🤝 E2E Tests
📏 ESLint
🧹 TFLint

Test Coverage

Coverage report for `packages/client`
St File % Stmts % Branch % Funcs % Lines Uncovered Line #s
🔴 All files 15.69 12.76 16.86 15.89
🔴  src 0 100 100 0
🔴   App.vue 0 100 100 0 9
🔴  src/arpa_reporter 0 100 100 0
🔴   App.vue 0 100 100 0 13
🟡  ...ter/components 53.96 31.81 58.97 53.96
🔴   AlertBox.vue 33.33 0 0 33.33 35-36
🟡   ...oadButton.vue 57.14 50 42.85 57.14 60-67
🟢   ...ileButton.vue 100 100 100 100
🔴   ...ttonSmall.vue 0 100 0 0 13-23
🟢   ...mplateBtn.vue 100 100 100 100
🟡   ...avigation.vue 65 100 62.5 65 213-219,228-235
🔴   StandardForm.vue 37.5 25 55.55 37.5 124-128,135-157
🟢  ...porter/helpers 84.61 79.48 87.5 84.61
🟢   form-helpers.js 84.21 79.48 85.71 84.21 7,16,25,81-83
🟢   short-uuid.js 100 100 100 100
🔴  ...eporter/router 0 0 0 0
🔴   index.js 0 0 0 0 21-135
🔴  ...reporter/store 4.85 0 2.17 5.1
🔴   index.js 4.85 0 2.17 5.1 13-16,34-263
🔴  ...reporter/views 17.03 11.51 25.98 17.03
🟢   AgenciesView.vue 100 100 100 100
🔴   AgencyView.vue 0 0 0 0 30-96
🔴   HomeView.vue 16.66 5 50 16.66 113,137-207
🔴   LoginView.vue 0 0 0 0 49-100
🔴   ...plateView.vue 0 0 0 0 47-113
🔴   ...ploadView.vue 0 0 0 0 24-144
🔴   ...eriodView.vue 0 0 0 0 30-90
🔴   ...riodsView.vue 0 0 0 0 124-174
🔴   ...pientView.vue 0 0 0 0 56-152
🔴   ...ientsView.vue 0 0 0 0 99-206
🔴   UploadView.vue 49.12 47.61 78.94 49.12 ...41-442,448-449
🔴   UploadsView.vue 44.44 40.9 66.66 44.44 ...61-264,272-287
🔴   UserView.vue 40.62 20 68.75 40.62 84,97-137
🔴   UsersView.vue 0 0 0 0 58-126
🔴   ...ationView.vue 0 0 0 0 109-270
🔴  src/components 13.61 8.2 12.85 13.61
🟡   ...vityTable.vue 69.23 45 100 69.23 164-170,185
🔴   BaseLayout.vue 45.45 100 44.44 45.45 220-232
🔴   ...atesTable.vue 11.11 0 0 11.11 56-88
🔴   CopyButton.vue 0 100 0 0 29-49
🔴   GrantsTable.vue 3.84 0 0 3.84 187-543
🔴   ...dUploader.vue 3.7 0 0 3.7 55-111
🔴   SearchFilter.vue 5.55 0 0 5.55 40-82
🔴   UserAvatar.vue 12.5 0 0 12.5 29-40
🔴  ...ponents/Modals 9.09 1.6 10.93 9.12
🔴   ...anization.vue 18.75 0 22.22 18.75 143-178
🔴   AddTeam.vue 45 25 58.33 45 204,210,216-245
🔴   AddUser.vue 0 0 0 0 74-176
🔴   ...anization.vue 20 0 16.66 20 58-78
🔴   EditTeam.vue 12.19 0 30.76 12.19 208-301
🔴   EditUser.vue 6.25 0 0 6.25 72-128
🔴   ...ilsLegacy.vue 2.5 0 0 2.5 205-369
🔴   ImportTeams.vue 10 0 0 10 56-82
🔴   ImportUsers.vue 0 0 0 0 49-80
🔴   ...archPanel.vue 3.03 0 0 3.03 146-255
🔴   SearchPanel.vue 3.44 0 0 3.5 301-486
🔴  src/helpers 16.96 20 17.14 17.43
🟢   constants.js 100 100 100 100
🟢   currency.js 100 100 100 100
🟢   dates.js 100 100 100 100
🔴   fetchApi.js 5.71 16.66 5.88 5.71 9-97
🔴   filters.js 4 0 0 4.54 19-51
🔴   form-helpers.js 0 0 0 0 5-82
🟡   gtag.js 77.77 90 75 77.77 12,51
🟢  ...s/featureFlags 100 100 100 100
🟢   index.js 100 100 100 100
🟢   utils.js 100 100 100 100
🔴  src/mixin 20 0 28.57 20
🔴   ...zableTable.js 20 0 28.57 20 16-31,36-37,42
🔴  src/router 20.51 16.66 15 20.51
🔴   index.js 20.51 16.66 15 20.51 ...02-203,207-226
🟢  src/store 100 100 100 100
🟢   index.js 100 100 100 100
🔴  src/store/modules 3.58 0 4.72 3.77
🔴   agencies.js 5.26 100 8.33 5.55 13-70
🔴   alerts.js 20 100 20 20 10-24
🔴   grants.js 1.41 0 1.05 1.49 58-352
🔴   organization.js 33.33 100 33.33 33.33 21-25
🔴   roles.js 20 100 20 25 13-22
🔴   tenants.js 11.11 100 14.28 12.5 13-32
🔴   users.js 2.43 0 4.76 2.5 17-100
🔴  src/views 12.34 4.44 11.59 12.39
🔴   ...orterView.vue 0 0 0 0 96-151
🔴   ...boardView.vue 30 0 36.36 30 ...30-140,158-169
🔴   ...tailsView.vue 0 0 0 0 245-532
🟢   GrantsView.vue 100 100 100 100
🔴   LoginView.vue 5.88 0 0 5.88 87-134
🔴   MyGrantsView.vue 0 100 0 0 47-69
🟡   ...ofileView.vue 77.77 66.66 75 77.77 130-134
🟡   NotFoundView.vue 80 100 0 100
🔴   ...tionsView.vue 45.45 100 40 45.45 84,94-97,111-115
🔴   ...ivityView.vue 0 0 0 0 63-134
🟡   TeamsView.vue 54.54 100 50 54.54 142,156-163
🔴   ...DatesView.vue 0 0 0 0 49-119
🔴   UsersView.vue 0 0 0 0 62-139
Coverage report for `packages/server`
St File % Stmts % Branch % Funcs % Lines Uncovered Line #s
🟡 All files 57.97 50.33 53.56 58.08
🟢  src 81.63 33.33 60 81.63
🟢   configure.js 81.63 33.33 60 81.63 42,61-68,97-99
🟢  src/arpa_reporter 98.75 66.66 100 98.75
🟢   configure.js 97.36 40 100 97.36 36
🟢   environment.js 100 100 100 100
🟢   use-request.js 100 100 100 100
🔴  src/arpa_reporter/db 38.58 32.92 44.44 40.16
🔴   arpa-subrecipients.js 13.15 4.34 15.38 14.28 23-92
🔴   reporting-periods.js 37.2 46.87 40 38.09 46,77-156
🟢   settings.js 100 83.33 100 100 13
🟡   uploads.js 50 28.57 52.38 51.42 18-29,84,99-124,141-150
🔴  src/arpa_reporter/lib 29.57 33.08 34.56 28.46
🟢   arpa-ec-codes.js 100 100 100 100
🔴   audit-report.js 21.44 19.35 24.19 21.32 ...28-529,554-684,732-758
🟡   ensure-async-context.js 75 100 50 100
🟢   format.js 90.62 90 90 91.3 41-42
🟡   log.js 75 50 50 75 13,25
🟡   preconditions.js 66.66 33.33 100 66.66 3
🔴   spreadsheet.js 9.09 0 0 9.09 15-32
🟢   validation-error.js 85.71 100 50 85.71 16
🔴  src/arpa_reporter/routes 40 14.92 14.28 40.6
🔴   agencies.js 22.58 0 0 23.33 13-21,26-53
🟡   application_settings.js 75 100 0 75 10-11
🟡   audit-report.js 68.91 58.33 100 68.91 57-58,64-78,100-116
🟢   exports.js 81.42 83.33 100 81.42 61-75,98-99
🔴   reporting-periods.js 20 0 0 20.43 ...25-137,143-149,154-180
🔴   subrecipients.js 23.8 0 0 23.8 12-13,17-27,31-48,52-63
🔴   uploads.js 28.28 7.89 9.09 29.16 ...33-154,164-166,173-180
🔴   users.js 19.6 0 0 20 15-35,39-44,48-81
🔴  src/arpa_reporter/services 42.83 30.41 45.71 43.22
🔴   generate-arpa-report.js 36.86 2.79 50 37.24 ...-974,983-996,1070-1137
🔴   get-template.js 21.62 0 0 21.62 18-79
🟡   persist-upload.js 68.6 90 69.56 68.67 ...58-200,221-235,273-295
🔴   records.js 20.75 0 11.11 21.15 38-204,221-276
🔴   revalidate-uploads.js 37.5 100 0 37.5 5-14
🔴   validate-upload.js 38.86 50.6 33.33 39.81 ...20,339,361,379-656,671
🟢   validation-rules.js 98.18 90 90.9 100 157,173
🟡  src/db 74.45 71.31 68.78 74.49
🟢   connection.js 100 50 100 100 6
🟢   constants.js 100 100 100 100
🟡   helpers.js 75 83.33 50 75 5,21-22
🟢   index.js 82.57 78.36 82.35 82.53 ...69-1435,1617-1618,1625
🟢   saved_search_migration.js 92 88.23 71.42 93.61 5,69,134
🔴   tenant_creation.js 10.58 2.7 0 11.11 15-40,48-210,220
🔴  src/db/arpa_reporter_db_shims 23.68 0 0 23.68
🔴   agencies.js 22.22 100 0 22.22 11-51
🔴   users.js 25 0 0 25 12-62
🟢  src/lib 85.32 78.75 91.04 85.28
🟢   access-helpers.js 93.54 89.18 100 93.54 96-97,102-103
🟢   agencyImporter.js 90.38 88.46 100 90.19 26,29,35,93-94
🟢   email.js 92.85 79.24 100 92.76 ...38,160-164,211,357-360
🔴   gost-aws.js 47.82 37.5 42.85 47.72 13-58,94,104,114-134
🟢   grants-ingest.js 83.33 97.5 90 83.33 ...28-131,138-140,155-159
🟡   logging.js 77.77 85.71 100 77.77 11,13
🟢   redirect_validation.js 100 100 100 100
🟢   userImporter.js 82.27 58.33 88.88 81.57 32,47,53,62,73-81,143-152
🔴  src/lib/annualReports 27.38 0 0 27.38
🔴   doc-builder.js 7.69 0 0 7.69 19-352
🟡   index.js 80 100 0 80 6
🟢   placeholderTextStrings.js 100 100 100 100
🔴   reportBuilder.js 17.24 0 0 17.24 21-33,50-62,86-103
🟢  src/lib/arpa_reporter_shims 100 100 100 100
🟢   email.js 100 100 100 100
🟢  src/lib/email 93.1 87.5 100 92.59
🟢   constants.js 100 100 100 100
🟢   email-nodemailer.js 88.23 83.33 100 86.66 33,64
🟢   service-email.js 100 100 100 100
🟢  src/lib/fieldConfigs 100 100 100 100
🟢   fundingActivityCategories.js 100 100 100 100
🟡  src/routes 70.59 62.55 62.82 70.53
🔴   agencies.js 42.39 30 33.33 42.39 ...13-121,125-146,154-160
🔴   annualReports.js 47.05 100 0 47.05 15-27
🟢   eligibilityCodes.js 100 100 100 100
🟢   grants.js 80.97 71.55 76.47 81.25 ...80-381,396-399,[412-413](https://git...*[Comment body truncated]*

Copy link

github-actions bot commented Jun 23, 2024

Terraform Summary

Step Result
🖌 Terraform Format & Style
⚙️ Terraform Initialization
🤖 Terraform Validation
📖 Terraform Plan

Hint: If "Terraform Format & Style" failed, run terraform fmt -recursive from the terraform/ directory and commit the results.

Output

Validation Output
stdout:
Success! The configuration is valid.


-------------------------------------
stderr:

Plan Summary
CHANGE RESOURCE
update module.api.aws_ecs_service.default[0]
module.api.module.grant_digest_scheduled_task.aws_iam_role_policy.default[0]
module.api.module.grant_digest_scheduled_task.aws_scheduler_schedule.default[0]
module.arpa_audit_report.aws_ecs_service.default
module.arpa_audit_report.aws_iam_role_policy.task["connect-to-postgres"]
module.arpa_treasury_report.aws_ecs_service.default
module.arpa_treasury_report.aws_iam_role_policy.task["connect-to-postgres"]
module.consume_grants.aws_ecs_service.default
module.consume_grants.aws_iam_role_policy.task["connect-to-postgres"]
module.postgres.module.db.aws_rds_cluster.this[0]
module.postgres.module.db.aws_rds_cluster_instance.this["one"]
module.website.aws_s3_object.deploy-config[0]
recreate module.api.aws_ecs_task_definition.default[0]
module.arpa_audit_report.aws_ecs_task_definition.consumer
module.arpa_treasury_report.aws_ecs_task_definition.consumer
module.consume_grants.aws_ecs_task_definition.consume_grants

Pusher: @thucdtran, Action: pull_request_target, Workflow: Continuous Integration

Knex documentation says for the unique() function, that useConstraint will use a unique constraint and is default true for postgres when there is no predicate defined.
Issue #3200 specifies that we want a unique index.
Documentation can be found here: https://knexjs.org/guide/schema-builder.html#unique
@thucdtran thucdtran requested a review from a team June 23, 2024 02:50
@jeffsmohan
Copy link
Contributor

This PR looks correct to the spec, but before we land the change I wanted to better understand why this is being structured as two tables instead of one. Are there query patterns or other considerations that make this setup better than a single table?

To make my question more concrete, here's my first instinct of how I would structure the new notes data, if we want to capture all the revisions:

  • table grant_notes
    • id: serial, not null, primary key
    • grant_id: text, not null, foreign key to grants.grant_id
    • user_id: int, not null, foreign key to users.id
    • text: text, not null
    • created_at: timestamp with time zone, not null, default now()

It seems like with either setup (as spec'd in the issue, or the table I laid out here) you have to do a slightly complicated query to get what you want to display on the page: (note that I haven't tested this query... it probably could use some tweaking, but it seemed useful for illustration)

with current_grant_notes as (
  select 
    id, 
    user_id, 
    text, 
    row_number() over(partition by user_id order by created_at desc) as rank
  from grant_notes
  where grant_id = {grant_id}
  -- also probably need to join in agency to filter this down to within-agency notes
)
select * from current_grant_notes
where rank = 1

If we wanted that query pattern to be simpler, I think we'd need to denormalize the concept of "current note" in the database (either as a separate table or as a flag on the table I'm proposing), which would then require a bit more upkeep when writing to the table. If we don't mind the above query pattern, then I'm not yet seeing the benefit of the two tables vs the one.

Sorry to hijack the PR for a design discussion, but I wanted to make sure we've nailed down the schema before committing to it. (And my apologies if this was already discussed/captured elsewhere and I missed it!)

What do you think, @TylerHendrickson and @thucdtran ?

@TylerHendrickson
Copy link
Member

This PR looks correct to the spec, but before we land the change I wanted to better understand why this is being structured as two tables instead of one. Are there query patterns or other considerations that make this setup better than a single table?
...
What do you think, @TylerHendrickson and @thucdtran ?

@jeffsmohan Thanks for the thoughts here – I wrestled with this a bit also. A few things to mention:

  • I did document a query pattern for getting the most recent revision for each grant/user here: Follow+Notes: Create backend library for grants collaboration management #3201. It uses a LEFT JOIN LATERAL (which maybe isn't a lot better) instead of a CTE, but just wanted to provide a link to the proposed querying pattern.
  • The challenge with using a single-table design has to do with enforcing the "one 1 note per-user-per-grant" constraint, and particularly doing that in a way that is compatible with us deciding to do away with this constraint at some point (which I think is pretty likely). With a single table, we would need:
    • Not implement a unique index/constraint on (grant_id, user_id)
    • Add another column like common_note_id (self-referential FK to id)
    • Enforce that every row sharing the same common_note_id also shares the same combination of (grant_id, user_id) values. The only way I know of that could do this would be a CHECK constraint, and given that we don't know much about usage patterns of this feature just yet, I'm not confident that the associated query would be very optimal.
  • While there's no explicit requirement to soft-delete comments at the moment, a two-table design allows us to do things like add an is_deleted boolean column (and other note metadata that's common to all revisions) to the grant_notes table; the alternative would either require adding is_deleted to all revision rows of a single note or else some other design choice that would be hard to anticipate before knowing more about the feature.

I think it's entirely possible that as the feature evolves and we understand more about its usage, we'll either need to migrate the data in this table to a new design or even to a different storage backend entirely, like DynamoDB or a document store, which are much more suitable for storing revisions and eventually consistent behaviors that I associate with distributed commenting systems. So I'm really viewing this table design as "works for the known short-term, somewhat robust enough for fast-follow/medium-term changes, with a decent chance that it will eventually be replaced in its entirety", while remaining something that's unlikely to have performance issues as a forcing function in making those decisions.

@jeffsmohan
Copy link
Contributor

@TylerHendrickson gotcha, thanks for catching me up, and sorry I missed the background in the other ticket!

I see where the design is coming from, and I agree we should be prepared to evolve the schema as these early feature requirements evolve anyway, so it's not worth holding this up any further. (In other words, PR looks good to merge!)

I'll just call out that it seems the requirement to store all note revisions ends up making the schema and the query patterns significantly more complicated to write (and whether it ends up mattering or not, less efficient queries). I'm trying to remember, was the storage of past revisions just to satisfy our internal desire to understand if/when/how users are modifying their notes? If so, I wonder if there's a better path where we only store the current note per user in the DB, but spew all revisions out to datadog as logs or some other less structured storage solution that we can query ad-hoc. Just noodling... if it spurs something useful, great; if not, consider me more than sufficiently happy to continue down the current path!

@thucdtran thucdtran merged commit af7e14f into main Jul 1, 2024
19 checks passed
@thucdtran thucdtran deleted the thucdtran/add-grant-notes-to-db branch July 1, 2024 02:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
database-changes Includes schema migrations or other critical changes javascript Pull requests that update Javascript code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants