Repository navigation
[DT-4274][risk=low] Define the showcase public fields and summary record (2/3) - #3190
Conversation
… and scope vocabulary ShowcasePublicField lists each public field with its source, type and nullability; ShowcaseDatasetSummary is the flat record whose components must equal that list; ShowcaseScopeKey fixes the scope vocabulary. The contracts doc records the fields, eligibility and approval gates. Split from #3184 at reviewer request; the query that fills the record stays in #3184. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rushtong
left a comment
There was a problem hiding this comment.
Review comments below. The most important one is the allowlist that comes from the Source labels (on ACCESS_URL).
| true), | ||
| ACCESS_URL( | ||
| "accessUrl", | ||
| Source.DATASET_PROPERTY, |
There was a problem hiding this comment.
datasetPropertyAllowlist() selects properties by their Source label. But this field's value is computed: the code returns it only for open/external datasets, validates it, and converts the host to punycode. By the javadoc on Source, it is DERIVED.
If someone corrects this label to DERIVED, url and consentGroup.url leave the allowlist. The #3184 query then stops reading them, and accessUrl is always null. Only the hard-coded list in testPropertyAllowlists catches this.
Suggestion: keep the allowlist separate from Source, for example with an explicit readsProperties flag or a separate list. The same label problem applies to IDENTIFIER (line 14): its source is COLUMN, but the code builds the value as DUOS- plus the padded alias.
There was a problem hiding this comment.
Fixed in 1ae0900. Each field now names the property table the projection reads (PropertyTable: NONE, DATASET, STUDY), and the allowlists come from that, not from Source. ACCESS_URL and IDENTIFIER are now DERIVED. The DERIVED Javadoc no longer says that raw inputs are never returned: a provider link that passes validation is returned as stored. testPropertySourcesReadTheirTable checks that a field labeled as a property row reads that table. The allowlists are unchanged: [numberOfParticipants, url, consentGroup.url] and [species].
| @Test | ||
| void testSerializedKeysEqualAllowlist() { | ||
| ShowcaseDatasetSummary summary = | ||
| new ShowcaseDatasetSummary(null, null, null, null, null, null, null, null, null); |
There was a problem hiding this comment.
This test builds a record in which all fields are null, including identifier, title and consentCodes, which the enum marks nullable=false. The test still passes. No test ties nullable or type to the record components. If participantCount changes to String, or consentCodes becomes null, no test fails, and the approved field table no longer matches the code.
Suggestion: assert that each type string matches the Java type of its record component. Or remove the metadata that no test checks.
There was a problem hiding this comment.
| new ShowcaseDatasetSummary(null, null, null, null, null, null, null, null, null); | ||
| JsonObject json = gson.toJsonTree(summary).getAsJsonObject(); | ||
|
|
||
| assertEquals(ShowcasePublicField.jsonNames(), List.copyOf(json.keySet())); |
There was a problem hiding this comment.
This assertion depends on key order. Gson gets the order from Class.getDeclaredFields(), and the Java specification does not define that order. A different JDK can change the order and fail this test, although the record and the enum still match.
Suggestion: compare the keys as sets here. testRecordComponentsEqualAllowlist already checks the order with getRecordComponents(), which has a defined order.
There was a problem hiding this comment.
Fixed in 1ae0900. The keys are compared as a set. testRecordComponentsEqualAllowlist still checks the order.
| * A scope with several keys matches rows that satisfy every key; the values of one key are | ||
| * alternatives. | ||
| */ | ||
| public enum ShowcaseScopeKey { |
There was a problem hiding this comment.
Nothing calls this enum, in this PR or in #3184. The contracts doc already records the vocabulary. Can we add the enum in S2.2, together with the query that uses it and its tests?
There was a problem hiding this comment.
Agreed, I'll move it to S2.2. The contracts doc keeps the agreed vocabulary (dac, exact DAC names), which is what DT-4274 asks for. The enum will come with the query that uses it and its tests.
| return jsonName; | ||
| } | ||
|
|
||
| public String getColumn() { |
There was a problem hiding this comment.
getColumn() returns SQL text (dac.name). This makes it easy for S2.2 to put the value into a query with @Define or a string template. That moves SQL out of the DAO, where reviewers look for it. It is safe today because the value is a constant. But a later key with a computed column (LOWER(...) or a JSON path) can then go into SQL without review.
Suggestion: keep the map from key to column in the DAO query, and keep only the JSON key in this enum.
There was a problem hiding this comment.
Moving the enum to S2.2 removes getColumn() from this PR. In S2.2 the key-to-column mapping will live in the DAO query, as you suggest.
| return jsonName; | ||
| } | ||
|
|
||
| public Source getSource() { |
There was a problem hiding this comment.
Nothing calls getSource(), getType(), getUnits() or isNullable(), in this PR or in #3184. These getters look like the source of truth, but nothing makes sure that they are correct. Remove them, or add a test or doc generator that reads them.
|
|
||
| Record for [DT-4274](https://broadworkbench.atlassian.net/browse/DT-4274). `ShowcasePublicField` | ||
| defines the fields, `ShowcaseDatasetSummary` returns them in this order, and | ||
| `DatasetDAO.findShowcaseSummaries` reads them. |
There was a problem hiding this comment.
DatasetDAO.findShowcaseSummaries arrives only in #3184. The merge order is #3189, this PR, then #3184. Between those merges, develop has a doc that describes a method and query rules (case-insensitive matching, the lowest-id row, the 200-identifier cap) that do not exist yet. Move these sentences to #3184, or mark them as pending.
| } | ||
|
|
||
| public static List<String> jsonNames() { | ||
| return List.of(values()).stream().map(ShowcasePublicField::getJsonName).toList(); |
There was a problem hiding this comment.
Nit: Arrays.stream(values()) does the same job and does not make the extra list. This also applies at line 133.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eld types against the record Address review on #3190: a PropertyTable per field drives the allowlists, so relabeling a Source cannot change which rows the projection reads. IDENTIFIER and ACCESS_URL are DERIVED. Tests check each field's type against its record component, compare serialized keys as a set and check that property sources read their table. Drop the unused units metadata, and mark the contracts doc's query rules as pending #3184. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review on #3190: nothing calls the enum yet, and its SQL column text belongs in the DAO query. The contracts doc keeps the agreed scope vocabulary; S2.2 adds the code with the query that uses it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rushtong
left a comment
There was a problem hiding this comment.
Thank you for the updates, they all look great 👍🏽
Addresses
https://broadworkbench.atlassian.net/browse/DT-4274
Security risk: low — defines the public field list and its record for a future public endpoint (S2.2). Nothing reads them yet.
Summary
Part 2 of 3 of the DT-4274 showcase catalog projection, split from #3184 at reviewer request. Stacked on #3189; merge order: #3189, this PR, then #3184.
ShowcasePublicFieldnames each public field with its source, type, units and nullability.ShowcaseDatasetSummaryis the flat record whose components must equal that list.ShowcaseDatasetSummaryTestfails if a field is added to one and not the other, and checks that the serialized keys (withGsonUtil.getNullSerializingInstance()from [DT-4274][risk=low] Add consent-code summaries and a null-serializing Gson (1/3) #3189) equalShowcasePublicField.jsonNames().scopevocabulary: the contracts doc records it (dac, exact DAC names), as DT-4274 asks. The enum moved to S2.2 at review (discussion); S2.2 adds it with the query that uses it and keeps the key-to-column mapping in the DAO query.docs/plans/data-showcase-contracts.mdrecords the field table, eligibility, scope vocabulary and the S2.2 requirements. The delivery plan points to it. The eligibility section describes the query in [DT-4274][risk=low] Add the showcase catalog projection query and eligibility filter (3/3) #3184.Reviewers: data stewards and AppSec
The field table in the contracts doc (section "S2.1 catalog projection") is an open gate. Please approve or change:
consentCodesreturns codes only (GRU,HMB, ...), not the translated text.availableDateisdataset.create_datefor open/external anddataset.dac_approval_dateotherwise. The columns have no time zone, so the date is UTC only if the writer ran in UTC.speciesis free text with no length limit (it could hold an email or a note): approve as is, truncate, or omit?Testing
ShowcaseDatasetSummaryTestpasses locally (5 tests). The code and docs are unchanged from #3184.Have you read CONTRIBUTING.md lately? If not, do that first.
🤖 Generated with Claude Code