From 653745a5f6622f68843fe10a5ff3dcdd83740dee Mon Sep 17 00:00:00 2001 From: Vitaly Kravtsov Date: Mon, 21 Oct 2024 17:11:36 +0600 Subject: [PATCH] fix(typescript): valuesets imports --- src/aidbox_sdk/converter.clj | 40 ++-- src/aidbox_sdk/core.clj | 1 + src/aidbox_sdk/generator/typescript.clj | 25 ++- test/aidbox_sdk/converter_test.clj | 26 ++- .../aidbox_sdk/fixtures/patient_ir_schema.edn | 2 + test/aidbox_sdk/generator/typescript_test.clj | 197 +++++++++++------- 6 files changed, 195 insertions(+), 96 deletions(-) diff --git a/src/aidbox_sdk/converter.clj b/src/aidbox_sdk/converter.clj index a32a375..db41a8a 100644 --- a/src/aidbox_sdk/converter.clj +++ b/src/aidbox_sdk/converter.clj @@ -280,27 +280,37 @@ (defn resolve-choices [schemas] (map resolve-schema-choices schemas)) -(defn collect-dependencies [schema] - (let [primitive-element? (partial fhir/primitive-element? (:fhir-version schema))] +(defn collect-dependencies + "Returns the set of dependencies for the provided schema." + [schema] + (let [primitive-element? (partial fhir/primitive-element? (:fhir-version schema)) + backbones-elements (->> (:backbone-elements schema) + (map :elements) + flatten) + all-elements (into (:elements schema) backbones-elements) + types (->> all-elements + (remove primitive-element?) + (map :type))] (set/union (cond-> #{} (:base-resource-name schema) (conj (:base-resource-name schema)) (fhir/constraint? schema) (conj "Meta")) - (->> (:elements schema) - (remove primitive-element?) - (map :type) - (remove nil?) - set) - (->> (:backbone-elements schema) - (map :elements) - flatten - (remove primitive-element?) - (map :type) - (remove nil?) - set)))) + (set (remove nil? types))))) + +(defn collect-valueset-dependencies + [schema] + (let [backbones-elements (->> (:backbone-elements schema) + (map :elements) + flatten) + all-elements (into (:elements schema) backbones-elements) + valusets (map :valueset all-elements)] + (set (remove nil? valusets)))) (defn resolve-dependencies [schemas] - (map #(assoc % :deps (collect-dependencies %)) schemas)) + (map #(-> % + (assoc :deps (collect-dependencies %)) + (assoc :valueset-deps (collect-valueset-dependencies %))) + schemas)) (defn resolve-valuesets [schema available-valuesets] (let [available-valuesets (get available-valuesets (:fhir-version schema))] diff --git a/src/aidbox_sdk/core.clj b/src/aidbox_sdk/core.clj index 0645ea2..cc28eec 100644 --- a/src/aidbox_sdk/core.clj +++ b/src/aidbox_sdk/core.clj @@ -45,6 +45,7 @@ (doseq [{:keys [path content]} files] (save-to-file! (io/file output-dir path) content))) + ;; ;; diff --git a/src/aidbox_sdk/generator/typescript.clj b/src/aidbox_sdk/generator/typescript.clj index b722e19..9df6380 100644 --- a/src/aidbox_sdk/generator/typescript.clj +++ b/src/aidbox_sdk/generator/typescript.clj @@ -103,14 +103,14 @@ (->backbone-type element) (:valueset element) - (str "vs." (class-name (:valueset element))) + (class-name (:valueset element)) :else (->lang-type (:type element))) primitive-type? (fhir/primitive-element? fhir-version element)] (str (str (->camel-case name) optional ": " type' (when array "[]") ";") (when primitive-type? - (str "\n" u/indent "_" (->camel-case name) ": Element;"))))))) + (str "\n" u/indent "_" (->camel-case name) "?: Element;"))))))) (defn generate-class "Generates TypeScript type from IR (intermediate representation) schema." @@ -131,6 +131,7 @@ (remove nil?) (map u/add-indent) (str/join "\n"))] + (str (when (seq inner-classes) (str (str/join "\n\n" inner-classes) "\n\n")) @@ -171,18 +172,28 @@ (let [relative-path (if (fhir/base-package? ir-schema) "./" (str "../" (package->directory (:fhir-version ir-schema)) "/")) - valueset-dep (when (fhir/base-package? ir-schema) - "import * as vs from \"./valuesets\"")] + valueset-deps (when (and (fhir/base-package? ir-schema) + (seq (:valueset-deps ir-schema))) + (format "import { %s } from \"./valuesets\"" + (->> (:valueset-deps ir-schema) + (map class-name) + (str/join ", "))))] (str (->> (:deps ir-schema) (map class-name) (map (fn [d] {:module (str relative-path d) :members [d]})) (map (fn [{:keys [module members]}] - (if (seq members) (format "import { %s } from \"%s\";" (str/join ", " members) module) (format "import * as %s from \"%s\";" (path->name module) module)))) + (if (seq members) + (format "import { %s } from \"%s\";" + (str/join ", " members) + module) + (format "import * as %s from \"%s\";" + (path->name module) + module)))) (str/join "\n")) - (when valueset-dep + (when valueset-deps (str "\n" - valueset-dep))))) + valueset-deps))))) (defn generate-module [& {:keys [deps classes] diff --git a/test/aidbox_sdk/converter_test.clj b/test/aidbox_sdk/converter_test.clj index ee226ba..91d608b 100644 --- a/test/aidbox_sdk/converter_test.clj +++ b/test/aidbox_sdk/converter_test.clj @@ -98,12 +98,16 @@ (deftest test-convert (testing "convert resource" - (match (sut/convert [fixtures/patient-fhir-schema] #{}) + (match (sut/convert [fixtures/patient-fhir-schema] + {"hl7.fhir.r4.core" + #{"http://hl7.org/fhir/ValueSet/administrative-gender"}}) + [fixtures/patient-ir-schema])) (testing "convert constraint" - (is (= [(fixt/get-data :organization-preferred-contact-ir-schema)] - (sut/convert [(fixt/get-data :organization-preferred-contact-fhir-schema)] #{}))))) + (match + (sut/convert [(fixt/get-data :organization-preferred-contact-fhir-schema)] #{}) + [(fixt/get-data :organization-preferred-contact-ir-schema)]))) (deftest test-apply-constraints (testing "constraints" @@ -133,9 +137,25 @@ {:url "http://hl7.org/fhir/StructureDefinition/SampledData", :base "http://hl7.org/fhir/StructureDefinition/Element"}])) +(deftest test-collect-dependencies + (match + (sut/collect-dependencies (fixt/get-data :patient-ir-schema)) + #{"Address" + "Attachment" + "Period" + "CodeableConcept" + "ContactPoint" + "HumanName" + "DomainResource" + "Reference" + "Identifier" + "BackboneElement"})) + (comment (fixt/load-data!) @fixt/data + (keys @fixt/data) + ::close) diff --git a/test/aidbox_sdk/fixtures/patient_ir_schema.edn b/test/aidbox_sdk/fixtures/patient_ir_schema.edn index e5a1b65..ed5ffe4 100644 --- a/test/aidbox_sdk/fixtures/patient_ir_schema.edn +++ b/test/aidbox_sdk/fixtures/patient_ir_schema.edn @@ -153,6 +153,7 @@ :array false, :required false, :value "string", + :valueset "administrative-gender" :type "code", :choice-option false} {:name "maritalStatus", @@ -259,6 +260,7 @@ :base "BackboneElement" :name "Patient_Contact"}], :base "http://hl7.org/fhir/StructureDefinition/DomainResource", + :valueset-deps #{"administrative-gender"} :deps #{"Address" "Attachment" diff --git a/test/aidbox_sdk/generator/typescript_test.clj b/test/aidbox_sdk/generator/typescript_test.clj index 2222047..2b658d0 100644 --- a/test/aidbox_sdk/generator/typescript_test.clj +++ b/test/aidbox_sdk/generator/typescript_test.clj @@ -3,6 +3,7 @@ [aidbox-sdk.fixtures :as fixt] [aidbox-sdk.generator :as sut] [aidbox-sdk.generator.typescript :refer [generator] :as gen.typescript] + [matcho.core :refer [match]] [clojure.java.io :as io] [clojure.string :as str] [clojure.test :refer [deftest is testing use-fixtures]])) @@ -11,7 +12,7 @@ (deftest test-generate-deps (testing "import for base package" - (is (= "import { Bundle } from \"./Bundle\";\nimport * as vs from \"./valuesets\"" + (is (= "import { Bundle } from \"./Bundle\";" (gen.typescript/generate-deps {:package "hl7.fhir.r4.core" :deps #{"Bundle"}})))) @@ -106,28 +107,30 @@ (deftest test-generate-class (testing "base" - (is (= (str/join "\n" ["export type Patient = DomainResource & {" - " address?: Address[];" - " managingOrganization?: Reference;" - " name?: HumanName[];" - " birthDate?: string;" - " _birthDate: Element;" - " multipleBirth?: boolean | number;" - " deceased?: string | boolean;" - " photo?: Attachment[];" - " link?: PatientLink[];" - " active?: boolean;" - " _active: Element;" - " communication?: PatientCommunication[];" - " identifier?: Identifier[];" - " telecom?: ContactPoint[];" - " generalPractitioner?: Reference[];" - " gender?: string;" - " _gender: Element;" - " maritalStatus?: CodeableConcept;" - " contact?: PatientContact[];" - "};"]) - (gen.typescript/generate-class (fixt/get-data :patient-ir-schema))))) + (match + (str/split-lines + (gen.typescript/generate-class (fixt/get-data :patient-ir-schema))) + ["export type Patient = DomainResource & {" + " address?: Address[];" + " managingOrganization?: Reference;" + " name?: HumanName[];" + " birthDate?: string;" + " _birthDate?: Element;" + " multipleBirth?: boolean | number;" + " deceased?: string | boolean;" + " photo?: Attachment[];" + " link?: PatientLink[];" + " active?: boolean;" + " _active?: Element;" + " communication?: PatientCommunication[];" + " identifier?: Identifier[];" + " telecom?: ContactPoint[];" + " generalPractitioner?: Reference[];" + " gender?: AdministrativeGender;" + " _gender?: Element;" + " maritalStatus?: CodeableConcept;" + " contact?: PatientContact[];" + "};"])) (testing "empty elements" (is (= "export type Base = {};" @@ -143,49 +146,51 @@ :deps #{}})))) (testing "with inner classes" - (is (= (str/join "\n" ["export type PatientLink = BackboneElement & {" - " type: string;" - " other: Reference;" - "};" - "" - "export type PatientCommunication = BackboneElement & {" - " language: CodeableConcept;" - " preferred?: boolean;" - "};" - "" - "export type PatientContact = BackboneElement & {" - " name?: HumanName;" - " gender?: string;" - " period?: Period;" - " address?: Address;" - " telecom?: ContactPoint[];" - " organization?: Reference;" - " relationship?: CodeableConcept[];" - "};" - "" - "export type Patient = DomainResource & {" - " address?: Address[];" - " managingOrganization?: Reference;" - " name?: HumanName[];" - " birthDate?: string;" - " _birthDate: Element;" - " multipleBirth?: boolean | number;" - " deceased?: string | boolean;" - " photo?: Attachment[];" - " link?: PatientLink[];" - " active?: boolean;" - " _active: Element;" - " communication?: PatientCommunication[];" - " identifier?: Identifier[];" - " telecom?: ContactPoint[];" - " generalPractitioner?: Reference[];" - " gender?: string;" - " _gender: Element;" - " maritalStatus?: CodeableConcept;" - " contact?: PatientContact[];" - "};"]) - (gen.typescript/generate-class (fixt/get-data :patient-ir-schema) - (map gen.typescript/generate-class (:backbone-elements (fixt/get-data :patient-ir-schema)))))))) + (match + (str/split-lines + (gen.typescript/generate-class (fixt/get-data :patient-ir-schema) + (map gen.typescript/generate-class (:backbone-elements (fixt/get-data :patient-ir-schema))))) + ["export type PatientLink = BackboneElement & {" + " type: string;" + " other: Reference;" + "};" + "" + "export type PatientCommunication = BackboneElement & {" + " language: CodeableConcept;" + " preferred?: boolean;" + "};" + "" + "export type PatientContact = BackboneElement & {" + " name?: HumanName;" + " gender?: string;" + " period?: Period;" + " address?: Address;" + " telecom?: ContactPoint[];" + " organization?: Reference;" + " relationship?: CodeableConcept[];" + "};" + "" + "export type Patient = DomainResource & {" + " address?: Address[];" + " managingOrganization?: Reference;" + " name?: HumanName[];" + " birthDate?: string;" + " _birthDate?: Element;" + " multipleBirth?: boolean | number;" + " deceased?: string | boolean;" + " photo?: Attachment[];" + " link?: PatientLink[];" + " active?: boolean;" + " _active?: Element;" + " communication?: PatientCommunication[];" + " identifier?: Identifier[];" + " telecom?: ContactPoint[];" + " generalPractitioner?: Reference[];" + " gender?: AdministrativeGender;" + " _gender?: Element;" + " maritalStatus?: CodeableConcept;" + " contact?: PatientContact[];" + "};"]))) #_(deftest test-generate-datatypes (is @@ -195,11 +200,61 @@ (sut/generate-datatypes generator [fixtures/coding-ir-schema])))) (deftest test-generate-resources - (is - (= {:path (io/file "hl7-fhir-r4-core/Patient.ts"), - :content - "import { Address } from \"./Address\";\nimport { Attachment } from \"./Attachment\";\nimport { Period } from \"./Period\";\nimport { CodeableConcept } from \"./CodeableConcept\";\nimport { ContactPoint } from \"./ContactPoint\";\nimport { HumanName } from \"./HumanName\";\nimport { DomainResource } from \"./DomainResource\";\nimport { Reference } from \"./Reference\";\nimport { Identifier } from \"./Identifier\";\nimport { BackboneElement } from \"./BackboneElement\";\nimport * as vs from \"./valuesets\"\n\nexport type PatientLink = BackboneElement & {\n type: string;\n other: Reference;\n};\n\nexport type PatientCommunication = BackboneElement & {\n language: CodeableConcept;\n preferred?: boolean;\n};\n\nexport type PatientContact = BackboneElement & {\n name?: HumanName;\n gender?: string;\n period?: Period;\n address?: Address;\n telecom?: ContactPoint[];\n organization?: Reference;\n relationship?: CodeableConcept[];\n};\n\nexport type Patient = DomainResource & {\n address?: Address[];\n managingOrganization?: Reference;\n name?: HumanName[];\n birthDate?: string;\n _birthDate: Element;\n multipleBirth?: boolean | number;\n deceased?: string | boolean;\n photo?: Attachment[];\n link?: PatientLink[];\n active?: boolean;\n _active: Element;\n communication?: PatientCommunication[];\n identifier?: Identifier[];\n telecom?: ContactPoint[];\n generalPractitioner?: Reference[];\n gender?: string;\n _gender: Element;\n maritalStatus?: CodeableConcept;\n contact?: PatientContact[];\n};"} - (sut/generate-resource-module generator (fixt/get-data :patient-ir-schema))))) + (match + (str/split-lines (:content (sut/generate-resource-module generator (fixt/get-data :patient-ir-schema)))) + ["import { Address } from \"./Address\";" + "import { Attachment } from \"./Attachment\";" + "import { Period } from \"./Period\";" + "import { CodeableConcept } from \"./CodeableConcept\";" + "import { ContactPoint } from \"./ContactPoint\";" + "import { HumanName } from \"./HumanName\";" + "import { DomainResource } from \"./DomainResource\";" + "import { Reference } from \"./Reference\";" + "import { Identifier } from \"./Identifier\";" + "import { BackboneElement } from \"./BackboneElement\";" + "import { AdministrativeGender } from \"./valuesets\"" + "" + "export type PatientLink = BackboneElement & {" + " type: string;" + " other: Reference;" + "};" + "" + "export type PatientCommunication = BackboneElement & {" + " language: CodeableConcept;" + " preferred?: boolean;" + "};" + "" + "export type PatientContact = BackboneElement & {" + " name?: HumanName;" + " gender?: string;" + " period?: Period;" + " address?: Address;" + " telecom?: ContactPoint[];" + " organization?: Reference;" + " relationship?: CodeableConcept[];" + "};" + "" + "export type Patient = DomainResource & {" + " address?: Address[];" + " managingOrganization?: Reference;" + " name?: HumanName[];" + " birthDate?: string;" + " _birthDate?: Element;" + " multipleBirth?: boolean | number;" + " deceased?: string | boolean;" + " photo?: Attachment[];" + " link?: PatientLink[];" + " active?: boolean;" + " _active?: Element;" + " communication?: PatientCommunication[];" + " identifier?: Identifier[];" + " telecom?: ContactPoint[];" + " generalPractitioner?: Reference[];" + " gender?: AdministrativeGender;" + " _gender?: Element;" + " maritalStatus?: CodeableConcept;" + " contact?: PatientContact[];" + "};"])) #_(deftest test-generate-search-params (is