From e2935362158c6fbb6191247d851e9c6786d66945 Mon Sep 17 00:00:00 2001 From: fern-api <115122769+fern-api[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:38:41 +0000 Subject: [PATCH 1/4] [fern-generated] Update SDK Generated by Fern CLI Version: unknown Generators: - fernapi/fern-java-sdk: 4.9.2 --- .fern/metadata.json | 6 +- build.gradle | 4 +- reference.md | 13 +++-- .../com/phenoml/api/core/ClientOptions.java | 4 +- .../fhir2omop/AsyncFhir2OmopClient.java | 26 +++++---- .../fhir2omop/AsyncRawFhir2OmopClient.java | 26 +++++---- .../resources/fhir2omop/Fhir2OmopClient.java | 26 +++++---- .../fhir2omop/RawFhir2OmopClient.java | 26 +++++---- .../types/ConditionOccurrenceRow.java | 26 +++++++++ .../fhir2omop/types/DrugExposureRow.java | 26 +++++++++ .../fhir2omop/types/MappingEntry.java | 20 +++---- .../fhir2omop/types/MeasurementRow.java | 58 +++++++++++++++++++ .../fhir2omop/types/ObservationRow.java | 26 +++++++++ .../types/ProcedureOccurrenceRow.java | 26 +++++++++ .../resources/fhir2omop/types/Summary.java | 12 ++-- ...Fhir2OmopWireTest_testCreate_response.json | 4 +- 16 files changed, 257 insertions(+), 72 deletions(-) diff --git a/.fern/metadata.json b/.fern/metadata.json index 2d8e4bf..11291a9 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -1,5 +1,5 @@ { - "cliVersion": "5.49.0", + "cliVersion": "5.49.1", "generatorName": "fernapi/fern-java-sdk", "generatorVersion": "4.9.2", "generatorConfig": { @@ -10,10 +10,10 @@ "enable-wire-tests": true, "publish-to": "central" }, - "originGitCommit": "5084e3096e5bd89bf2e86ea44d96e3407f6f5c6b", + "originGitCommit": "788cec0a9a7651caf2b106634631d487dfa16704", "originGitCommitIsDirty": true, "invokedBy": "ci", "requestedVersion": "AUTO", "ciProvider": "unknown", - "sdkVersion": "17.3.0" + "sdkVersion": "0.0.0-fern-placeholder" } \ No newline at end of file diff --git a/build.gradle b/build.gradle index ac6c355..e9f35ce 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ java { group = 'com.phenoml.maven' -version = '17.3.0' +version = '0.0.0-fern-placeholder' jar { dependsOn(":generatePomFileForMavenPublication") @@ -79,7 +79,7 @@ publishing { maven(MavenPublication) { groupId = 'com.phenoml.maven' artifactId = 'phenoml-java-sdk' - version = '17.3.0' + version = '0.0.0-fern-placeholder' from components.java pom { name = 'phenoml' diff --git a/reference.md b/reference.md index 6164712..4792978 100644 --- a/reference.md +++ b/reference.md @@ -3043,12 +3043,15 @@ to the row it produced), `dropped` (resources that could not be shaped into a row), `vocab_version` (the OMOP vocabulary release codes were resolved against), and a small `summary` of the resolution outcomes. -A `concept_id` of `0` means "no matching standard concept" (OMOP -semantics) and is reported, not omitted — a coding with no match is -`UNMAPPED`. Only the primary clinical coding is resolved; +A `concept_id` of `0` is reported, not omitted (OMOP "no matching +concept" semantics): it covers both a coding with no standard match +(`UNMAPPED`) and an unverified suggestion for a text-only resource +(`UNCHECKED`). Only the primary clinical coding is resolved, so `gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are -always `0`. Each `*_source_value` carries the verbatim FHIR coding -(`system#code`), and `*_type_concept_id` is set to `32817` (EHR). +always `0`; the one populated non-resolved concept is measurement +`operator_concept_id`, set from a value comparator (`<`, `<=`, `>`, `>=`) +rather than the resolver. Each `*_source_value` carries the verbatim FHIR +coding (`system#code`), and `*_type_concept_id` is set to `32817` (EHR). Medication codes are resolved whether they appear inline (`medicationCodeableConcept`) or via a `medicationReference` to a contained, diff --git a/src/main/java/com/phenoml/api/core/ClientOptions.java b/src/main/java/com/phenoml/api/core/ClientOptions.java index 41183a7..b1f15e2 100644 --- a/src/main/java/com/phenoml/api/core/ClientOptions.java +++ b/src/main/java/com/phenoml/api/core/ClientOptions.java @@ -38,10 +38,10 @@ private ClientOptions( this.headers.putAll(headers); this.headers.putAll(new HashMap() { { - put("User-Agent", "com.phenoml.maven:phenoml-java-sdk/17.3.0"); + put("User-Agent", "com.phenoml.maven:phenoml-java-sdk/0.0.0-fern-placeholder"); put("X-Fern-Language", "JAVA"); put("X-Fern-SDK-Name", "com.phenoml.fern:api-sdk"); - put("X-Fern-SDK-Version", "17.3.0"); + put("X-Fern-SDK-Version", "0.0.0-fern-placeholder"); } }); this.headerSuppliers = headerSuppliers; diff --git a/src/main/java/com/phenoml/api/resources/fhir2omop/AsyncFhir2OmopClient.java b/src/main/java/com/phenoml/api/resources/fhir2omop/AsyncFhir2OmopClient.java index 01d7fed..907d42a 100644 --- a/src/main/java/com/phenoml/api/resources/fhir2omop/AsyncFhir2OmopClient.java +++ b/src/main/java/com/phenoml/api/resources/fhir2omop/AsyncFhir2OmopClient.java @@ -36,12 +36,15 @@ public AsyncRawFhir2OmopClient withRawResponse() { * to the row it produced), dropped (resources that could not be shaped * into a row), vocab_version (the OMOP vocabulary release codes were * resolved against), and a small summary of the resolution outcomes.

- *

A concept_id of 0 means "no matching standard concept" (OMOP - * semantics) and is reported, not omitted — a coding with no match is - * UNMAPPED. Only the primary clinical coding is resolved; + *

A concept_id of 0 is reported, not omitted (OMOP "no matching + * concept" semantics): it covers both a coding with no standard match + * (UNMAPPED) and an unverified suggestion for a text-only resource + * (UNCHECKED). Only the primary clinical coding is resolved, so * gender/race/ethnicity/visit/value/unit concept_ids are - * always 0. Each *_source_value carries the verbatim FHIR coding - * (system#code), and *_type_concept_id is set to 32817 (EHR).

+ * always 0; the one populated non-resolved concept is measurement + * operator_concept_id, set from a value comparator (<, <=, >, >=) + * rather than the resolver. Each *_source_value carries the verbatim FHIR + * coding (system#code), and *_type_concept_id is set to 32817 (EHR).

*

Medication codes are resolved whether they appear inline * (medicationCodeableConcept) or via a medicationReference to a contained, * relative (Type/id), or bundle-entry (urn:uuid) Medication resource. @@ -65,12 +68,15 @@ public CompletableFuture create(CreateOmopRequest request) { * to the row it produced), dropped (resources that could not be shaped * into a row), vocab_version (the OMOP vocabulary release codes were * resolved against), and a small summary of the resolution outcomes.

- *

A concept_id of 0 means "no matching standard concept" (OMOP - * semantics) and is reported, not omitted — a coding with no match is - * UNMAPPED. Only the primary clinical coding is resolved; + *

A concept_id of 0 is reported, not omitted (OMOP "no matching + * concept" semantics): it covers both a coding with no standard match + * (UNMAPPED) and an unverified suggestion for a text-only resource + * (UNCHECKED). Only the primary clinical coding is resolved, so * gender/race/ethnicity/visit/value/unit concept_ids are - * always 0. Each *_source_value carries the verbatim FHIR coding - * (system#code), and *_type_concept_id is set to 32817 (EHR).

+ * always 0; the one populated non-resolved concept is measurement + * operator_concept_id, set from a value comparator (<, <=, >, >=) + * rather than the resolver. Each *_source_value carries the verbatim FHIR + * coding (system#code), and *_type_concept_id is set to 32817 (EHR).

*

Medication codes are resolved whether they appear inline * (medicationCodeableConcept) or via a medicationReference to a contained, * relative (Type/id), or bundle-entry (urn:uuid) Medication resource. diff --git a/src/main/java/com/phenoml/api/resources/fhir2omop/AsyncRawFhir2OmopClient.java b/src/main/java/com/phenoml/api/resources/fhir2omop/AsyncRawFhir2OmopClient.java index 99d71da..a6f3f84 100644 --- a/src/main/java/com/phenoml/api/resources/fhir2omop/AsyncRawFhir2OmopClient.java +++ b/src/main/java/com/phenoml/api/resources/fhir2omop/AsyncRawFhir2OmopClient.java @@ -47,12 +47,15 @@ public AsyncRawFhir2OmopClient(ClientOptions clientOptions) { * to the row it produced), dropped (resources that could not be shaped * into a row), vocab_version (the OMOP vocabulary release codes were * resolved against), and a small summary of the resolution outcomes.

- *

A concept_id of 0 means "no matching standard concept" (OMOP - * semantics) and is reported, not omitted — a coding with no match is - * UNMAPPED. Only the primary clinical coding is resolved; + *

A concept_id of 0 is reported, not omitted (OMOP "no matching + * concept" semantics): it covers both a coding with no standard match + * (UNMAPPED) and an unverified suggestion for a text-only resource + * (UNCHECKED). Only the primary clinical coding is resolved, so * gender/race/ethnicity/visit/value/unit concept_ids are - * always 0. Each *_source_value carries the verbatim FHIR coding - * (system#code), and *_type_concept_id is set to 32817 (EHR).

+ * always 0; the one populated non-resolved concept is measurement + * operator_concept_id, set from a value comparator (<, <=, >, >=) + * rather than the resolver. Each *_source_value carries the verbatim FHIR + * coding (system#code), and *_type_concept_id is set to 32817 (EHR).

*

Medication codes are resolved whether they appear inline * (medicationCodeableConcept) or via a medicationReference to a contained, * relative (Type/id), or bundle-entry (urn:uuid) Medication resource. @@ -76,12 +79,15 @@ public CompletableFuture> create(C * to the row it produced), dropped (resources that could not be shaped * into a row), vocab_version (the OMOP vocabulary release codes were * resolved against), and a small summary of the resolution outcomes.

- *

A concept_id of 0 means "no matching standard concept" (OMOP - * semantics) and is reported, not omitted — a coding with no match is - * UNMAPPED. Only the primary clinical coding is resolved; + *

A concept_id of 0 is reported, not omitted (OMOP "no matching + * concept" semantics): it covers both a coding with no standard match + * (UNMAPPED) and an unverified suggestion for a text-only resource + * (UNCHECKED). Only the primary clinical coding is resolved, so * gender/race/ethnicity/visit/value/unit concept_ids are - * always 0. Each *_source_value carries the verbatim FHIR coding - * (system#code), and *_type_concept_id is set to 32817 (EHR).

+ * always 0; the one populated non-resolved concept is measurement + * operator_concept_id, set from a value comparator (<, <=, >, >=) + * rather than the resolver. Each *_source_value carries the verbatim FHIR + * coding (system#code), and *_type_concept_id is set to 32817 (EHR).

*

Medication codes are resolved whether they appear inline * (medicationCodeableConcept) or via a medicationReference to a contained, * relative (Type/id), or bundle-entry (urn:uuid) Medication resource. diff --git a/src/main/java/com/phenoml/api/resources/fhir2omop/Fhir2OmopClient.java b/src/main/java/com/phenoml/api/resources/fhir2omop/Fhir2OmopClient.java index 40decc3..8f44914 100644 --- a/src/main/java/com/phenoml/api/resources/fhir2omop/Fhir2OmopClient.java +++ b/src/main/java/com/phenoml/api/resources/fhir2omop/Fhir2OmopClient.java @@ -35,12 +35,15 @@ public RawFhir2OmopClient withRawResponse() { * to the row it produced), dropped (resources that could not be shaped * into a row), vocab_version (the OMOP vocabulary release codes were * resolved against), and a small summary of the resolution outcomes.

- *

A concept_id of 0 means "no matching standard concept" (OMOP - * semantics) and is reported, not omitted — a coding with no match is - * UNMAPPED. Only the primary clinical coding is resolved; + *

A concept_id of 0 is reported, not omitted (OMOP "no matching + * concept" semantics): it covers both a coding with no standard match + * (UNMAPPED) and an unverified suggestion for a text-only resource + * (UNCHECKED). Only the primary clinical coding is resolved, so * gender/race/ethnicity/visit/value/unit concept_ids are - * always 0. Each *_source_value carries the verbatim FHIR coding - * (system#code), and *_type_concept_id is set to 32817 (EHR).

+ * always 0; the one populated non-resolved concept is measurement + * operator_concept_id, set from a value comparator (<, <=, >, >=) + * rather than the resolver. Each *_source_value carries the verbatim FHIR + * coding (system#code), and *_type_concept_id is set to 32817 (EHR).

*

Medication codes are resolved whether they appear inline * (medicationCodeableConcept) or via a medicationReference to a contained, * relative (Type/id), or bundle-entry (urn:uuid) Medication resource. @@ -64,12 +67,15 @@ public CreateOmopResponse create(CreateOmopRequest request) { * to the row it produced), dropped (resources that could not be shaped * into a row), vocab_version (the OMOP vocabulary release codes were * resolved against), and a small summary of the resolution outcomes.

- *

A concept_id of 0 means "no matching standard concept" (OMOP - * semantics) and is reported, not omitted — a coding with no match is - * UNMAPPED. Only the primary clinical coding is resolved; + *

A concept_id of 0 is reported, not omitted (OMOP "no matching + * concept" semantics): it covers both a coding with no standard match + * (UNMAPPED) and an unverified suggestion for a text-only resource + * (UNCHECKED). Only the primary clinical coding is resolved, so * gender/race/ethnicity/visit/value/unit concept_ids are - * always 0. Each *_source_value carries the verbatim FHIR coding - * (system#code), and *_type_concept_id is set to 32817 (EHR).

+ * always 0; the one populated non-resolved concept is measurement + * operator_concept_id, set from a value comparator (<, <=, >, >=) + * rather than the resolver. Each *_source_value carries the verbatim FHIR + * coding (system#code), and *_type_concept_id is set to 32817 (EHR).

*

Medication codes are resolved whether they appear inline * (medicationCodeableConcept) or via a medicationReference to a contained, * relative (Type/id), or bundle-entry (urn:uuid) Medication resource. diff --git a/src/main/java/com/phenoml/api/resources/fhir2omop/RawFhir2OmopClient.java b/src/main/java/com/phenoml/api/resources/fhir2omop/RawFhir2OmopClient.java index a485e4b..5f16e04 100644 --- a/src/main/java/com/phenoml/api/resources/fhir2omop/RawFhir2OmopClient.java +++ b/src/main/java/com/phenoml/api/resources/fhir2omop/RawFhir2OmopClient.java @@ -43,12 +43,15 @@ public RawFhir2OmopClient(ClientOptions clientOptions) { * to the row it produced), dropped (resources that could not be shaped * into a row), vocab_version (the OMOP vocabulary release codes were * resolved against), and a small summary of the resolution outcomes.

- *

A concept_id of 0 means "no matching standard concept" (OMOP - * semantics) and is reported, not omitted — a coding with no match is - * UNMAPPED. Only the primary clinical coding is resolved; + *

A concept_id of 0 is reported, not omitted (OMOP "no matching + * concept" semantics): it covers both a coding with no standard match + * (UNMAPPED) and an unverified suggestion for a text-only resource + * (UNCHECKED). Only the primary clinical coding is resolved, so * gender/race/ethnicity/visit/value/unit concept_ids are - * always 0. Each *_source_value carries the verbatim FHIR coding - * (system#code), and *_type_concept_id is set to 32817 (EHR).

+ * always 0; the one populated non-resolved concept is measurement + * operator_concept_id, set from a value comparator (<, <=, >, >=) + * rather than the resolver. Each *_source_value carries the verbatim FHIR + * coding (system#code), and *_type_concept_id is set to 32817 (EHR).

*

Medication codes are resolved whether they appear inline * (medicationCodeableConcept) or via a medicationReference to a contained, * relative (Type/id), or bundle-entry (urn:uuid) Medication resource. @@ -72,12 +75,15 @@ public PhenomlClientHttpResponse create(CreateOmopRequest re * to the row it produced), dropped (resources that could not be shaped * into a row), vocab_version (the OMOP vocabulary release codes were * resolved against), and a small summary of the resolution outcomes.

- *

A concept_id of 0 means "no matching standard concept" (OMOP - * semantics) and is reported, not omitted — a coding with no match is - * UNMAPPED. Only the primary clinical coding is resolved; + *

A concept_id of 0 is reported, not omitted (OMOP "no matching + * concept" semantics): it covers both a coding with no standard match + * (UNMAPPED) and an unverified suggestion for a text-only resource + * (UNCHECKED). Only the primary clinical coding is resolved, so * gender/race/ethnicity/visit/value/unit concept_ids are - * always 0. Each *_source_value carries the verbatim FHIR coding - * (system#code), and *_type_concept_id is set to 32817 (EHR).

+ * always 0; the one populated non-resolved concept is measurement + * operator_concept_id, set from a value comparator (<, <=, >, >=) + * rather than the resolver. Each *_source_value carries the verbatim FHIR + * coding (system#code), and *_type_concept_id is set to 32817 (EHR).

*

Medication codes are resolved whether they appear inline * (medicationCodeableConcept) or via a medicationReference to a contained, * relative (Type/id), or bundle-entry (urn:uuid) Medication resource. diff --git a/src/main/java/com/phenoml/api/resources/fhir2omop/types/ConditionOccurrenceRow.java b/src/main/java/com/phenoml/api/resources/fhir2omop/types/ConditionOccurrenceRow.java index 03e9283..babf04b 100644 --- a/src/main/java/com/phenoml/api/resources/fhir2omop/types/ConditionOccurrenceRow.java +++ b/src/main/java/com/phenoml/api/resources/fhir2omop/types/ConditionOccurrenceRow.java @@ -34,6 +34,8 @@ public final class ConditionOccurrenceRow { private final Optional conditionTypeConceptId; + private final Optional visitOccurrenceId; + private final Optional conditionSourceValue; private final Optional conditionSourceConceptId; @@ -50,6 +52,7 @@ private ConditionOccurrenceRow( Optional conditionStartDatetime, Optional conditionEndDate, Optional conditionTypeConceptId, + Optional visitOccurrenceId, Optional conditionSourceValue, Optional conditionSourceConceptId, Optional conditionStatusSourceValue, @@ -61,6 +64,7 @@ private ConditionOccurrenceRow( this.conditionStartDatetime = conditionStartDatetime; this.conditionEndDate = conditionEndDate; this.conditionTypeConceptId = conditionTypeConceptId; + this.visitOccurrenceId = visitOccurrenceId; this.conditionSourceValue = conditionSourceValue; this.conditionSourceConceptId = conditionSourceConceptId; this.conditionStatusSourceValue = conditionStatusSourceValue; @@ -102,6 +106,11 @@ public Optional getConditionTypeConceptId() { return conditionTypeConceptId; } + @JsonProperty("visit_occurrence_id") + public Optional getVisitOccurrenceId() { + return visitOccurrenceId; + } + @JsonProperty("condition_source_value") public Optional getConditionSourceValue() { return conditionSourceValue; @@ -136,6 +145,7 @@ private boolean equalTo(ConditionOccurrenceRow other) { && conditionStartDatetime.equals(other.conditionStartDatetime) && conditionEndDate.equals(other.conditionEndDate) && conditionTypeConceptId.equals(other.conditionTypeConceptId) + && visitOccurrenceId.equals(other.visitOccurrenceId) && conditionSourceValue.equals(other.conditionSourceValue) && conditionSourceConceptId.equals(other.conditionSourceConceptId) && conditionStatusSourceValue.equals(other.conditionStatusSourceValue); @@ -151,6 +161,7 @@ public int hashCode() { this.conditionStartDatetime, this.conditionEndDate, this.conditionTypeConceptId, + this.visitOccurrenceId, this.conditionSourceValue, this.conditionSourceConceptId, this.conditionStatusSourceValue); @@ -181,6 +192,8 @@ public static final class Builder { private Optional conditionTypeConceptId = Optional.empty(); + private Optional visitOccurrenceId = Optional.empty(); + private Optional conditionSourceValue = Optional.empty(); private Optional conditionSourceConceptId = Optional.empty(); @@ -200,6 +213,7 @@ public Builder from(ConditionOccurrenceRow other) { conditionStartDatetime(other.getConditionStartDatetime()); conditionEndDate(other.getConditionEndDate()); conditionTypeConceptId(other.getConditionTypeConceptId()); + visitOccurrenceId(other.getVisitOccurrenceId()); conditionSourceValue(other.getConditionSourceValue()); conditionSourceConceptId(other.getConditionSourceConceptId()); conditionStatusSourceValue(other.getConditionStatusSourceValue()); @@ -283,6 +297,17 @@ public Builder conditionTypeConceptId(Long conditionTypeConceptId) { return this; } + @JsonSetter(value = "visit_occurrence_id", nulls = Nulls.SKIP) + public Builder visitOccurrenceId(Optional visitOccurrenceId) { + this.visitOccurrenceId = visitOccurrenceId; + return this; + } + + public Builder visitOccurrenceId(Long visitOccurrenceId) { + this.visitOccurrenceId = Optional.ofNullable(visitOccurrenceId); + return this; + } + @JsonSetter(value = "condition_source_value", nulls = Nulls.SKIP) public Builder conditionSourceValue(Optional conditionSourceValue) { this.conditionSourceValue = conditionSourceValue; @@ -325,6 +350,7 @@ public ConditionOccurrenceRow build() { conditionStartDatetime, conditionEndDate, conditionTypeConceptId, + visitOccurrenceId, conditionSourceValue, conditionSourceConceptId, conditionStatusSourceValue, diff --git a/src/main/java/com/phenoml/api/resources/fhir2omop/types/DrugExposureRow.java b/src/main/java/com/phenoml/api/resources/fhir2omop/types/DrugExposureRow.java index 4b77b60..c82620c 100644 --- a/src/main/java/com/phenoml/api/resources/fhir2omop/types/DrugExposureRow.java +++ b/src/main/java/com/phenoml/api/resources/fhir2omop/types/DrugExposureRow.java @@ -38,6 +38,8 @@ public final class DrugExposureRow { private final Optional sig; + private final Optional visitOccurrenceId; + private final Optional drugSourceValue; private final Optional drugSourceConceptId; @@ -54,6 +56,7 @@ private DrugExposureRow( Optional drugTypeConceptId, Optional stopReason, Optional sig, + Optional visitOccurrenceId, Optional drugSourceValue, Optional drugSourceConceptId, Map additionalProperties) { @@ -66,6 +69,7 @@ private DrugExposureRow( this.drugTypeConceptId = drugTypeConceptId; this.stopReason = stopReason; this.sig = sig; + this.visitOccurrenceId = visitOccurrenceId; this.drugSourceValue = drugSourceValue; this.drugSourceConceptId = drugSourceConceptId; this.additionalProperties = additionalProperties; @@ -116,6 +120,11 @@ public Optional getSig() { return sig; } + @JsonProperty("visit_occurrence_id") + public Optional getVisitOccurrenceId() { + return visitOccurrenceId; + } + @JsonProperty("drug_source_value") public Optional getDrugSourceValue() { return drugSourceValue; @@ -147,6 +156,7 @@ private boolean equalTo(DrugExposureRow other) { && drugTypeConceptId.equals(other.drugTypeConceptId) && stopReason.equals(other.stopReason) && sig.equals(other.sig) + && visitOccurrenceId.equals(other.visitOccurrenceId) && drugSourceValue.equals(other.drugSourceValue) && drugSourceConceptId.equals(other.drugSourceConceptId); } @@ -163,6 +173,7 @@ public int hashCode() { this.drugTypeConceptId, this.stopReason, this.sig, + this.visitOccurrenceId, this.drugSourceValue, this.drugSourceConceptId); } @@ -196,6 +207,8 @@ public static final class Builder { private Optional sig = Optional.empty(); + private Optional visitOccurrenceId = Optional.empty(); + private Optional drugSourceValue = Optional.empty(); private Optional drugSourceConceptId = Optional.empty(); @@ -215,6 +228,7 @@ public Builder from(DrugExposureRow other) { drugTypeConceptId(other.getDrugTypeConceptId()); stopReason(other.getStopReason()); sig(other.getSig()); + visitOccurrenceId(other.getVisitOccurrenceId()); drugSourceValue(other.getDrugSourceValue()); drugSourceConceptId(other.getDrugSourceConceptId()); return this; @@ -319,6 +333,17 @@ public Builder sig(String sig) { return this; } + @JsonSetter(value = "visit_occurrence_id", nulls = Nulls.SKIP) + public Builder visitOccurrenceId(Optional visitOccurrenceId) { + this.visitOccurrenceId = visitOccurrenceId; + return this; + } + + public Builder visitOccurrenceId(Long visitOccurrenceId) { + this.visitOccurrenceId = Optional.ofNullable(visitOccurrenceId); + return this; + } + @JsonSetter(value = "drug_source_value", nulls = Nulls.SKIP) public Builder drugSourceValue(Optional drugSourceValue) { this.drugSourceValue = drugSourceValue; @@ -352,6 +377,7 @@ public DrugExposureRow build() { drugTypeConceptId, stopReason, sig, + visitOccurrenceId, drugSourceValue, drugSourceConceptId, additionalProperties); diff --git a/src/main/java/com/phenoml/api/resources/fhir2omop/types/MappingEntry.java b/src/main/java/com/phenoml/api/resources/fhir2omop/types/MappingEntry.java index 1b6d07c..73bbb33 100644 --- a/src/main/java/com/phenoml/api/resources/fhir2omop/types/MappingEntry.java +++ b/src/main/java/com/phenoml/api/resources/fhir2omop/types/MappingEntry.java @@ -121,12 +121,10 @@ public Optional getTargetVocabulary() { } /** - * @return The standard concept's code, when present. Populated only for an - * UNCHECKED suggestion (where the API normalized a text-only resource - * to a suggested code); omitted for codings resolved through concept - * resolution (ALREADY_STANDARD / MAPPED / UNMAPPED), which are - * identified by target_vocabulary, target_name, and the row's - * *_concept_id rather than by code. + * @return The standard concept's own code: the source code itself for an + * ALREADY_STANDARD row, the standard concept's code for a MAPPED row, + * or the suggested code for an UNCHECKED row. Omitted for UNMAPPED + * rows. */ @JsonProperty("target_code") public Optional getTargetCode() { @@ -348,12 +346,10 @@ public Builder targetVocabulary(String targetVocabulary) { } /** - *

The standard concept's code, when present. Populated only for an - * UNCHECKED suggestion (where the API normalized a text-only resource - * to a suggested code); omitted for codings resolved through concept - * resolution (ALREADY_STANDARD / MAPPED / UNMAPPED), which are - * identified by target_vocabulary, target_name, and the row's - * *_concept_id rather than by code.

+ *

The standard concept's own code: the source code itself for an + * ALREADY_STANDARD row, the standard concept's code for a MAPPED row, + * or the suggested code for an UNCHECKED row. Omitted for UNMAPPED + * rows.

*/ @JsonSetter(value = "target_code", nulls = Nulls.SKIP) public Builder targetCode(Optional targetCode) { diff --git a/src/main/java/com/phenoml/api/resources/fhir2omop/types/MeasurementRow.java b/src/main/java/com/phenoml/api/resources/fhir2omop/types/MeasurementRow.java index ccf5225..ecbb2dd 100644 --- a/src/main/java/com/phenoml/api/resources/fhir2omop/types/MeasurementRow.java +++ b/src/main/java/com/phenoml/api/resources/fhir2omop/types/MeasurementRow.java @@ -34,6 +34,8 @@ public final class MeasurementRow { private final Optional valueAsNumber; + private final Optional operatorConceptId; + private final Optional valueAsConceptId; private final Optional unitConceptId; @@ -42,6 +44,8 @@ public final class MeasurementRow { private final Optional rangeHigh; + private final Optional visitOccurrenceId; + private final Optional measurementSourceValue; private final Optional measurementSourceConceptId; @@ -60,10 +64,12 @@ private MeasurementRow( Optional measurementDatetime, Optional measurementTypeConceptId, Optional valueAsNumber, + Optional operatorConceptId, Optional valueAsConceptId, Optional unitConceptId, Optional rangeLow, Optional rangeHigh, + Optional visitOccurrenceId, Optional measurementSourceValue, Optional measurementSourceConceptId, Optional unitSourceValue, @@ -76,10 +82,12 @@ private MeasurementRow( this.measurementDatetime = measurementDatetime; this.measurementTypeConceptId = measurementTypeConceptId; this.valueAsNumber = valueAsNumber; + this.operatorConceptId = operatorConceptId; this.valueAsConceptId = valueAsConceptId; this.unitConceptId = unitConceptId; this.rangeLow = rangeLow; this.rangeHigh = rangeHigh; + this.visitOccurrenceId = visitOccurrenceId; this.measurementSourceValue = measurementSourceValue; this.measurementSourceConceptId = measurementSourceConceptId; this.unitSourceValue = unitSourceValue; @@ -122,6 +130,14 @@ public Optional getValueAsNumber() { return valueAsNumber; } + /** + * @return OMOP "Meas Value Operator" standard concept qualifying value_as_number (<, <=, >, >=), parsed from a numeric-string value or a FHIR valueQuantity.comparator. 0 when no operator (a bare number). + */ + @JsonProperty("operator_concept_id") + public Optional getOperatorConceptId() { + return operatorConceptId; + } + @JsonProperty("value_as_concept_id") public Optional getValueAsConceptId() { return valueAsConceptId; @@ -142,6 +158,11 @@ public Optional getRangeHigh() { return rangeHigh; } + @JsonProperty("visit_occurrence_id") + public Optional getVisitOccurrenceId() { + return visitOccurrenceId; + } + @JsonProperty("measurement_source_value") public Optional getMeasurementSourceValue() { return measurementSourceValue; @@ -181,10 +202,12 @@ private boolean equalTo(MeasurementRow other) { && measurementDatetime.equals(other.measurementDatetime) && measurementTypeConceptId.equals(other.measurementTypeConceptId) && valueAsNumber.equals(other.valueAsNumber) + && operatorConceptId.equals(other.operatorConceptId) && valueAsConceptId.equals(other.valueAsConceptId) && unitConceptId.equals(other.unitConceptId) && rangeLow.equals(other.rangeLow) && rangeHigh.equals(other.rangeHigh) + && visitOccurrenceId.equals(other.visitOccurrenceId) && measurementSourceValue.equals(other.measurementSourceValue) && measurementSourceConceptId.equals(other.measurementSourceConceptId) && unitSourceValue.equals(other.unitSourceValue) @@ -201,10 +224,12 @@ public int hashCode() { this.measurementDatetime, this.measurementTypeConceptId, this.valueAsNumber, + this.operatorConceptId, this.valueAsConceptId, this.unitConceptId, this.rangeLow, this.rangeHigh, + this.visitOccurrenceId, this.measurementSourceValue, this.measurementSourceConceptId, this.unitSourceValue, @@ -236,6 +261,8 @@ public static final class Builder { private Optional valueAsNumber = Optional.empty(); + private Optional operatorConceptId = Optional.empty(); + private Optional valueAsConceptId = Optional.empty(); private Optional unitConceptId = Optional.empty(); @@ -244,6 +271,8 @@ public static final class Builder { private Optional rangeHigh = Optional.empty(); + private Optional visitOccurrenceId = Optional.empty(); + private Optional measurementSourceValue = Optional.empty(); private Optional measurementSourceConceptId = Optional.empty(); @@ -265,10 +294,12 @@ public Builder from(MeasurementRow other) { measurementDatetime(other.getMeasurementDatetime()); measurementTypeConceptId(other.getMeasurementTypeConceptId()); valueAsNumber(other.getValueAsNumber()); + operatorConceptId(other.getOperatorConceptId()); valueAsConceptId(other.getValueAsConceptId()); unitConceptId(other.getUnitConceptId()); rangeLow(other.getRangeLow()); rangeHigh(other.getRangeHigh()); + visitOccurrenceId(other.getVisitOccurrenceId()); measurementSourceValue(other.getMeasurementSourceValue()); measurementSourceConceptId(other.getMeasurementSourceConceptId()); unitSourceValue(other.getUnitSourceValue()); @@ -353,6 +384,20 @@ public Builder valueAsNumber(Double valueAsNumber) { return this; } + /** + *

OMOP "Meas Value Operator" standard concept qualifying value_as_number (<, <=, >, >=), parsed from a numeric-string value or a FHIR valueQuantity.comparator. 0 when no operator (a bare number).

+ */ + @JsonSetter(value = "operator_concept_id", nulls = Nulls.SKIP) + public Builder operatorConceptId(Optional operatorConceptId) { + this.operatorConceptId = operatorConceptId; + return this; + } + + public Builder operatorConceptId(Long operatorConceptId) { + this.operatorConceptId = Optional.ofNullable(operatorConceptId); + return this; + } + @JsonSetter(value = "value_as_concept_id", nulls = Nulls.SKIP) public Builder valueAsConceptId(Optional valueAsConceptId) { this.valueAsConceptId = valueAsConceptId; @@ -397,6 +442,17 @@ public Builder rangeHigh(Double rangeHigh) { return this; } + @JsonSetter(value = "visit_occurrence_id", nulls = Nulls.SKIP) + public Builder visitOccurrenceId(Optional visitOccurrenceId) { + this.visitOccurrenceId = visitOccurrenceId; + return this; + } + + public Builder visitOccurrenceId(Long visitOccurrenceId) { + this.visitOccurrenceId = Optional.ofNullable(visitOccurrenceId); + return this; + } + @JsonSetter(value = "measurement_source_value", nulls = Nulls.SKIP) public Builder measurementSourceValue(Optional measurementSourceValue) { this.measurementSourceValue = measurementSourceValue; @@ -450,10 +506,12 @@ public MeasurementRow build() { measurementDatetime, measurementTypeConceptId, valueAsNumber, + operatorConceptId, valueAsConceptId, unitConceptId, rangeLow, rangeHigh, + visitOccurrenceId, measurementSourceValue, measurementSourceConceptId, unitSourceValue, diff --git a/src/main/java/com/phenoml/api/resources/fhir2omop/types/ObservationRow.java b/src/main/java/com/phenoml/api/resources/fhir2omop/types/ObservationRow.java index d8ea202..86f2408 100644 --- a/src/main/java/com/phenoml/api/resources/fhir2omop/types/ObservationRow.java +++ b/src/main/java/com/phenoml/api/resources/fhir2omop/types/ObservationRow.java @@ -40,6 +40,8 @@ public final class ObservationRow { private final Optional unitConceptId; + private final Optional visitOccurrenceId; + private final Optional observationSourceValue; private final Optional observationSourceConceptId; @@ -61,6 +63,7 @@ private ObservationRow( Optional valueAsString, Optional valueAsConceptId, Optional unitConceptId, + Optional visitOccurrenceId, Optional observationSourceValue, Optional observationSourceConceptId, Optional unitSourceValue, @@ -76,6 +79,7 @@ private ObservationRow( this.valueAsString = valueAsString; this.valueAsConceptId = valueAsConceptId; this.unitConceptId = unitConceptId; + this.visitOccurrenceId = visitOccurrenceId; this.observationSourceValue = observationSourceValue; this.observationSourceConceptId = observationSourceConceptId; this.unitSourceValue = unitSourceValue; @@ -133,6 +137,11 @@ public Optional getUnitConceptId() { return unitConceptId; } + @JsonProperty("visit_occurrence_id") + public Optional getVisitOccurrenceId() { + return visitOccurrenceId; + } + @JsonProperty("observation_source_value") public Optional getObservationSourceValue() { return observationSourceValue; @@ -175,6 +184,7 @@ private boolean equalTo(ObservationRow other) { && valueAsString.equals(other.valueAsString) && valueAsConceptId.equals(other.valueAsConceptId) && unitConceptId.equals(other.unitConceptId) + && visitOccurrenceId.equals(other.visitOccurrenceId) && observationSourceValue.equals(other.observationSourceValue) && observationSourceConceptId.equals(other.observationSourceConceptId) && unitSourceValue.equals(other.unitSourceValue) @@ -194,6 +204,7 @@ public int hashCode() { this.valueAsString, this.valueAsConceptId, this.unitConceptId, + this.visitOccurrenceId, this.observationSourceValue, this.observationSourceConceptId, this.unitSourceValue, @@ -231,6 +242,8 @@ public static final class Builder { private Optional unitConceptId = Optional.empty(); + private Optional visitOccurrenceId = Optional.empty(); + private Optional observationSourceValue = Optional.empty(); private Optional observationSourceConceptId = Optional.empty(); @@ -255,6 +268,7 @@ public Builder from(ObservationRow other) { valueAsString(other.getValueAsString()); valueAsConceptId(other.getValueAsConceptId()); unitConceptId(other.getUnitConceptId()); + visitOccurrenceId(other.getVisitOccurrenceId()); observationSourceValue(other.getObservationSourceValue()); observationSourceConceptId(other.getObservationSourceConceptId()); unitSourceValue(other.getUnitSourceValue()); @@ -372,6 +386,17 @@ public Builder unitConceptId(Long unitConceptId) { return this; } + @JsonSetter(value = "visit_occurrence_id", nulls = Nulls.SKIP) + public Builder visitOccurrenceId(Optional visitOccurrenceId) { + this.visitOccurrenceId = visitOccurrenceId; + return this; + } + + public Builder visitOccurrenceId(Long visitOccurrenceId) { + this.visitOccurrenceId = Optional.ofNullable(visitOccurrenceId); + return this; + } + @JsonSetter(value = "observation_source_value", nulls = Nulls.SKIP) public Builder observationSourceValue(Optional observationSourceValue) { this.observationSourceValue = observationSourceValue; @@ -428,6 +453,7 @@ public ObservationRow build() { valueAsString, valueAsConceptId, unitConceptId, + visitOccurrenceId, observationSourceValue, observationSourceConceptId, unitSourceValue, diff --git a/src/main/java/com/phenoml/api/resources/fhir2omop/types/ProcedureOccurrenceRow.java b/src/main/java/com/phenoml/api/resources/fhir2omop/types/ProcedureOccurrenceRow.java index 0631b91..48b7924 100644 --- a/src/main/java/com/phenoml/api/resources/fhir2omop/types/ProcedureOccurrenceRow.java +++ b/src/main/java/com/phenoml/api/resources/fhir2omop/types/ProcedureOccurrenceRow.java @@ -32,6 +32,8 @@ public final class ProcedureOccurrenceRow { private final Optional procedureTypeConceptId; + private final Optional visitOccurrenceId; + private final Optional procedureSourceValue; private final Optional procedureSourceConceptId; @@ -45,6 +47,7 @@ private ProcedureOccurrenceRow( Optional procedureDate, Optional procedureDatetime, Optional procedureTypeConceptId, + Optional visitOccurrenceId, Optional procedureSourceValue, Optional procedureSourceConceptId, Map additionalProperties) { @@ -54,6 +57,7 @@ private ProcedureOccurrenceRow( this.procedureDate = procedureDate; this.procedureDatetime = procedureDatetime; this.procedureTypeConceptId = procedureTypeConceptId; + this.visitOccurrenceId = visitOccurrenceId; this.procedureSourceValue = procedureSourceValue; this.procedureSourceConceptId = procedureSourceConceptId; this.additionalProperties = additionalProperties; @@ -89,6 +93,11 @@ public Optional getProcedureTypeConceptId() { return procedureTypeConceptId; } + @JsonProperty("visit_occurrence_id") + public Optional getVisitOccurrenceId() { + return visitOccurrenceId; + } + @JsonProperty("procedure_source_value") public Optional getProcedureSourceValue() { return procedureSourceValue; @@ -117,6 +126,7 @@ private boolean equalTo(ProcedureOccurrenceRow other) { && procedureDate.equals(other.procedureDate) && procedureDatetime.equals(other.procedureDatetime) && procedureTypeConceptId.equals(other.procedureTypeConceptId) + && visitOccurrenceId.equals(other.visitOccurrenceId) && procedureSourceValue.equals(other.procedureSourceValue) && procedureSourceConceptId.equals(other.procedureSourceConceptId); } @@ -130,6 +140,7 @@ public int hashCode() { this.procedureDate, this.procedureDatetime, this.procedureTypeConceptId, + this.visitOccurrenceId, this.procedureSourceValue, this.procedureSourceConceptId); } @@ -157,6 +168,8 @@ public static final class Builder { private Optional procedureTypeConceptId = Optional.empty(); + private Optional visitOccurrenceId = Optional.empty(); + private Optional procedureSourceValue = Optional.empty(); private Optional procedureSourceConceptId = Optional.empty(); @@ -173,6 +186,7 @@ public Builder from(ProcedureOccurrenceRow other) { procedureDate(other.getProcedureDate()); procedureDatetime(other.getProcedureDatetime()); procedureTypeConceptId(other.getProcedureTypeConceptId()); + visitOccurrenceId(other.getVisitOccurrenceId()); procedureSourceValue(other.getProcedureSourceValue()); procedureSourceConceptId(other.getProcedureSourceConceptId()); return this; @@ -244,6 +258,17 @@ public Builder procedureTypeConceptId(Long procedureTypeConceptId) { return this; } + @JsonSetter(value = "visit_occurrence_id", nulls = Nulls.SKIP) + public Builder visitOccurrenceId(Optional visitOccurrenceId) { + this.visitOccurrenceId = visitOccurrenceId; + return this; + } + + public Builder visitOccurrenceId(Long visitOccurrenceId) { + this.visitOccurrenceId = Optional.ofNullable(visitOccurrenceId); + return this; + } + @JsonSetter(value = "procedure_source_value", nulls = Nulls.SKIP) public Builder procedureSourceValue(Optional procedureSourceValue) { this.procedureSourceValue = procedureSourceValue; @@ -274,6 +299,7 @@ public ProcedureOccurrenceRow build() { procedureDate, procedureDatetime, procedureTypeConceptId, + visitOccurrenceId, procedureSourceValue, procedureSourceConceptId, additionalProperties); diff --git a/src/main/java/com/phenoml/api/resources/fhir2omop/types/Summary.java b/src/main/java/com/phenoml/api/resources/fhir2omop/types/Summary.java index c67e947..5f018ef 100644 --- a/src/main/java/com/phenoml/api/resources/fhir2omop/types/Summary.java +++ b/src/main/java/com/phenoml/api/resources/fhir2omop/types/Summary.java @@ -44,7 +44,7 @@ private Summary( } /** - * @return Codings already a standard OMOP concept. + * @return Coded concepts already a standard OMOP concept (ALREADY_STANDARD). */ @JsonProperty("codes_already_standard") public Optional getCodesAlreadyStandard() { @@ -52,7 +52,7 @@ public Optional getCodesAlreadyStandard() { } /** - * @return Codings mapped or suggested to a standard concept (MAPPED or UNCHECKED). + * @return Coded concepts mapped or suggested to a standard concept (MAPPED or UNCHECKED). */ @JsonProperty("codes_normalized") public Optional getCodesNormalized() { @@ -60,7 +60,7 @@ public Optional getCodesNormalized() { } /** - * @return Codings with no standard concept found. + * @return Coded concepts with no standard concept found (UNMAPPED). */ @JsonProperty("codes_unmapped") public Optional getCodesUnmapped() { @@ -131,7 +131,7 @@ public Builder from(Summary other) { } /** - *

Codings already a standard OMOP concept.

+ *

Coded concepts already a standard OMOP concept (ALREADY_STANDARD).

*/ @JsonSetter(value = "codes_already_standard", nulls = Nulls.SKIP) public Builder codesAlreadyStandard(Optional codesAlreadyStandard) { @@ -145,7 +145,7 @@ public Builder codesAlreadyStandard(Integer codesAlreadyStandard) { } /** - *

Codings mapped or suggested to a standard concept (MAPPED or UNCHECKED).

+ *

Coded concepts mapped or suggested to a standard concept (MAPPED or UNCHECKED).

*/ @JsonSetter(value = "codes_normalized", nulls = Nulls.SKIP) public Builder codesNormalized(Optional codesNormalized) { @@ -159,7 +159,7 @@ public Builder codesNormalized(Integer codesNormalized) { } /** - *

Codings with no standard concept found.

+ *

Coded concepts with no standard concept found (UNMAPPED).

*/ @JsonSetter(value = "codes_unmapped", nulls = Nulls.SKIP) public Builder codesUnmapped(Optional codesUnmapped) { diff --git a/src/test/resources/wire-tests/Fhir2OmopWireTest_testCreate_response.json b/src/test/resources/wire-tests/Fhir2OmopWireTest_testCreate_response.json index e2e5afd..5498547 100644 --- a/src/test/resources/wire-tests/Fhir2OmopWireTest_testCreate_response.json +++ b/src/test/resources/wire-tests/Fhir2OmopWireTest_testCreate_response.json @@ -63,7 +63,7 @@ "source_code": "44054006", "source_name": "Type 2 diabetes mellitus", "target_vocabulary": "SNOMED", - "target_code": "target_code", + "target_code": "44054006", "target_name": "Type 2 diabetes mellitus", "mapping_status": "ALREADY_STANDARD", "note": "note" @@ -77,7 +77,7 @@ "source_code": "860975", "source_name": "metformin hydrochloride 500 MG", "target_vocabulary": "RXNORM", - "target_code": "target_code", + "target_code": "860975", "target_name": "metformin hydrochloride 500 MG", "mapping_status": "ALREADY_STANDARD", "note": "note" From 514f9e8426d8e651bdb33bbc8dc2841ae6460952 Mon Sep 17 00:00:00 2001 From: fern-api <115122769+fern-api[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:39:27 +0000 Subject: [PATCH 2/4] [fern-autoversion] feat: add fhir2omop service with FHIR-to-OMOP CDM v5.4 mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose a new `fhir2omop` sub-client on both `PhenomlClient` and `AsyncPhenomlClient` that posts FHIR R4 resources or Bundles to `POST /fhir2omop/create` and returns typed OMOP CDM v5.4 rows. The response includes OMOP rows grouped by table (`tables`), per-coding mapping entries (`mappings`), resources that could not be shaped into a row (`dropped`), the OMOP vocabulary release used (`vocab_version`), and aggregate resolution statistics (`summary`). Key changes: - Add `Fhir2OmopClient` / `AsyncFhir2OmopClient` and expose them via `PhenomlClient.fhir2Omop()` / `AsyncPhenomlClient.fhir2Omop()` - Add `CreateOmopRequest` (requires `fhirResources`) and `CreateOmopResponse` (carries `tables`, `mappings`, `dropped`, `vocab_version`, `summary`) - Add OMOP CDM v5.4 row types: `PersonRow`, `VisitOccurrenceRow`, `ConditionOccurrenceRow`, `DrugExposureRow`, `ProcedureOccurrenceRow`, `MeasurementRow`, `ObservationRow` under `OmopTables` - Add `MappingEntry`, `Summary`, and `DroppedResource` supporting types - Register `fhir2omop` service in OpenAPI spec and update code-examples metadata 🌿 Generated with Fern --- .fern/metadata.json | 2 +- build.gradle | 4 ++-- changelog.md | 8 ++++++++ src/main/java/com/phenoml/api/core/ClientOptions.java | 4 ++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.fern/metadata.json b/.fern/metadata.json index 11291a9..872c578 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -15,5 +15,5 @@ "invokedBy": "ci", "requestedVersion": "AUTO", "ciProvider": "unknown", - "sdkVersion": "0.0.0-fern-placeholder" + "sdkVersion": "17.4.0" } \ No newline at end of file diff --git a/build.gradle b/build.gradle index e9f35ce..9f65502 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ java { group = 'com.phenoml.maven' -version = '0.0.0-fern-placeholder' +version = '17.4.0' jar { dependsOn(":generatePomFileForMavenPublication") @@ -79,7 +79,7 @@ publishing { maven(MavenPublication) { groupId = 'com.phenoml.maven' artifactId = 'phenoml-java-sdk' - version = '0.0.0-fern-placeholder' + version = '17.4.0' from components.java pom { name = 'phenoml' diff --git a/changelog.md b/changelog.md index f52f854..7ac1ed0 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,11 @@ +## [17.4.0] - 2026-06-15 +### Added +- **`Fhir2OmopClient` / `AsyncFhir2OmopClient`** — new sync and async clients exposing `create(CreateOmopRequest)` and `create(CreateOmopRequest, RequestOptions)` to post a FHIR R4 resource or Bundle to `POST /fhir2omop/create` and return typed OMOP CDM v5.4 rows. +- **`RawFhir2OmopClient` / `AsyncRawFhir2OmopClient`** — raw-response variants of the fhir2omop clients returning full HTTP metadata; accessible via `withRawResponse()` on the standard clients. +- **`CreateOmopRequest` / `CreateOmopResponse`** — new request/response types; request carries `fhirResources`; response exposes `success`, `message`, `tables`, `mappings`, `dropped`, `vocabVersion`, and `summary`. +- **`OmopTables`** and seven OMOP CDM v5.4 row types — new `PersonRow`, `VisitOccurrenceRow`, `ConditionOccurrenceRow`, `DrugExposureRow`, `ProcedureOccurrenceRow`, `MeasurementRow`, and `ObservationRow` classes aggregated under `OmopTables`. +- **`MappingEntry`, `Summary`, and `DroppedResource`** — new supporting types providing per-coding mapping status, aggregate resolution statistics, and details on resources that could not be shaped into OMOP rows; `BadRequestError`, `UnauthorizedError`, `InternalServerError`, and `ServiceUnavailableError` typed exceptions also added for fhir2omop error handling. + ## [17.3.0] - 2026-06-15 ### Added - **`PhenomlClient.fhir2Omop().create()`** — new method (sync and async) posting a FHIR R4 resource or Bundle to `POST /fhir2omop/create` and returning typed OMOP CDM v5.4 rows in both `resolved` and `structural` modes. diff --git a/src/main/java/com/phenoml/api/core/ClientOptions.java b/src/main/java/com/phenoml/api/core/ClientOptions.java index b1f15e2..89f6871 100644 --- a/src/main/java/com/phenoml/api/core/ClientOptions.java +++ b/src/main/java/com/phenoml/api/core/ClientOptions.java @@ -38,10 +38,10 @@ private ClientOptions( this.headers.putAll(headers); this.headers.putAll(new HashMap() { { - put("User-Agent", "com.phenoml.maven:phenoml-java-sdk/0.0.0-fern-placeholder"); + put("User-Agent", "com.phenoml.maven:phenoml-java-sdk/17.4.0"); put("X-Fern-Language", "JAVA"); put("X-Fern-SDK-Name", "com.phenoml.fern:api-sdk"); - put("X-Fern-SDK-Version", "0.0.0-fern-placeholder"); + put("X-Fern-SDK-Version", "17.4.0"); } }); this.headerSuppliers = headerSuppliers; From 6ccf1416ee74fd2ec07a95e8548f4f58b1707a4a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:39:49 +0000 Subject: [PATCH 3/4] chore: sync OpenAPI spec + code-examples for 788cec0a9a7651caf2b106634631d487dfa16704 --- code-examples.json | 6 ++-- src/main/resources/openapi/openapi.json | 43 ++++++++++++++++++++----- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/code-examples.json b/code-examples.json index f59a195..48af205 100644 --- a/code-examples.json +++ b/code-examples.json @@ -2,8 +2,8 @@ "metadata": { "language": "java", "packageName": "com.phenoml.maven:phenoml-java-sdk", - "sdkVersion": "17.3.0", - "specCommit": "5084e3096e5bd89bf2e86ea44d96e3407f6f5c6b", + "sdkVersion": "17.4.0", + "specCommit": "788cec0a9a7651caf2b106634631d487dfa16704", "generatorName": "fernapi/fern-java-sdk" }, "renderRules": { @@ -2298,6 +2298,7 @@ "source_code": "44054006", "source_name": "Type 2 diabetes mellitus", "target_vocabulary": "SNOMED", + "target_code": "44054006", "target_name": "Type 2 diabetes mellitus", "mapping_status": "ALREADY_STANDARD" }, @@ -2310,6 +2311,7 @@ "source_code": "860975", "source_name": "metformin hydrochloride 500 MG", "target_vocabulary": "RXNORM", + "target_code": "860975", "target_name": "metformin hydrochloride 500 MG", "mapping_status": "ALREADY_STANDARD" } diff --git a/src/main/resources/openapi/openapi.json b/src/main/resources/openapi/openapi.json index 3cb0654..3924b6b 100644 --- a/src/main/resources/openapi/openapi.json +++ b/src/main/resources/openapi/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.3", "info": { "title": "Phenoml API", - "version": "5084e3096e5bd89bf2e86ea44d96e3407f6f5c6b" + "version": "788cec0a9a7651caf2b106634631d487dfa16704" }, "x-services": [ { @@ -3021,7 +3021,7 @@ "post": { "operationId": "fhir2omop_create", "summary": "Map FHIR resources to OMOP CDM v5.4", - "description": "Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows\n(person, visit_occurrence, condition_occurrence, drug_exposure,\nprocedure_occurrence, measurement, observation).\n\nEach resource's primary clinical coding is resolved to a standard OMOP\n`concept_id`. Alongside the OMOP rows grouped by table (`tables`), the\nresponse carries `mappings` (how each source coding resolved, linked back\nto the row it produced), `dropped` (resources that could not be shaped\ninto a row), `vocab_version` (the OMOP vocabulary release codes were\nresolved against), and a small `summary` of the resolution outcomes.\n\nA `concept_id` of `0` means \"no matching standard concept\" (OMOP\nsemantics) and is reported, not omitted \u2014 a coding with no match is\n`UNMAPPED`. Only the primary clinical coding is resolved;\n`gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are\nalways `0`. Each `*_source_value` carries the verbatim FHIR coding\n(`system#code`), and `*_type_concept_id` is set to `32817` (EHR).\n\nMedication codes are resolved whether they appear inline\n(`medicationCodeableConcept`) or via a `medicationReference` to a contained,\nrelative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource.\nResources that cannot be shaped into a row \u2014 a medication with no usable\ncode, resolvable reference, or display, or any clinical resource whose\nsubject/patient reference cannot be tied to a person \u2014 are reported under\n`dropped` rather than emitted as blank rows. The\nbundle must contain at least one Patient resource.\n", + "description": "Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows\n(person, visit_occurrence, condition_occurrence, drug_exposure,\nprocedure_occurrence, measurement, observation).\n\nEach resource's primary clinical coding is resolved to a standard OMOP\n`concept_id`. Alongside the OMOP rows grouped by table (`tables`), the\nresponse carries `mappings` (how each source coding resolved, linked back\nto the row it produced), `dropped` (resources that could not be shaped\ninto a row), `vocab_version` (the OMOP vocabulary release codes were\nresolved against), and a small `summary` of the resolution outcomes.\n\nA `concept_id` of `0` is reported, not omitted (OMOP \"no matching\nconcept\" semantics): it covers both a coding with no standard match\n(`UNMAPPED`) and an unverified suggestion for a text-only resource\n(`UNCHECKED`). Only the primary clinical coding is resolved, so\n`gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are\nalways `0`; the one populated non-resolved concept is measurement\n`operator_concept_id`, set from a value comparator (`<`, `<=`, `>`, `>=`)\nrather than the resolver. Each `*_source_value` carries the verbatim FHIR\ncoding (`system#code`), and `*_type_concept_id` is set to `32817` (EHR).\n\nMedication codes are resolved whether they appear inline\n(`medicationCodeableConcept`) or via a `medicationReference` to a contained,\nrelative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource.\nResources that cannot be shaped into a row \u2014 a medication with no usable\ncode, resolvable reference, or display, or any clinical resource whose\nsubject/patient reference cannot be tied to a person \u2014 are reported under\n`dropped` rather than emitted as blank rows. The\nbundle must contain at least one Patient resource.\n", "requestBody": { "required": true, "content": { @@ -3113,7 +3113,7 @@ "examples": { "mapping_result": { "summary": "Mapping result", - "description": "The example bundle mapped to OMOP. Both source codes are already\nstandard, so each clinical row carries its own OMOP `concept_id`\nwith `ALREADY_STANDARD` status. `target_code` is omitted for a\nresolved concept \u2014 it is identified by `target_vocabulary` and\n`target_name` (and the row's `*_concept_id`). Illustrative\n`concept_id` values.\n", + "description": "The example bundle mapped to OMOP. Both source codes are already\nstandard, so each clinical row carries its own OMOP `concept_id`\nwith `ALREADY_STANDARD` status and a `target_code` equal to the\nsource code. Illustrative `concept_id` values.\n", "value": { "success": true, "message": "FHIR resources mapped to OMOP CDM v5.4", @@ -3167,6 +3167,7 @@ "source_code": "44054006", "source_name": "Type 2 diabetes mellitus", "target_vocabulary": "SNOMED", + "target_code": "44054006", "target_name": "Type 2 diabetes mellitus", "mapping_status": "ALREADY_STANDARD" }, @@ -3179,6 +3180,7 @@ "source_code": "860975", "source_name": "metformin hydrochloride 500 MG", "target_vocabulary": "RXNORM", + "target_code": "860975", "target_name": "metformin hydrochloride 500 MG", "mapping_status": "ALREADY_STANDARD" } @@ -8738,6 +8740,10 @@ "type": "integer", "format": "int64" }, + "visit_occurrence_id": { + "type": "integer", + "format": "int64" + }, "condition_source_value": { "type": "string" }, @@ -8784,6 +8790,10 @@ "sig": { "type": "string" }, + "visit_occurrence_id": { + "type": "integer", + "format": "int64" + }, "drug_source_value": { "type": "string" }, @@ -8818,6 +8828,10 @@ "type": "integer", "format": "int64" }, + "visit_occurrence_id": { + "type": "integer", + "format": "int64" + }, "procedure_source_value": { "type": "string" }, @@ -8856,6 +8870,11 @@ "type": "number", "format": "double" }, + "operator_concept_id": { + "type": "integer", + "format": "int64", + "description": "OMOP \"Meas Value Operator\" standard concept qualifying value_as_number (<, <=, >, >=), parsed from a numeric-string value or a FHIR valueQuantity.comparator. 0 when no operator (a bare number)." + }, "value_as_concept_id": { "type": "integer", "format": "int64" @@ -8872,6 +8891,10 @@ "type": "number", "format": "double" }, + "visit_occurrence_id": { + "type": "integer", + "format": "int64" + }, "measurement_source_value": { "type": "string" }, @@ -8927,6 +8950,10 @@ "type": "integer", "format": "int64" }, + "visit_occurrence_id": { + "type": "integer", + "format": "int64" + }, "observation_source_value": { "type": "string" }, @@ -8974,7 +9001,7 @@ }, "target_code": { "type": "string", - "description": "The standard concept's code, when present. Populated only for an\n`UNCHECKED` suggestion (where the API normalized a text-only resource\nto a suggested code); omitted for codings resolved through concept\nresolution (`ALREADY_STANDARD` / `MAPPED` / `UNMAPPED`), which are\nidentified by `target_vocabulary`, `target_name`, and the row's\n`*_concept_id` rather than by code.\n" + "description": "The standard concept's own code: the source code itself for an\nALREADY_STANDARD row, the standard concept's code for a MAPPED row,\nor the suggested code for an UNCHECKED row. Omitted for UNMAPPED\nrows.\n" }, "target_name": { "type": "string" @@ -8990,19 +9017,19 @@ }, "fhir2omop_Summary": { "type": "object", - "description": "The request's data-quality headline: how the coded concepts split across\nresolution outcomes, and the share that was not already in a target\nstandard vocabulary.\n", + "description": "The request's data-quality headline: how the coded concepts split across\nresolution outcomes, and the share that was not already in a target\nstandard vocabulary. Each coded resource is counted once (per resolved\nconcept), even when it carried several codings \u2014 unlike `mappings`, which\nhas one entry per coding.\n", "properties": { "codes_already_standard": { "type": "integer", - "description": "Codings already a standard OMOP concept." + "description": "Coded concepts already a standard OMOP concept (ALREADY_STANDARD)." }, "codes_normalized": { "type": "integer", - "description": "Codings mapped or suggested to a standard concept (MAPPED or UNCHECKED)." + "description": "Coded concepts mapped or suggested to a standard concept (MAPPED or UNCHECKED)." }, "codes_unmapped": { "type": "integer", - "description": "Codings with no standard concept found." + "description": "Coded concepts with no standard concept found (UNMAPPED)." }, "off_vocab_rate": { "type": "number", From 689cf3a17c70ea95cc2c50620a6691adf75279ff Mon Sep 17 00:00:00 2001 From: Gavin Sharp Date: Mon, 15 Jun 2026 12:57:34 -0400 Subject: [PATCH 4/4] docs(changelog): scope 17.4.0 entry to the real delta The auto-generated 17.4.0 entry re-described the entire fhir2omop client/types/errors as newly added (it shipped in 17.2.0). This release only adds getVisitOccurrenceId() (5 row types) + MeasurementRow.getOperatorConceptId() and clarifies target_code / Summary / create() docs. Co-Authored-By: Claude Opus 4.8 --- changelog.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 7ac1ed0..dd6a01c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,12 @@ ## [17.4.0] - 2026-06-15 ### Added -- **`Fhir2OmopClient` / `AsyncFhir2OmopClient`** — new sync and async clients exposing `create(CreateOmopRequest)` and `create(CreateOmopRequest, RequestOptions)` to post a FHIR R4 resource or Bundle to `POST /fhir2omop/create` and return typed OMOP CDM v5.4 rows. -- **`RawFhir2OmopClient` / `AsyncRawFhir2OmopClient`** — raw-response variants of the fhir2omop clients returning full HTTP metadata; accessible via `withRawResponse()` on the standard clients. -- **`CreateOmopRequest` / `CreateOmopResponse`** — new request/response types; request carries `fhirResources`; response exposes `success`, `message`, `tables`, `mappings`, `dropped`, `vocabVersion`, and `summary`. -- **`OmopTables`** and seven OMOP CDM v5.4 row types — new `PersonRow`, `VisitOccurrenceRow`, `ConditionOccurrenceRow`, `DrugExposureRow`, `ProcedureOccurrenceRow`, `MeasurementRow`, and `ObservationRow` classes aggregated under `OmopTables`. -- **`MappingEntry`, `Summary`, and `DroppedResource`** — new supporting types providing per-coding mapping status, aggregate resolution statistics, and details on resources that could not be shaped into OMOP rows; `BadRequestError`, `UnauthorizedError`, `InternalServerError`, and `ServiceUnavailableError` typed exceptions also added for fhir2omop error handling. +- **`ConditionOccurrenceRow.getVisitOccurrenceId()`**, **`DrugExposureRow.getVisitOccurrenceId()`**, **`MeasurementRow.getVisitOccurrenceId()`**, **`ObservationRow.getVisitOccurrenceId()`**, and **`ProcedureOccurrenceRow.getVisitOccurrenceId()`** — new optional accessor linking each clinical OMOP row back to its `visit_occurrence` row. +- **`MeasurementRow.getOperatorConceptId()`** — new optional accessor carrying the OMOP "Meas Value Operator" standard concept (`<`, `<=`, `>`, `>=`) parsed from a FHIR `valueQuantity.comparator` or numeric-string value; `0` when no operator is present. + +### Changed +- **`MappingEntry.getTargetCode()`** — value semantics updated: now the standard concept's own code for `ALREADY_STANDARD`, `MAPPED`, and `UNCHECKED` rows; empty for `UNMAPPED` rows. +- **`Summary`** — count-field docs clarify each total is counted once per resolved concept (not per coding), unlike `mappings`, which has one entry per coding. +- **`PhenomlClient.fhir2Omop().create(...)` Javadoc** — clarifies that a clinical `concept_id` of `0` covers both `UNMAPPED` and `UNCHECKED`, and that `operator_concept_id` is the one non-zero non-resolved concept on measurement rows. ## [17.3.0] - 2026-06-15 ### Added