From 0c64b858b78876a1e7e96988ac7825d9941ad11e Mon Sep 17 00:00:00 2001 From: Adam Wright Date: Tue, 27 Oct 2026 00:58:07 +0000 Subject: [PATCH 1/2] Spec 016 T014: one icon's page, recorded A test opens an icon's own page (/community/icon-lib/R-ICO-012449), recorded against beta's services, so /search/icon/{id} has an answer the check reads: calls.ts loses its gap, and the answer holds to the contract's IconEntry (a contract iconName made a number fails on it). Co-Authored-By: Claude Opus 5.5 --- e2e/content-pages.spec.ts | 8 ++++++++ e2e/har/1f4df14a0a07f02f5d3570058da470c7d43b7683.json | 1 + ...der-backend-data-an-icon-s-own-page-shows-t-fcbdb0.har | 1 + tools/api-types/calls.ts | 2 +- 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 e2e/har/1f4df14a0a07f02f5d3570058da470c7d43b7683.json create mode 100644 e2e/har/content-pages--content-pages-render-backend-data-an-icon-s-own-page-shows-t-fcbdb0.har diff --git a/e2e/content-pages.spec.ts b/e2e/content-pages.spec.ts index d7ce08a00..29557a855 100644 --- a/e2e/content-pages.spec.ts +++ b/e2e/content-pages.spec.ts @@ -138,6 +138,14 @@ test.describe('Content pages render backend data', () => { await expect(page.locator('body')).not.toContainText('Loading', { timeout: LOAD }); }); + test("an icon's own page shows that icon, by its id", async ({ page }) => { + // /search/icon/{id}: the one icon, as a link to it opens it. + await page.goto('/community/icon-lib/R-ICO-012449'); + await expect(page.locator('.detail-stid')).toHaveText('R-ICO-012449', { timeout: LOAD }); + await expect(page.locator('.detail-summation')).not.toBeEmpty(); + await expect(page.locator('.detail-preview img')).toBeVisible(); + }); + test('a CMS article page renders its body', async ({ page }) => { // page.component.ts fetches the compiled content JSON and renders markdown. await page.goto('/documentation/userguide'); diff --git a/e2e/har/1f4df14a0a07f02f5d3570058da470c7d43b7683.json b/e2e/har/1f4df14a0a07f02f5d3570058da470c7d43b7683.json new file mode 100644 index 000000000..1a12c2f12 --- /dev/null +++ b/e2e/har/1f4df14a0a07f02f5d3570058da470c7d43b7683.json @@ -0,0 +1 @@ +{"dbId":"012449","stId":"R-ICO-012449","id":"R-ICO-012449","name":"ASPPs","type":"Icon","exactType":"Icon","species":["Homo sapiens"],"summation":"Family of Apoptosis-stimulating of p53 proteins","hasReferenceEntity":false,"hasEHLD":false,"iconName":"ASPPs","iconCategories":["protein"],"iconCuratorName":"Marija Orlic-Milacic","iconCuratorOrcidId":"0000-0002-3218-5631","iconDesignerName":"Cristoffer Sevilla","iconDesignerUrl":"https://graphsolutions.co.uk","iconReferences":["Q96KQ4","UNIPROT:Q96KQ4","Q13625","UNIPROT:Q13625"],"iconPhysicalEntities":[{"stId":"R-HSA-6799775","type":"ReferenceGeneProduct","name":"PPP1R13B","compartments":"nucleoplasm","displayName":"PPP1R13B [nucleoplasm]"},{"stId":"uniprot:Q96KQ4","type":"ReferenceGeneProduct","name":"PPP1R13B","compartments":"null","displayName":"PPP1R13B [null]"},{"stId":"R-HSA-6799780","type":"ReferenceGeneProduct","name":"TP53BP2","compartments":"nucleoplasm","displayName":"TP53BP2 [nucleoplasm]"},{"stId":"uniprot:Q13625","type":"ReferenceGeneProduct","name":"TP53BP2","compartments":"null","displayName":"TP53BP2 [null]"}],"iconEhlds":["R-HSA-5633007"],"icon":true} \ No newline at end of file diff --git a/e2e/har/content-pages--content-pages-render-backend-data-an-icon-s-own-page-shows-t-fcbdb0.har b/e2e/har/content-pages--content-pages-render-backend-data-an-icon-s-own-page-shows-t-fcbdb0.har new file mode 100644 index 000000000..c857facd2 --- /dev/null +++ b/e2e/har/content-pages--content-pages-render-backend-data-an-icon-s-own-page-shows-t-fcbdb0.har @@ -0,0 +1 @@ +{"log":{"version":"1.2","entries":[{"request":{"method":"GET","url":"http://localhost:4330/ContentService/search/icon/facet"},"response":{"status":200,"headers":[{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"connection","value":"close"},{"name":"content-length","value":"389"},{"name":"content-type","value":"application/json;charset=UTF-8"},{"name":"date","value":"Tue, 27 Oct 2026 00:55:12 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"7fee71d851dbc0883e01198a2731b5c3b66c49d3.json"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/search/icon/R-ICO-012449"},"response":{"status":200,"headers":[{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"connection","value":"close"},{"name":"content-length","value":"1132"},{"name":"content-type","value":"application/json;charset=UTF-8"},{"name":"date","value":"Tue, 27 Oct 2026 00:55:12 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"1f4df14a0a07f02f5d3570058da470c7d43b7683.json"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/species/main"},"response":{"status":200,"headers":[{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"connection","value":"close"},{"name":"content-length","value":"3237"},{"name":"content-type","value":"application/json;charset=UTF-8"},{"name":"date","value":"Tue, 27 Oct 2026 00:55:12 GMT"}],"content":{"mimeType":"application/json;charset=UTF-8","_file":"b1aee4d715fbe327136decb78e604b0ab8464ced.json"}}},{"request":{"method":"GET","url":"http://localhost:4330/ContentService/data/database/version"},"response":{"status":200,"headers":[{"name":"Vary","value":"Origin"},{"name":"access-control-allow-origin","value":"*"},{"name":"connection","value":"close"},{"name":"content-length","value":"2"},{"name":"content-type","value":"text/plain;charset=UTF-8"},{"name":"date","value":"Tue, 27 Oct 2026 00:55:12 GMT"}],"content":{"mimeType":"text/plain;charset=UTF-8","_file":"812ed4562d3211363a7b813aa9cd2cf042b63bb2.txt"}}}]}} \ No newline at end of file diff --git a/tools/api-types/calls.ts b/tools/api-types/calls.ts index c826111e1..7175ae7ca 100644 --- a/tools/api-types/calls.ts +++ b/tools/api-types/calls.ts @@ -166,7 +166,7 @@ export const CALLS: Call[] = [ { file: `${WEB}/services/detail-data.service.ts`, method: 'fetchEnhancedData', http: 'GET', route: '/data/query/enhanced/v2/{id}', type: 'DatabaseObject', reads: 'T' }, { file: `${WEB}/services/icon.service.ts`, method: 'getFacets', http: 'GET', route: '/search/icon/facet', type: 'IconFacetResponse' }, { file: `${WEB}/services/icon.service.ts`, method: 'queryIcons', http: 'GET', route: '/search/icon/query', type: 'IconResult' }, - { file: `${WEB}/services/icon.service.ts`, method: 'getIcon', http: 'GET', route: '/search/icon/{id}', type: 'IconEntry', gap: 'the recordings never open one icon' }, + { file: `${WEB}/services/icon.service.ts`, method: 'getIcon', http: 'GET', route: '/search/icon/{id}', type: 'IconEntry' }, { file: `${WEB}/services/search.service.ts`, method: 'search', http: 'GET', route: '/search/query', type: 'SearchResult' }, { file: `${WEB}/services/search.service.ts`, method: 'getFacets', http: 'GET', route: '/search/facet_query', type: 'FacetResponse' }, { file: `${WEB}/services/search.service.ts`, method: 'getAllFacets', http: 'GET', route: '/search/facet', type: 'FacetResponse', gap: 'the recordings always search with a query' }, From 9f67ef299c5dd662ae4f6660dabecb5b0acc2c66 Mon Sep 17 00:00:00 2001 From: Adam Wright Date: Tue, 27 Oct 2026 01:52:52 +0000 Subject: [PATCH 2/2] Spec 016 T014, T015: the single icon recorded; spec 013's T019 done The single-icon test's tick, and spec 013's T019: the icon entry, its last item, went in with the search family's contract (#684-#704). Co-Authored-By: Claude Opus 5.5 --- specs/013-server-types/tasks.md | 2 +- specs/016-api-contract/tasks.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/013-server-types/tasks.md b/specs/013-server-types/tasks.md index 39af98938..c3c682745 100644 --- a/specs/013-server-types/tasks.md +++ b/specs/013-server-types/tasks.md @@ -55,7 +55,7 @@ Each task: rename to `.ts`, update imports, file names that name it (R3 list), f ## Phase 4: The answers the services write (US3) -- [ ] T019 [US3] Where the site reads the same answer (spec 012's call table), the content service writes it through the site's model type, one definition; show a field renamed in the shared type failing in both. Done for the contents lists (toc, doi, contributors), the schema attribute and referral tables and the schema model (#561). Schema min and instance referrers too (#562), with the site's `SimpleDatabaseObject` corrected to what is written (no `stId` on an InstanceEdit or Species). The diagram and species-overview flags and the PSICQUIC resources too (#563); spellcheck and suggest are `string[]` on both sides already. Then (#676, replacing #675) the pathway citation; the facets and icon facets; and locationsInPWB -- the last two decided 26 Oct 2026 to follow what is written, measured where it mattered. The facets: the site's `FacetCount` and `IconCategory` leave the name optional and `FacetResponse` its four facets, as the service (and Java) leave out an unnamed count and a facet Solr did not count; the site's readers show only named counts; node writes them as the site's `FacetResponse` and `IconFacetResponse`, its `FacetContainer` being the site's `FacetCount`. locationsInPWB: over 179 recorded answers (1,694 nodes) `diagram` is never written and `species` is absent on species-less objects (56), so the site's `PathwayBrowserNode` drops `diagram` and makes `species` optional; stId, name, type and url are in every one and stay required; node writes them through the site's type and, as Java does, leaves out one a node lacks rather than failing (never seen). The icon library's `iconCategoriesFacet` is optional too, as the search facets are. Then (#677) the interactors (the molecules' details, the enhanced details, withEntities): their writers keep writing JSON text, member by member, for Java's numbers, but `nonEmptyJson` now checks each member's name and value against the site's type T -- a field renamed or retyped there fails here. It does not check that a required member is written (any value may be left out) or what JSON already written holds (the nested lists, checked only by their own writers). The site's `CustomInteraction` gains the `url` it is written with: the recorded answers' 89 known problems on that route are 0 now. Then (#678) the person event lists: over the recorded answers (8 distinct requests, 14 events) each carries the edit's dateTime and authorDbId and its labels, and one a DOI, which the site's `SimpleEvent` lacked; it has them now, node writes each event as that type, and the route's 31 known problems are 0. Left: the icon entry (the search family's entry, built field by field from Solr's documents with nothing typed). (Likewise, a PSICQUIC service whose registry name is empty is written without the `name` the site's `PsicquicResource` requires, as Java writes it; the real registry has none.) +- [x] T019 [US3] Where the site reads the same answer (spec 012's call table), the content service writes it through the site's model type, one definition; show a field renamed in the shared type failing in both. Done for the contents lists (toc, doi, contributors), the schema attribute and referral tables and the schema model (#561). Schema min and instance referrers too (#562), with the site's `SimpleDatabaseObject` corrected to what is written (no `stId` on an InstanceEdit or Species). The diagram and species-overview flags and the PSICQUIC resources too (#563); spellcheck and suggest are `string[]` on both sides already. Then (#676, replacing #675) the pathway citation; the facets and icon facets; and locationsInPWB -- the last two decided 26 Oct 2026 to follow what is written, measured where it mattered. The facets: the site's `FacetCount` and `IconCategory` leave the name optional and `FacetResponse` its four facets, as the service (and Java) leave out an unnamed count and a facet Solr did not count; the site's readers show only named counts; node writes them as the site's `FacetResponse` and `IconFacetResponse`, its `FacetContainer` being the site's `FacetCount`. locationsInPWB: over 179 recorded answers (1,694 nodes) `diagram` is never written and `species` is absent on species-less objects (56), so the site's `PathwayBrowserNode` drops `diagram` and makes `species` optional; stId, name, type and url are in every one and stay required; node writes them through the site's type and, as Java does, leaves out one a node lacks rather than failing (never seen). The icon library's `iconCategoriesFacet` is optional too, as the search facets are. Then (#677) the interactors (the molecules' details, the enhanced details, withEntities): their writers keep writing JSON text, member by member, for Java's numbers, but `nonEmptyJson` now checks each member's name and value against the site's type T -- a field renamed or retyped there fails here. It does not check that a required member is written (any value may be left out) or what JSON already written holds (the nested lists, checked only by their own writers). The site's `CustomInteraction` gains the `url` it is written with: the recorded answers' 89 known problems on that route are 0 now. Then (#678) the person event lists: over the recorded answers (8 distinct requests, 14 events) each carries the edit's dateTime and authorDbId and its labels, and one a DOI, which the site's `SimpleEvent` lacked; it has them now, node writes each event as that type, and the route's 31 known problems are 0. The icon entry, the last left, went in with the search family's whole contract in spec 016 (T002–T014): the facets (#684), the entry and its typed writer (#689), its readers (#691, #697), the search answers (#703) and the single icon's recording (#704). The search routes' known problems are 0. (Likewise, a PSICQUIC service whose registry name is empty is written without the `name` the site's `PsicquicResource` requires, as Java writes it; the real registry has none.) - [ ] T020 [US3] Answers only the services write: types beside the service. --- diff --git a/specs/016-api-contract/tasks.md b/specs/016-api-contract/tasks.md index 889949b58..be3894361 100644 --- a/specs/016-api-contract/tasks.md +++ b/specs/016-api-contract/tasks.md @@ -74,8 +74,8 @@ description: 'Tasks for spec 016: one API contract for the content service and t - [x] T013 [US3] Write `ResultGroup`, `GroupedResult` (with `numberOfGroups`), the diagram and species-overview result types and `IconResult` (with `rowCount` and `typeName`) in `projects/contract/search.ts`, and point their writers in `search-endpoints.ts` and `search-diagram-endpoints.ts`, and their readers, at them. - Done in #703. The contract's `ResultGroup`, `GroupedResult`, `DiagramResult`, `TargetResult` and `IconResult`, each taking its entry type as a named argument: the service writes them with its `WrittenEntry`, a reader reads them with the view. A group's `entries` is optional (an ungrouped page past the end has none) and so is its `typeName` (a group Solr names with nothing); the search page settles both once when the answer arrives (`ShownGroup`), and the pathway browser's search shows only named type facets, as the site's does. A target's empty term or resource is left out, so both are optional. A grouping without `matches` is skipped, as SolrJ's `QueryResponse.extractGroupedInfo` skips it (read from its bytecode, from #700's review): the first grouping that has its matches is read, and none is no result, Java's 500 (a spec of two groupings, the first without matches, fails on the first-grouping reading). The search routes' known problems are 0, the whole 12,164. From #697's review, the pathway browser's icon tooltip takes `iconName`, never highlighted. -- [ ] T014 [US3] Record an answer for `/search/icon/{id}` (already in `tools/api-types/calls.ts` as `IconEntry`, with a `gap`) through the e2e suite and remove the `gap` (record rewrites three HARs: check the diff). Type its writer. -- [ ] T015 [US3] Tick spec 013 T019 in `specs/013-server-types/tasks.md` with T002–T014's PR numbers. +- [x] T014 [US3] Record an answer for `/search/icon/{id}` (already in `tools/api-types/calls.ts` as `IconEntry`, with a `gap`) through the e2e suite and remove the `gap` (record rewrites three HARs: check the diff). Type its writer. — #704. `e2e/content-pages.spec.ts` opens `/community/icon-lib/R-ICO-012449`, recorded against beta's services (`CONTENT_NODE_TARGET` at its port): the single-icon answer holds to the contract's `IconEntry`, and a contract `iconName` made a number fails on it, so the check reads it. +- [x] T015 [US3] Tick spec 013 T019 in `specs/013-server-types/tasks.md` with T002–T014's PR numbers. — #704. ## Phase 5: User Story 1 - A change fails on both sides (P1)