Skip to content

[DT-4061] Consent modifications to support redesigned study pages. - #3047

Closed
otchet-broad wants to merge 5 commits into
developfrom
otchet-dt-3990-study-ratings-pi-details
Closed

[DT-4061] Consent modifications to support redesigned study pages.#3047
otchet-broad wants to merge 5 commits into
developfrom
otchet-dt-3990-study-ratings-pi-details

Conversation

@otchet-broad

Copy link
Copy Markdown
Contributor

Addresses

https://broadworkbench.atlassian.net/browse/DT-4061

Summary

Adds the backend for the redesigned study page: PI contact details, researcher ratings/comments, first-class registration assets, and study-level metrics and recommendations.

PI details on Study

Four new study columns (pi_institution_id, pi_orcid, pi_linkedin_url, pi_website_url), plumbed through Study, StudyPatch, StudyDAO, and DatasetServiceDAO. Study reads join institution so the PI institution name comes back with the study. Registration already collected the PI institution as the numeric piInstitution study property; the create path now writes the column as well, and a backfill changeset copies the property into the column for existing studies (skipping non-numeric values and ids with no institution row).

Because these are columns rather than study properties, they follow JSON semantics instead of the blank-string-deletes convention used elsewhere in StudyPatch: absent is a no-op, explicit null clears, a blank string normalizes to a clear, a value sets. StudyPatch now records which fields arrived as explicit JSON nulls to make that distinction possible.

Behavior change: PATCH authorization
StudyResource.patchStudyById now requires creator/custodian/admin on this study, not merely a study-editing role somewhere in DUOS. A role-only callewho could PATCH a publicly visible study on develop now getsstration PUT path.

Comments & ratings

New study_comment table (rating 1–5 enforced by check construdy, FK cascade on study and user) with GET (list + average), POST (upsert), and DELETE (own comment only) under api/dataset/study/{studyId}/comments. Posting requires the Researcher role and a library card; reads are @permitAll subject to study visibility.

Asset promotion

Eight asset lists (models, workspaces, presentations, publiclectualProperties, biospecimens, funding) move out of theclient-managed, unvalidated assets blob into their own study_property rows and become first-class registration fields. Reads fall back to the legacy object for studies not re-registered since; a present promoteven when empty, so removing the last asset of a type isn'tundone by a stale copy. Elasticsearch reassembles the original assets shape, so search consumers are unaffected.

