Repository navigation
OpenConceptLab/ocl_online#451 | The FHIR host serves its own Swagger page of the terminology operations - #957
Merged
snyaggarwal merged 3 commits intoOct 23, 2026
Conversation
…page of the terminology operations On the FHIR host, /swagger/ (and swagger.json, swagger.yaml) describes the FHIR terminology service alone: search and read of CodeSystem, ValueSet and ConceptMap, $lookup, $validate-code, $expand, $translate, metadata and $versions, at /fhir and the owner bases. Each operation is listed once, tagged by resource type, with its query parameters, a Parameters body for POST, and OperationOutcome errors. Writes and the slash and no-slash duplicates are left out, and Try it out calls the FHIR host itself. When FHIR has its own host, the API's Swagger leaves the FHIR paths out (they answer 404 there) and links to the FHIR page. When FHIR shares the host, the API's Swagger lists the same curated operations instead of the generated ones. The other API operations are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8
…ng replaces When a coding or codeableConcept is given, the operations don't read the separate inputs it stands for: code and system for $lookup (version still selects the code system version), code and display for CodeSystem $validate-code (url and version still name the code system), code, system, systemVersion and display for ValueSet $validate-code, and code, system and version for $translate. Each operation's coding parameter now names them, and each of those inputs says it isn't read alongside a coding. A GET coding token carries no version or display; a POSTed valueCoding does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8
…names an instance alongside a coding At the type level, $lookup takes the code system from a coding, so a separate system has no effect; at an instance, a separate system must still name that code system, as without a coding. Its description now says so instead of saying it isn't read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8
Member
Author
Review loop (Codex passes 1–3, simplification)
The full findings are on the tracking ticket. |
snyaggarwal
approved these changes
Oct 23, 2026
snyaggarwal
pushed a commit
that referenced
this pull request
Oct 26, 2026
…VCM transactions (#956) * OpenConceptLab/ocl_online#451 | FHIR requests and errors use native FHIR JSON and XML FHIR views now accept and return FHIR JSON and XML through the shared core/fhir/base.py layer. _format overrides Accept, media aliases and quality values are honoured, and unsupported FHIR versions are refused. XML is converted locally with fhir.resources' R4B models (fhir.resources[xml] 8.3.0, up from 7.1.0), so XML conversion no longer calls the validator service and the FHIR_VALIDATOR_URL application setting is removed. The shared CodeSystem converter (core/code_systems/converter.py) moves to the R4B model and model_dump(). XML follows FHIR element order and preserves the values JSON carries. Unsupported elements and shapes are omitted, a single-valued element keeps its first item, and characters XML cannot represent become replacement characters. Errors from views, routing and middleware return OperationOutcome with terminology issue coding; server errors become 422 responses. FhirErrorMiddleware runs outermost, and core/wsgi.py installs FhirWSGIRequest, so a malformed Content-Type parameter or a non-text charset on a FHIR path is a FHIR JSON 400 rather than an error page; other paths are unchanged. metadata also answers with a trailing slash, and names the request's own FHIR base. Tests cover negotiation, JSON and XML input and output, malformed headers and queries, serializer outcomes and middleware failures. Work package WP1 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | Canonical lookups preserve registry entries and support FHIR version rules Canonical resolution can select a repository type and require a released default version. The new core/fhir/resolve.py applies the FHIR base's namespace and access rules, preserves the resource type of OCL paths, and uses HEAD only when named. Repositories without a release are absent. Unavailable repositories receive the same not-found answer as missing ones. $resolveReference accepts namespace for expressions that name none when the URL registry is enabled; an expression's own namespace wins. Existing shared-resolver callers keep their default resolution rules. Registry reads no longer save or reindex entries. Repository writes refresh cached links, and ConceptContainerModel.delete in core/common/models.py preserves entries and resolves their links again after deletion. Link updates save only their own fields, preserving concurrent edits. Tests cover both registry settings, resource types, namespaces, named versions and query counts, plus link maintenance, concurrent edits and retries of interrupted repository or version deletes. Work package WP2 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | FHIR search and read return the releases a caller may view CodeSystem, ValueSet and ConceptMap search and read share core/fhir/search.py. Search lists released versions with real totals, paging links and SUBSETTED summaries. Global reads use an opaque id per release; owner reads use the mnemonic and a named version or the newest visible release. meta.versionId distinguishes releases at both bases. Search supports SVCM parameters, token systems, date comparisons, comma alternatives and repeated criteria. String searches ignore case and accents except with :exact; identifiers and canonical URLs match the values resources show. CodeSystem reads include all concepts up to 1,000, then report not-present. A release is visible only with a viewable active HEAD. The global base shows public content; owner bases apply the caller's rights. Source and reference filters can match any owner's source the caller may view. ConceptMap creation by PUT or PATCH checks ownership, and deletion of a missing map returns 404. Search evaluates its criteria once, batches canonical lookups, accepts at most 20 values per parameter and 50 overall, and bounds database work to five seconds. Tests cover search semantics, paging, read content, access, write ownership, query counts and too-costly outcomes. Work package WP3 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | Missing FHIR operation inputs get consistent, typed error outcomes Required-input errors in $validate-code and $translate now use the shared FHIR InvalidError. A missing required input is a 400 OperationOutcome naming it, with terminology issue coding. CodeSystem $validate-code for an unknown or unavailable system still answers result=false, with or without a code. core/fhir/tests_operation_bounds.py checks FHIR JSON POSTs and the issue coding, alongside bounded concept and mapping reads and unchanged answers for list-only options. It replaces master's copy of these bounds tests (core/code_systems/tests_operation_bounds.py, which came from this work) with core/fhir/tests_operation_bounds.py. Its $translate cases run by GET here; the POST cases come back with the $translate work later in the series. The operation input checks of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | FHIR lookup and validation answer for the requested version and value set $lookup and CodeSystem and ValueSet $validate-code now use shared operations in core/fhir/ops and an R4 response adapter. GET and POST accept separate inputs or a Coding, with instance routes for lookup and validation. CodeableConcept validation accepts one coding. Operations resolve the newest release the caller may view unless a version is named. Without the URL registry, a canonical at the global base resolves only among repositories the caller is shown, so a private copy never hides a public one; the shared resolver in core/common/models.py gains optional heads and head_shown arguments for this, and other callers keep their defaults. Owner instance routes accept HEAD when explicitly named. Conflicting systems or versions are reported, deleted concepts are absent, and unavailable repositories receive the same answers as missing ones. $lookup returns the resolved version, display, designations and inactive property. Validation returns the code, system, version, display and issues; display checks accept any concept name regardless of case, and retired codes remain valid with a warning. ValueSet membership comes from its default expansion, reporting a visible holding release: the explicit pin, then the evaluated pin, then the newest. Tests cover GET and POST, instance routes, encoded codes, display languages, version conflicts, expansion membership and access with both registry settings. Query checks keep reads focused on the named code as repositories grow. Work package WP4 + WP5 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | FHIR translation returns the requested map's live mappings and R4 equivalences $translate now reads the resolved ConceptMap version's own live mappings through core/fhir/ops/translate.py. GET takes code and system, or a system|code coding token; a POSTed Parameters also takes a Coding or a CodeableConcept. url and conceptMapVersion select the map. Without url, a forward translation uses the input system's own map, and a reverse one uses targetsystem's (with no targetsystem there is no map). Map types and stored relationship spellings become R4 equivalences. Reverse translation swaps direction and wider or narrower meaning; code-system versions and targetsystem narrow the matches. The input code must belong to its source scope, or target scope in reverse, with ValueSet paths resolved as value sets. Translation, ConceptMap reads and system searches name mapping sides consistently: a visible source's canonical URL, otherwise the mapping's stored URL. Deleted or retired mappings are omitted, each match names its map, and results list at most 1,000 matches within the search time bound. Tests cover versions, aliases, scopes, reverse equivalences, GET and POST, JSON and XML, access rules and bounded queries. ConceptMap reads group repeated source codes together, and writes store equivalences as the corresponding map types. Work package WP6 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | FHIR expansion pages the requested value set's stored members ValueSet $expand now serves the resolved version's default stored expansion at type and instance routes. GET and POST only read it; neither computes nor saves an expansion. Missing, unfinished or unresolved expansions return a 422 OperationOutcome. Responses carry the ValueSet, expansion identifier and timestamp, total, offset, echoed inputs and used code systems. Each visible member has its system, code and display, with a holding release when available, inactive status and optional designations. count and offset page up to 1,000 members; activeOnly, word-prefix filter and displayLanguage shape the answer. Members report their release's own canonical URL when present, and ValueSet validation accepts that system too. Filters match decoded codes and names. Inputs requiring a newly computed expansion are refused explicitly, and members whose code systems the caller cannot view contribute neither content nor totals. Tests cover paging, filtering, languages, designations, release selection, access and unsupported inputs with both registry settings. They also check that POST writes nothing, queries stay bounded and timeouts return too- costly outcomes. Work package WP7 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | FHIR metadata describes the operations and code-system versions each base serves CapabilityStatement is built from the routes and search definitions in core/fhir/capabilities.py. It advertises FHIR R4, the available interactions and operations, SVCM terminology support, and the request's own base URL. metadata and GET $versions work at global, organization and user bases, with either trailing-slash form. TerminologyCapabilities lists the code systems the caller may view and their released versions, each under its canonical URL or its OCL CodeSystem path. The default release is marked, and expansion and translation capabilities follow the routed operations. Business versions are listed as stored and resolve without a second decoding changing their meaning. OCL-to-FHIR path conversion replaces only the repository-type segment, preserving owner and repository names. Tests compare advertised routes, parameters and versions with the implementation, check JSON and XML at every base, and cover registry choices, shared canonicals, access, query counts and timeouts. Work package WP8 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | ConceptMap system searches use indexed candidates and check each release's mappings ConceptMap source-system and target-system searches use core/fhir/mapping_index.py to find candidate repositories and versions. The database checks each candidate through its own live mappings, avoiding a join over every mapping of a heavily mapped source. Index requests are scoped by the versions the database shows the caller, use one bounded request per parameter and require a complete answer. Time or aggregation limits return too-costly; unavailable or partial index responses return a 422 OperationOutcome. Recent releases of repositories found in the index are checked for seven days even if the release is not indexed yet. Matches through a source's canonical URL need the index: repositories with no indexed mappings, and older unindexed releases, are absent until indexed. Matches on the URL a mapping stores, external URLs and OCL paths alike, don't need the mappings to be indexed, but a search that also names a visible source's canonical URL needs a complete answer from the index. Tests cover candidate verification, case-distinct owners, current access rules, recent releases, index lag, incomplete responses, timeouts and fixed database query counts with both registry settings. Work package WP3b of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | CodeSystem and ValueSet titles fall back to the name when no full name is set CodeSystem and ValueSet reads and search entries use the repository name as the title when the full name is null, empty or only whitespace. A nonblank full name remains the title, and title writes still update the full name. Title searches match that same displayed value for prefix, :exact and :contains searches. Tests cover both resource types at global and owner bases, including Unicode whitespace and names that differ from a nonblank full name. Work package WP3c of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | A named FHIR version resolves to a visible repository that holds it When the first repository selected for a canonical URL cannot serve the named release, FHIR resolution can select the lowest-id visible repository with that canonical that holds it. A named unreleased version or HEAD remains limited to the first eligible repository; unavailable holders are not served. This applies where repositories sharing the canonical determine resolution. An explicit registry entry still decides, owner bases stay within their owner, and requests without a version keep their default. TerminologyCapabilities also lists the additional releases served by name under the canonical, preserving the default. Tests cover lookup, validation, expansion, translation, search and capabilities across callers, bases and both registry settings, including retired, deleted and unavailable versions. Query-count tests retain the shared resolver's existing differences and account for the HEAD read used by $resolveReference's view-access check. Work package WP2b of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | Value set members stored as root concept rows report a holding release $expand and ValueSet $validate-code can report a release for a member stored as its concept's versioned-object row, which only HEAD holds. When no visible live release holds the member's exact row, a release holding another active version of that concept supplies the version. A release holding the exact member takes precedence. Within each choice, the expansion's explicit pin wins, then its evaluated pin, then the newest release. Validation keeps the stored member's own display, so the coding returned by expansion validates with that display. Tests cover root and version rows, pins, named system versions, deleted concept versions, unavailable releases and competing members with the same code. They check expansion and validation together and preserve the query count. Work package WP10 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | FHIR operations reject parameters with competing values and explain ConceptMap URI searches A POSTed Parameters entry with more than one value[x], resource or part is now a 400 OperationOutcome naming the parameter, including nested parts. A primitive's _value[x] counts as its value[x], so its own extensions remain valid while another value choice is refused. CapabilityStatement documents that ConceptMap source-uri and target-uri match the groups' systems, since ConceptMaps store no source or target value set. Tests cover value order, nested and extension-only values, and the search documentation at every base. Work package WP11 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | Root concept members report the newest visible release that holds the concept $expand and ValueSet $validate-code now report a versioned-object member in the newest visible live release holding an active version of its concept, regardless of expansion pins. Such a row carries the concept's newest content. Members whose exact rows belong to releases still follow the explicit pin, then the evaluated pin, then the newest. Expansion reads the links of the relevant repositories' newest releases together and looks up older releases only for unresolved members. The page and used-codesystem use the same rules for selecting releases. used- codesystem reads every visible member's id and release (count=0 included); full concepts and names are read only for the page. Tests cover pins, several repositories, unavailable or deleted versions, older holding releases and members with no holding release. They verify expansion and validation agree, query counts do not grow with member count, and members held in the newest releases need no individual fallback. Work package WP12 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | Filtered expansions match members once for both the total and the page $expand with a filter reads matching member ids once in id order. Their number supplies total and a slice supplies the requested page, so selecting the page no longer repeats the name filter. Unfiltered expansions keep their existing queries. Tests check a page larger than the matches, paging with an offset, count=0, an offset beyond the matches and no matches. Each filtered request matches names once, and an empty page reads no page concepts or names. Work package WP13 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | Instance operations serve named unreleased versions; capped reads say they're partial At an owner base, the instance-level operations (CodeSystem $lookup and $validate-code, ValueSet $validate-code and $expand) resolve a named version the read doesn't find as their type-level operations do, with the same visibility rule: a named, public, unreleased version is served, and one the caller may not see answers exactly as a missing one. Released versions the read finds keep their path and statement count, and reads still return released versions only. A version the resolver returns carries its repository's canonical URL, from the HEAD it already reads. A resolved version accepts that URL as a system or url even when its own canonical URL differs; answers name the version's own canonical URL when it has one, else its repository's, so a version named by an OCL path, or served unreleased at an owner base, no longer answers with the path. A ValueSet read builds compose from at most 1,000 concept references in id order, and a ConceptMap read builds group from at most 1,000 live mappings; each reads one more to know when it was cut and then tags meta SUBSETTED, keeping the tags meta already has. The FHIR answer to disabled anonymous access carries only its detail text. Unused parameter serializers, FhirParameters.names() and MultipleMatchesError are removed. Work package WP14 of OpenConceptLab/ocl_online#451. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | Only /fhir/ and owner bases are FHIR paths A path is a FHIR path under /fhir/, or under an owner base (/orgs/{org}/, /users/{user}/) when it names a FHIR resource type, metadata or $versions. Other paths whose third segment is a resource type, such as a current user's source named CodeSystem (/user/sources/CodeSystem/), keep their own error bodies and statuses, and the API host serves them when FHIR has its own host. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | The FHIR host serves its own Swagger page of the terminology operations (#957) * OpenConceptLab/ocl_online#451 | The FHIR host serves its own Swagger page of the terminology operations On the FHIR host, /swagger/ (and swagger.json, swagger.yaml) describes the FHIR terminology service alone: search and read of CodeSystem, ValueSet and ConceptMap, $lookup, $validate-code, $expand, $translate, metadata and $versions, at /fhir and the owner bases. Each operation is listed once, tagged by resource type, with its query parameters, a Parameters body for POST, and OperationOutcome errors. Writes and the slash and no-slash duplicates are left out, and Try it out calls the FHIR host itself. When FHIR has its own host, the API's Swagger leaves the FHIR paths out (they answer 404 there) and links to the FHIR page. When FHIR shares the host, the API's Swagger lists the same curated operations instead of the generated ones. The other API operations are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | FHIR Swagger: say which inputs a coding replaces When a coding or codeableConcept is given, the operations don't read the separate inputs it stands for: code and system for $lookup (version still selects the code system version), code and display for CodeSystem $validate-code (url and version still name the code system), code, system, systemVersion and display for ValueSet $validate-code, and code, system and version for $translate. Each operation's coding parameter now names them, and each of those inputs says it isn't read alongside a coding. A GET coding token carries no version or display; a POSTed valueCoding does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 * OpenConceptLab/ocl_online#451 | FHIR Swagger: $lookup's system still names an instance alongside a coding At the type level, $lookup takes the code system from a coding, so a separate system has no effect; at an instance, a separate system must still name that code system, as without a coding. Its description now says so instead of saying it isn't read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * OpenConceptLab/ocl_online#451 | Tests: non-FHIR paths keep Django's behaviour, a released HEAD can be excluded - FhirErrorMiddleware re-raises an exception on a path that isn't FHIR. - The production request class reads a malformed Content-Type, or a non-text charset, off a FHIR path as Django does. - get_latest_released_version(exclude_head=True) skips a released HEAD and retired versions. - The convert_ocl_uri_to_fhir_url tests move to IdentifierSerializerTest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | A server error answered as a 422 is still logged at ERROR and sent to Errbit FHIR answers every error with an OperationOutcome and no 5xx (design.md's Protocol row). Each place that turns a 5xx into a 422 now goes through report_server_error: an exception's traceback is logged at ERROR, which Errbit gets; a 5xx a view or the stack returned without one is logged at ERROR with a notice to Errbit of its own. A response Django has already logged isn't reported twice. The log line, "FHIR answered a <status> server error as a 422", is the same everywhere, so monitoring can count it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | The FHIR API throttles every caller at one rate Every FHIR view (search, read, operations, metadata) takes its throttle from FhirViewMixin: FhirMinuteThrottle, the same rate for every caller whatever their groups, set by FHIR_THROTTLE_RATE (1000/minute by default) when throttling is on. It protects the server; groups play no part in it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | FHIR limits are settings with their own names; one too-costly error; no import-time state - The page sizes, read limits and time bound are settings: FHIR_SEARCH_MAX_COUNT (100), FHIR_SEARCH_TIMEOUT_MS (5000), FHIR_CODE_SYSTEM_CONCEPT_LIMIT, FHIR_VALUE_SET_REFERENCE_LIMIT, FHIR_CONCEPT_MAP_MAPPING_LIMIT, FHIR_EXPAND_MAX_COUNT and FHIR_TRANSLATE_MATCH_LIMIT (1000 each), read when used. - An answer that would take too long raises one TooCostlyError with its own message (core.fhir.constants); within_search_timeout takes the message, and the mappings index raises the search's. - The CapabilityStatement's date is computed when first asked, not at import, and the XML's log-once of what it leaves out is a cached function rather than a module-level set. - The URL registry toggle's name is a constant (core.url_registry.constants), and ServedHead sits with resolve.py's other definitions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | One reader per input type: typed accessors on FhirParameters FhirParameters reads every operation input: text, required_text, boolean, whole_number, coding and codeable_concept_coding, and Coding carries path and require. The per-module parsers are gone, and with them the differences between operations: - A GET's system|code token splits at its first |, as a token search value does (search's _count and token parameters read the same way). $lookup and $validate-code split at the last one before. - A codeableConcept is read with one coding everywhere. $translate took the first of several and left the others unread; now, as in $lookup and $validate-code, several are a 400 not-supported, and none is a 400 invalid. - Booleans and whole numbers take no padding (" true", "5 "), as no FHIR boolean or integer has any; $expand's stripped it, $translate's didn't. The error messages are the same everywhere. - An empty _count is the default page size, as an empty search value is no criterion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | FHIR's access checks use the models' own rules - BaseModel.is_public_access is the rule public_can_view reads, and PUBLIC_ACCESS_VALUES its stored values for querysets; FHIR uses both rather than a copy of its own. - Organization.has_edit_access and UserProfile.has_edit_access say who may edit an owner's repositories, and so view its private ones: staff, an org's members, the user. FHIR's owner_access asks the owner, as a repository's has_parent_edit_access does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | One task keeps the URL registry's links, queued once a change commits A repository row created with, or given, a canonical URL no longer resolves registry entries in the request. Its save, in Source and Collection alike, calls ConceptContainerModel.resolve_url_registry_entries_later: one query asks whether any active entry is linked to the row or names its URL in its namespace, and only then is the existing resolve_url_registry_entries task queued, on commit, so it reads the committed row. The task re-resolves those entries (url_registry_entries_to_resolve), and skips a row deleted meanwhile. A delete re-resolves only its own namespace's entries naming a deleted row's canonical URL, besides those that were linked to the rows: an entry resolves in its own namespace, so no other could have pointed at them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | One set of OCL path and FHIR path helpers; one place for a base's metadata routes - core.common.fhir_helpers holds the conversions between an OCL repository path and its FHIR path: to_ocl_path (by the path's own resource type), to_fhir_path, and repository_prefixes (a source's path in any of its forms as the OCL prefixes to match). IdentifierSerializer's converters, the resolver, $translate and ConceptMap search use them instead of their own. convert_fhir_url_to_ocl_uri now renames only the resource type's segment, so an owner or repository named CodeSystem, ValueSet or ConceptMap keeps its name. - core.fhir.urls.metadata_routes makes a base's metadata and $versions routes, for /fhir/ and both owner bases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | Test: a ConceptMap read and written back keeps its map types Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | Pylint: line lengths, a shadowed name, an import made once Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | Review fixes: FHIR's remaining-requests header counts FHIR's throttle; the queued task is the saved row's - On a FHIR path, ThrottleHeadersMiddleware reports X-LimitRemaining-Minute from FhirMinuteThrottle, the counter FHIR views throttle by, and no daily header; it used the plan throttles' counters, which FHIR views no longer use. - The registry task queued on commit takes the saved row's id and type, so an instance reused before the commit (as a new version is made from one) doesn't queue another row's. - The test that a response Django already logged isn't reported again patches the middleware's own reference. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | Simplification cuts: one log-once function, to_ocl_path reused, a dead property and a repeated test gone - log_dropped is the cached function itself. - convert_fhir_url_to_ocl_uri is to_ocl_path with a kind. - ConceptContainerModel.active_url_registry_entries had no caller left. - The Errbit-notice unit test repeated the view test; the delete-scope test uses the mock's own call record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | core/fhir/base.py split by concern; core.common no longer imports core.fhir - base.py's parts are modules of their own: constants (media types, FHIR versions, XML names), utils (is_fhir_path, fhir_base_url, namespace_of), xml (JSON and XML conversion), negotiation (media types, versioning, parsers, renderers, the middleware's responses), parameters (FhirParameters, Coding) and mixins (FhirViewMixin, FhirOperationView, and the search and read mixins from search.py). - The serializer fields and mixin shared by the CodeSystem, ValueSet and ConceptMap serializers move from search.py to core/fhir/serializers.py. - core/common/swagger.py keeps the generic generator; the FHIR-aware one is core.fhir.swagger.ApiSchemaGenerator, which core/urls.py uses, so core.common no longer imports core.fhir. Behaviour is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | One FhirResolver per request; the shared resolver takes a resolution policy instead of FHIR's flags - ConceptContainerModel.resolve_reference_expression and resolve_repo take a `resolution`, a ReferenceResolution: which repository each step finds (a canonical match, an owner's repository, a registry entry's) and the version it answers with. Its defaults are $resolveReference's rules, so the shared path holds no FHIR rule, and the repo_type, allow_head, heads and head_shown parameters are gone, as are repo_type on find_repo_by_canonical_url, Repository.get and URLRegistry.find_repo. - core.fhir.resolve.FhirResolution is FHIR's policy: one resource type, a canonical URL on a repository's active HEAD, no HEAD by default, only the HEADs the base shows, and a HEAD the caller isn't shown found before any version is read. It records the HEAD it found, where a closure collected it before. - core.fhir.resolve.FhirResolver is one request's base and caller: resolve, is_shown, is_visible, has_release, shown_q, viewable_rows, visible_versions and served_repositories are its methods rather than functions passed (namespace, user) or (kwargs, user). It asks about the caller's rights over an owner's private repositories once per owner, where the answer was stamped on each version and copied between them. FhirViewMixin builds one per request, and the operations, search, read, metadata and the ConceptMap serializer take it. Behaviour is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | A version's repository access and canonical URL are declared on the model, not stamped on it - ConceptContainerModel declares repository_public_access and repository_canonical_url as cached properties (the repository's active HEAD's, read once through repository_head), which a query may annotate and the resolver may fill in, and is_fqdn and resolution_url as class attributes. FHIR's repository_access helper and its getattr fallbacks are gone. - A CodeSystem read's listed concepts are kept in the serializer's context, not on the version (fhir_concepts). Behaviour is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | A mapping's side is a MappingSide; ConceptMap groups are built by core.fhir.ops.translate - MappingSide names a mapping's from or to fields once (source_id, source_url, concept_code, the shown and canonical annotations) and holds the queries and answers that read them: of_version, shown_as, with_shown, fields and system. FROM and TO replace the f'{side}_...' strings $translate built its queries from. - concept_map_groups (core.fhir.ops.translate) builds a ConceptMap version's groups, version-neutral, with each target's relationship; r4.concept_map_groups writes them in R4. ConceptMapGroupField only asks for them, where it read the request, the view's kwargs and four of translate's helpers itself. Behaviour is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | Test: the resolver asks about an owner's private repositories once Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | Test: a release without a canonical URL of its own shows its repository's CanonicalUrlField now reads the version's declared repository_canonical_url wherever the serializer is used, so a directly serialized release (a write's response) applies the rule reads and searches already applied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | Simplification cuts: forwarding wrappers gone, fhir_base_url built from namespace_of Translation.resolve, Search.viewable_sources and FhirViewMixin.get_fhir_base_url only forwarded to the resolver or fhir_base_url; their callers call those directly. fhir_base_url builds the base from namespace_of. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | Comment: finish the sentence above is_fqdn Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc * OpenConceptLab/ocl_online#451 | Test: releases without a canonical URL of their own add no query to a search Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBw1DvEtiXZKpHazJFbqyc --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A Swagger page for the FHIR terminology service
Follows #956 and is stacked on its branch; it gets retargeted to
masteronce #956 merges. Tracking: OpenConceptLab/ocl_online#451.Why: when FHIR has its own host (
FHIR_SUBDOMAIN), the API host answers FHIR paths with a 404. Today's Swagger page still lists them, each up to four times (with and without a trailing slash, plus malformed forms such as/fhir/CodeSystem$lookup), with write methods FHIR doesn't serve and internal docstrings. "Try it out" on them fails.What changes
/swagger/,/swagger.jsonand/swagger.yaml. It describes the FHIR terminology service alone, and "Try it out" calls that host.$lookup; CodeSystem and ValueSet$validate-code;$expand;$translate;metadata; and$versions. Each is listed at/fhirand at the owner bases/orgs/{org}and/users/{user}.Parametersbody for POST, and OperationOutcome errors.codingreplaces.FhirMiddleware: it opens only the three documentation paths on the FHIR host. The Swagger UI's static files are served ahead of it, as they are today, and the middleware order is unchanged.Testing
core/fhir).core/fhir/tests_swagger.py:python manage.py generate_swaggeralready fails onmaster, in a bulk-import view, so this PR leaves it alone. The HTTP schema endpoints work.Review
Codex reviewed it in three passes. Passes 1 and 2 each found a description that over-promised what an input does alongside a
coding, and both are fixed; pass 3 was clean. A simplification review proposed one cut, which was declined.🤖 Generated with Claude Code
https://claude.ai/code/session_01R8hw7w8753DDSCezrmrDA8