Skip to content

Port the static interactor endpoints the site calls to node - #478

Merged
adamjohnwright merged 2 commits into
mainfrom
port/interactors
Oct 15, 2026
Merged

adamjohnwright merged 2 commits into
mainfrom
port/interactors

Conversation

@adamjohnwright

Copy link
Copy Markdown
Contributor

Four interactor endpoints that the site calls now come from node:

Endpoint Used by
GET /interactors/static/molecule/{acc}/details (with optional page/pageSize) Entity popup
GET …/molecule/enhanced/{acc}/details Interactor tables
GET …/molecule/{acc}/withEntities Interactor page
POST …/molecules/details Diagram's static interactors

PSICQUIC, uploads, tokens and summaries stay with Java.

Order. Each endpoint runs Java's Cypher as written, because the order of equal scores comes from the query plan, not from an ORDER BY. For example, TP53's 39 groups of tied scores all come back in descending dbId. Every one of the 78,872 interactions has exactly two interactors, so an Interactor is always the other one.

Matching Java's output:

  • Empty values: a missing or empty value is left out, as Java's NON_EMPTY mapper does.
  • Doubles: a whole score is written 1.0.
  • Interactor fields: an isoform is named by its variant; the alias is the first gene name of a ReferenceSequence only.
  • Paging: page and pageSize are Integers defaulting to -1. A blank value gives Java's generic 500; a negative SKIP or LIMIT gives Neo4j's message as Spring wraps it.
  • POST:
    • Checks run in Java's order: 415, then paging, then 422.
    • The accessions go into a HashSet, and its order is the answer's.
    • Queries run one at a time, as in Java.
    • When nothing is found, the 404 names the body as sent.
  • withEntities sort: entities are sorted as String.CASE_INSENSITIVE_ORDER does: the lower case of the upper case, using the JVM's own per-character mappings. These come from case-snapshot.json, generated by snapshot-case.mjs. JavaScript's mappings differ on 168 characters (İ, ß, newer Unicode). Fuzzed against the JDK over 80,000 random lists: identical.

Checked against Java on a local node: 54 requests over 12 accessions, covering all three GET forms, paging, every error and the POSTs. All identical except details.pdf, a path no node route takes; Spring answers its own 404 there, and nginx keeps that path on Java.

Mutation checks: each new test fails under a mutation of the code it covers.

🤖 Generated with Claude Code

adamjohnwright and others added 2 commits October 15, 2026 19:34
The entity popup's list (GET /interactors/static/molecule/{acc}/details,
paged or not), the interactor tables' rows (molecule/enhanced/{acc}/
details), the interactor page's rows with their Reactome entities
(molecule/{acc}/withEntities), and the POST the diagram sends for every
protein in it (molecules/details) now come from node.

Each runs Java's Cypher as written -- inside a subquery where a result
is reshaped -- because the order of equal scores is the plan's: in
TP53's 249 interactors, all 39 groups of ties come in descending dbId,
from no ORDER BY. Every interaction has two interactors (checked over
all 78,872), so an Interactor is the other one.

Written as the content service writes them, all measured:
- NON_EMPTY: a missing or empty alias, list or name is left out; an
  entry with no interactors keeps its count and loses its list.
- a whole score is 1.0; an isoform is named by its variant; the alias
  is the first gene name of a ReferenceSequence only.
- page and pageSize are Integers defaulting to -1, paging only when
  neither is: a blank one is the service's NPE (Java's generic 500), a
  negative SKIP or LIMIT Neo4j's message as Spring wraps it.
- the POST: 415, paging, 422, then the body split and trimmed into a
  HashSet whose order is the answer's, one query at a time; none found
  is a 404 naming the body as sent.
- withEntities sorts each row's entities as String.CASE_INSENSITIVE_ORDER
  does: per unit, the lower case of the upper case, by the JVM's own
  Character mappings (case-snapshot.json, from snapshot-case.mjs) --
  JavaScript's are the full mappings of a newer Unicode, and differ on
  168 characters. 80,000 random lists against the JDK: identical.

Against Java on a local node: 54 requests over twelve accessions, the
three GET forms, paging, every error and the POSTs -- identical bar a
path no node route takes (details.pdf, Spring's 404, which nginx keeps
on Java). PSICQUIC, uploads, tokens and summaries stay with Java.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #478, both now matching Java:
- graph-core unboxes page first and pageSize only when page is not -1,
  once per accession. A blank pageSize with the default page is Java's
  unpaged answer (or its 404), not a 500; a POST whose body is missing
  (422) or splits to nothing (404 naming the body) never unboxes either.
  pagingUnboxFails says where it fails.
- CustomInteraction's Record constructor reads identifier, url,
  evidenceURL, databaseName and displayName with asString(), which writes
  a missing one as the text "null", and fails on a missing dbId, score or
  evidenceCount; the details' Interactor fails on an interaction with no
  accession list. Node had left those out. None of these is missing in
  today's data (78,872 interactions, 22,792 partners counted), so this
  is the next release's safety.

Against Java: the review's three cases, a blank pageSize on a real
accession, and a blank page with one -- identical. Each new test fails
under a mutation of the code it covers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adamjohnwright
adamjohnwright merged commit 62c6bda into main Oct 15, 2026
6 checks passed
@adamjohnwright
adamjohnwright deleted the port/interactors branch October 15, 2026 20:17
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.

1 participant