Seven new GET /api/dataset/study/{studyId}/assets/* endpoints. Two naming mismatches worth knowing: /assets/intellectualProperty reads intellectualProperties, /assets/fundingResources reads fundindpoint — it's reachable only through the registration GET.

Metrics & recommendations

  • Study-scoped DAR summaries in a single query rather than o summary from the most recently submitted qualifying DAR so a pending progress report can't overwrite the grant's title, RUS, and date.
  • Research outputs (presentations, publications, IP) aggregaeports.
  • findSimilar — public studies sharing the PI or a data type, ranked by overlap; a blank PI name never matches another blank one.
  • findFrequentlyRequestedWith — public studies co-requested ived DARs, and progress reports are excluded from scoring.

Cross-cutting

All twelve new @permitAll GETs go through a shared DatasetSecess, so a non-public study is readable only by creator,custodians, and admins. New services are registered as Guice singletons (docs/ai/CLAUDE.md updated with the provider pattern). AuthorizationHelper now
null-guards a user with no role rows, which previously turne

Reviewer notes

  • Four Liquibase changesets; two are data migrations with noangeset is destructive by design — it removes promoted keysfrom the legacy object after copying them. The registration PUT is a full replace: a client omitting
  • Unit/integration tests added for each new DAO, service, resource, and both migrations. The five scripts/verify-study-*.sh runbooks drive a live instance manually and are not part of CI.

Have you read CONTRIBUTING.md lately? If not, do that first.

  • Label PR with a Jira ticket number and include a link to the ticket
  • Label PR with a security risk modifier [no, low, medium, high]
  • PR describes scope of changes
  • Get a minimum of one thumbs worth of review, preferably two if enough team members are available
  • Get PO sign-off for all non-trivial UI or workflow changes
  • Verify all tests go green
  • Test this change deployed correctly and works on dev environment after deployment

@sonarqubecloud

sonarqubecloud Bot commented Sep 4, 2026

Copy link
Copy Markdown

@otchet-broad
otchet-broad marked this pull request as ready for review September 8, 2026 13:26
@otchet-broad
otchet-broad requested a review from a team as a code owner September 8, 2026 13:26
@otchet-broad
otchet-broad requested review from fboulnois and rushtong and a lite review from Copilot and removed request for a team September 8, 2026 13:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are confirmed correctness/validation issues (commentText accepts non-string primitives, and POST can throw a contextless NoSuchElementException) plus a test change that introduces Mockito lenient stubbing contrary to repo guidance.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds backend support for redesigned study pages by introducing PI profile fields on Study, promoting registration “assets” into first-class study_property rows (with legacy fallback), and adding new study-scoped comments/ratings and metrics/recommendation endpoints with consistent study-visibility gating.

Changes:

  • Adds PI institution + profile link columns, patch semantics (absent vs explicit null), and supporting migrations/tests.
  • Promotes selected asset lists out of the legacy assets blob into dedicated properties, updates search indexing and registration mapping, and adds read endpoints + OpenAPI updates.
  • Adds study comment CRUD, study-scoped DAR summaries, research outputs aggregation from progress reports, and study recommendation queries/endpoints.
File summaries
File Description
src/test/java/org/broadinstitute/consent/http/service/StudyCommentServiceTest.java Unit coverage for comment listing/post/delete behavior and visibility gating.
src/test/java/org/broadinstitute/consent/http/service/StudyAssetServiceTest.java Unit coverage for promoted-vs-legacy asset reads and visibility gating.
src/test/java/org/broadinstitute/consent/http/service/MetricsServiceTest.java Unit coverage for study-scoped metrics, outputs aggregation, and recommendations.
src/test/java/org/broadinstitute/consent/http/service/DatasetServiceTest.java Adds tests for shared study visibility gate and updates for new PI fields.
src/test/java/org/broadinstitute/consent/http/service/DatasetRegistrationServiceTest.java Ensures registration updates preserve PI profile links and set PI institution.
src/test/java/org/broadinstitute/consent/http/service/dao/DatasetServiceDAOTest.java Validates study insert/patch/update behavior including PI institution + PI detail fields.
src/test/java/org/broadinstitute/consent/http/resources/StudyResourceTest.java Updates resource tests for new visibility gate and stricter PATCH authorization.
src/test/java/org/broadinstitute/consent/http/resources/StudyCommentResourceTest.java Resource-level tests for comment payload validation and error mapping.
src/test/java/org/broadinstitute/consent/http/resources/StudyAssetResourceTest.java Resource-level tests for asset subresource delegation and error mapping.
src/test/java/org/broadinstitute/consent/http/resources/MetricsResourceTest.java Resource-level tests for new study metrics endpoints and error mapping.
src/test/java/org/broadinstitute/consent/http/models/StudyPatchTest.java Tests new PI column patch semantics (absent/no-op vs explicit null/clear).
src/test/java/org/broadinstitute/consent/http/models/StudyAssetsTest.java Tests promoted asset behavior, legacy fallback, and assembled legacy shape.
src/test/java/org/broadinstitute/consent/http/models/dto/registration/RegistrationRequestMapperTest.java Ensures promoted asset lists map to their own properties and legacy stripping.
src/test/java/org/broadinstitute/consent/http/db/StudyRecommendationDAOTest.java Integration tests for similarity and co-request recommendation queries.
src/test/java/org/broadinstitute/consent/http/db/StudyPiInstitutionBackfillMigrationTest.java Executes shipped Liquibase SQL against seeded data to validate migration.
src/test/java/org/broadinstitute/consent/http/db/StudyDAOTest.java Verifies PI detail column persistence and join behavior.
src/test/java/org/broadinstitute/consent/http/db/StudyCommentDAOTest.java Integration tests for comment upsert/list/delete semantics.
src/test/java/org/broadinstitute/consent/http/db/StudyAssetsMigrationTest.java Executes shipped Liquibase SQL to validate asset promotion/stripping migration.
src/test/java/org/broadinstitute/consent/http/db/DatasetDAOTest.java Updates dataset/study mapping tests for new PI detail columns.
src/test/java/org/broadinstitute/consent/http/db/DataAccessRequestDAOTest.java Tests new study-scoped DAR summary query and progress report filtering.
src/test/java/org/broadinstitute/consent/http/authentication/AuthorizationHelperTest.java Adds coverage for null/empty-role authorization behavior.
src/main/resources/changesets/changelog-consent-2026-09-02-study-pi-institution-backfill.xml Data migration: backfill study.pi_institution_id from legacy property.
src/main/resources/changesets/changelog-consent-2026-09-02-study-assets.xml Data migration: promote asset keys into properties and strip legacy object.
src/main/resources/changesets/changelog-consent-2026-08-18-study-pi-details.xml Schema change: adds PI detail columns + FK + index.
src/main/resources/changesets/changelog-consent-2026-08-18-study-comments.xml Schema change: introduces study_comment table + constraints.
src/main/resources/changelog-master.xml Wires new changesets into Liquibase master changelog.
src/main/resources/assets/schemas/Workspace.yaml Adds OpenAPI schema for workspace assets.
src/main/resources/assets/schemas/StudyResearchOutputs.yaml Adds OpenAPI schema for aggregated progress-report outputs.
src/main/resources/assets/schemas/StudyRecommendation.yaml Adds OpenAPI schema for recommendation results.
src/main/resources/assets/schemas/StudyPatch.yaml Documents new patchable PI fields and their null/omit semantics.
src/main/resources/assets/schemas/StudyCommentsSummary.yaml Adds OpenAPI schema for comments list + average rating.
src/main/resources/assets/schemas/StudyComment.yaml Adds OpenAPI schema for a study comment.
src/main/resources/assets/schemas/Study.yaml Adds PI institution + profile link fields to study schema.
src/main/resources/assets/schemas/Maintainer.yaml Adds schema used by AI model assets.
src/main/resources/assets/schemas/IntellectualProperty.yaml Fixes filingDate schema type/description.
src/main/resources/assets/schemas/FundingResource.yaml Adds OpenAPI schema for funding assets.
src/main/resources/assets/schemas/DatasetRegistrationSchemaV1.yaml Adds promoted asset list fields and deprecates legacy assets.
src/main/resources/assets/schemas/DarMetric.yaml Adds submissionDate + PI/institution fields to DAR metric schema.
src/main/resources/assets/schemas/ClinicalTrial.yaml Adds OpenAPI schema for clinical trial assets.
src/main/resources/assets/schemas/Biospecimen.yaml Adds OpenAPI schema for biospecimen assets.
src/main/resources/assets/schemas/AiModel.yaml Adds OpenAPI schema for AI model assets.
src/main/resources/assets/paths/studyWorkspaces.yaml New study asset endpoint OpenAPI path (workspaces).
src/main/resources/assets/paths/studySimilarRecommendations.yaml New metrics endpoint OpenAPI path (similar studies).
src/main/resources/assets/paths/studyResearchOutputs.yaml New metrics endpoint OpenAPI path (research outputs).
src/main/resources/assets/paths/studyPublications.yaml New study asset endpoint OpenAPI path (publications).
src/main/resources/assets/paths/studyPresentations.yaml New study asset endpoint OpenAPI path (presentations).
src/main/resources/assets/paths/studyModels.yaml New study asset endpoint OpenAPI path (models).
src/main/resources/assets/paths/studyIntellectualProperty.yaml New study asset endpoint OpenAPI path (intellectual property).
src/main/resources/assets/paths/studyFundingResources.yaml New study asset endpoint OpenAPI path (funding resources).
src/main/resources/assets/paths/studyFrequentlyRequestedWith.yaml New metrics endpoint OpenAPI path (co-requested studies).
src/main/resources/assets/paths/studyDarSummaries.yaml New metrics endpoint OpenAPI path (study DAR summaries).
src/main/resources/assets/paths/studyComments.yaml New comments endpoint OpenAPI path (list + post).
src/main/resources/assets/paths/studyCommentById.yaml New comments endpoint OpenAPI path (delete own comment).
src/main/resources/assets/paths/studyClinicalTrials.yaml New study asset endpoint OpenAPI path (clinical trials).
src/main/resources/assets/paths/studyById.yaml Updates PATCH docs for new authorization and new PI patch fields.
src/main/resources/assets/api-docs.yaml Registers new study asset/comment and study metrics OpenAPI routes.
src/main/java/org/broadinstitute/consent/http/service/studytemplate/StudyTemplateV1Fields.java Allows promoted asset fields in template validation/whitelisting.
src/main/java/org/broadinstitute/consent/http/service/StudyCommentService.java Implements comment list/post/delete with role+library-card checks and visibility gating.
src/main/java/org/broadinstitute/consent/http/service/StudyAssetService.java Reads promoted/legacy assets with shared visibility enforcement.
src/main/java/org/broadinstitute/consent/http/service/MetricsService.java Adds study-scoped DAR summaries, research outputs aggregation, and recommendations.
src/main/java/org/broadinstitute/consent/http/service/ElasticSearchService.java Re-assembles legacy-shaped assets object for search indexing after promotion.
src/main/java/org/broadinstitute/consent/http/service/DatasetService.java Introduces shared verifyStudyVisibilityAccess used across study-derived endpoints.
src/main/java/org/broadinstitute/consent/http/service/DatasetRegistrationService.java Ensures PI institution + existing profile links are handled correctly on registration update/insert.
src/main/java/org/broadinstitute/consent/http/service/dao/DatasetServiceDAO.java Persists PI detail columns on update/patch and records PI institution on insert.
src/main/java/org/broadinstitute/consent/http/resources/StudyResource.java Tightens PATCH authorization to creator/custodian/admin and centralizes visibility check.
src/main/java/org/broadinstitute/consent/http/resources/StudyCommentResource.java Adds comments API with stricter JSON typing checks for rating payload.
src/main/java/org/broadinstitute/consent/http/resources/StudyAssetResource.java Adds study asset subresource endpoints delegating to StudyAssetService.
src/main/java/org/broadinstitute/consent/http/resources/MetricsResource.java Adds study-scoped metrics/recommendations endpoints.
src/main/java/org/broadinstitute/consent/http/models/StudyResearchOutputs.java DTO for aggregated progress report outputs.
src/main/java/org/broadinstitute/consent/http/models/StudyRecommendation.java DTO for recommendation results.
src/main/java/org/broadinstitute/consent/http/models/StudyPatch.java Adds PI fields and explicit-null tracking to distinguish omit vs clear semantics.
src/main/java/org/broadinstitute/consent/http/models/StudyCommentsSummary.java DTO for comment list + average rating.
src/main/java/org/broadinstitute/consent/http/models/StudyComment.java DTO for stored comment with display/institution info.
src/main/java/org/broadinstitute/consent/http/models/StudyAssets.java Centralizes promoted asset read/assemble/strip logic with legacy fallback.
src/main/java/org/broadinstitute/consent/http/models/Study.java Adds PI institution and profile link fields.
src/main/java/org/broadinstitute/consent/http/models/dto/registration/StudyRegistrationRequest.java Adds top-level promoted asset fields to registration request DTO.
src/main/java/org/broadinstitute/consent/http/models/dto/registration/RegistrationRequestMapper.java Promotes asset lists into separate study properties and strips legacy object.
src/main/java/org/broadinstitute/consent/http/models/dataset_registration_v1/DatasetRegistrationSchemaV1.java Adds promoted asset lists to generated schema model.
src/main/java/org/broadinstitute/consent/http/models/dataset_registration_v1/builder/SchemaFromStudy.java Emits both promoted fields and legacy-shaped assembled assets for compatibility.
src/main/java/org/broadinstitute/consent/http/models/DarMetricsSummary.java Extends metrics DTO with submission date and PI/institution fields.
src/main/java/org/broadinstitute/consent/http/db/StudyRecommendationDAO.java Adds queries for similar and frequently co-requested studies.
src/main/java/org/broadinstitute/consent/http/db/StudyDAO.java Adds PI detail columns to updates and joins PI institution name on reads.
src/main/java/org/broadinstitute/consent/http/db/StudyCommentDAO.java Adds DAO for comment list/upsert/delete.
src/main/java/org/broadinstitute/consent/http/db/mapper/StudyReducer.java Maps PI institution (id + optional name) onto Study during row reduction.
src/main/java/org/broadinstitute/consent/http/db/mapper/DarMetricsSummaryMapper.java Maps new summary fields and expired computation based on submission date.
src/main/java/org/broadinstitute/consent/http/db/DataAccessRequestDAO.java Adds study-scoped metrics/progress-report queries and enriches summary rows.
src/main/java/org/broadinstitute/consent/http/ConsentModule.java Registers new services and updates MetricsService provider signature.
src/main/java/org/broadinstitute/consent/http/ConsentApplication.java Registers new Jersey resources for study assets and comments.
src/main/java/org/broadinstitute/consent/http/authentication/AuthorizationHelper.java Null-guards missing user/roles to avoid 500s on @RolesAllowed endpoints.
docs/ai/CLAUDE.md Updates provider-pattern guidance for Guice singletons.
Review details
  • Files reviewed: 95/95 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +108 to +111
if (!element.isJsonPrimitive()) {
throw new BadRequestException("Comment text must be a string.");
}
return element.getAsString();
return commentDAO.findByStudyId(studyId).stream()
.filter(comment -> comment.studyCommentId().equals(id))
.findFirst()
.orElseThrow();
Comment on lines 67 to +83
resource = new StudyResource(datasetService, datasetRegistrationService, elasticSearchService);
// The read-access gate now lives in DatasetService#verifyStudyVisibilityAccess (shared with
// the study asset, comment, and metrics endpoints). These tests exercise the resource, so the
// mock replays the real rule against whatever isCreatorCustodianOrAdmin each test stubs.
// DatasetServiceTest covers the rule itself.
lenient()
.when(datasetService.verifyStudyVisibilityAccess(any(), any()))
.thenAnswer(
invocation -> {
Study study = invocation.getArgument(0);
User requestingUser = invocation.getArgument(1);
if (!datasetService.isCreatorCustodianOrAdmin(requestingUser, study)
&& !Boolean.TRUE.equals(study.getPublicVisibility())) {
throw new NotFoundException("Study not found");
}
return study;
});
@rushtong

rushtong commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Some unfiltered AI findings while I go through this manually.


Claude

Findings

  • StudyAssets.java:93 — assemble removes legacy asset keys with equalsIgnoreCase, but findAssetList reads the legacy object with an exact-case get(key). A key that differs only in case is deleted and never put back.
  • MetricsService.java:54 — findProgressReportsByStudyId applies no approval filter, so research outputs from progress reports on ungranted requests reach the public study page.
    Detail

1. Case-variant legacy asset keys disappear.

Take a study that carries assets = {"Publications": [...]} and was never re-registered. Both migration changesets use exact-case JSON operators, so they leave the key alone. Then on every read:

  1. assemble copies the legacy object.
  2. Line 93 runs removeIf("publications"::equalsIgnoreCase) and deletes "Publications".
  3. Line 78 looks up get("publications") — exact case — and gets null.
  4. findAssetList returns an empty list, so line 95 puts nothing back.

The key then vanishes from the registration GET and from the search index. Before this change, findMapPropValue returned the legacy map as it stood and kept it. Pick one convention for both sides: either read the legacy map case-insensitively, or remove from it case-sensitively.

2. Research outputs skip the approval gate.

The sibling query, findSummaryMetricApprovedDARsByStudyIdIncludesExpired, works hard to require a final access vote of TRUE. findProgressReportsByStudyId at DataAccessRequestDAO.java:294 checks only parent_id IS NOT NULL, a submission date, and non-archived status. The new DAO test at DataAccessRequestDAOTest.java:1397 builds a parent DAR with no election or vote and still expects both reports back. So on a public study, any signed-in caller can see outputs from a request nobody granted. If a progress report can only exist after a grant in practice, say so in the Javadoc; otherwise add the vote filter.

One nit outside the findings: findProgressReportsByDatasetId at DataAccessRequestDAO.java:318 has no production caller — only the DAO test uses it.


Codex

PI institution edits can be reverted by registration saves. PATCH updates the institution column but leaves the piInstitution study property unchanged. Registration GET reads that stale property, and a subsequent PUT writes it back into the column. Changing institution A to B—or clearing it—can therefore be undone by an unrelated registration edit. Keep both representations synchronized or make registration reads use the column.

Legacy asset edits can be silently discarded. Registration GET returns assets both top-level and inside assets. A legacy client that preserves the response and edits only assets.publications sends the unchanged top-level copy too. promotedValue() always selects that copy, so additions, edits, and deletions are ignored despite a successful save. The compatibility path needs to handle conflicting representations.

@fboulnois fboulnois left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we split this out into smaller PRs? 8k lines is not easily human-reviewable.

Aside from that, at first glance I see that tests have been added using bash scripts. I am concerned about the maintainability of this approach. Additionally, I think given their size, each could be a separate reviewable component.

Comment on lines +111 to +113
if (user == null || user.getRoles() == null) {
return false;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might also be in User.getRoles() if roles were instantiated with an empty list by default. That would also protect other callers from hitting this edge case.

@otchet-broad

Copy link
Copy Markdown
Contributor Author

I'm going to split this up into multiple PRs and remove the bash scripts. Those scripts have been helpful testing against the variants in dev and prod databases, but aren't needed moving forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants