From fad2cd1b7f02d5b23507f4fdcc6eff3ed49d8e11 Mon Sep 17 00:00:00 2001 From: Vitaly Kravtsov Date: Thu, 26 Sep 2024 23:20:50 +0600 Subject: [PATCH] fix: resource-name for typescript --- src/aidbox_sdk/converter.clj | 8 ++- src/aidbox_sdk/generator/helpers.clj | 4 ++ src/aidbox_sdk/generator/typescript.clj | 55 +++++++++------- .../fixtures/extension_ir_schema.edn | 2 + ...ganization_preferred_contact_ir_schema.edn | 1 + .../aidbox_sdk/fixtures/patient_ir_schema.edn | 1 + .../patient_nationality_ir_schema.edn | 2 + test/aidbox_sdk/generator/typescript_test.clj | 65 +++++++++++++++++-- 8 files changed, 108 insertions(+), 30 deletions(-) diff --git a/src/aidbox_sdk/converter.clj b/src/aidbox_sdk/converter.clj index 73b5fc4..e168cb4 100644 --- a/src/aidbox_sdk/converter.clj +++ b/src/aidbox_sdk/converter.clj @@ -86,13 +86,13 @@ :else (if type (str "Base." type) "string"))) (defn- derive-basic-type [name element] - (get-type name (url->resource-name (:type element)))) + (get-type name (str/replace (or (url->resource-name (:type element)) "") #"-" ""))) (defn- transform-element [name element required] (->> (derive-basic-type name element))) (defn- resolve-backbone-elements [[k, v]] - (if (= (url->resource-name (:type v)) "BackboneElement") (vector k, v) (vector))) + (if (= (:type v) "BackboneElement") (vector k, v) (vector))) (defn- collect-types [parent-name required [k v]] (if (contains? v :choices) @@ -127,6 +127,7 @@ (seq (:elements schema))) backbone-elements (remove empty? (:backbone-elements data))] (-> data + (assoc :resource-name name) (assoc :backbone-elements (if (empty? backbone-elements) [] @@ -337,7 +338,8 @@ (apply-patterns (:url constraint) (filter #(contains? (last %) :pattern) (:elements constraint))) - ((fn [schema] (update schema :deps set/union #{"Meta"}))))) + ((fn [schema] (update schema :deps set/union #{"Meta"}))) + ((fn [schema] (assoc schema :resource-name (url->resource-name (:url constraint)) ))))) (defn convert-constraints [constraint-schemas base-schemas] (let [base-schemas (vector->map base-schemas)] diff --git a/src/aidbox_sdk/generator/helpers.clj b/src/aidbox_sdk/generator/helpers.clj index 57ee85d..612be1f 100644 --- a/src/aidbox_sdk/generator/helpers.clj +++ b/src/aidbox_sdk/generator/helpers.clj @@ -20,6 +20,10 @@ (defn ->snake-case [s] (str/join "_" (map str/lower-case (words s)))) +(defn ->camel-case [s] + (str/join (into [(first (words s))] + (map str/capitalize (rest (words s)))))) + (defn uppercase-first-letter "NOTE: Do not confuse with `capitalize` and `->pascal-case` functions. Capitalize function lowercasing all letters after first. diff --git a/src/aidbox_sdk/generator/typescript.clj b/src/aidbox_sdk/generator/typescript.clj index 227fa3b..dab11bc 100644 --- a/src/aidbox_sdk/generator/typescript.clj +++ b/src/aidbox_sdk/generator/typescript.clj @@ -1,7 +1,8 @@ (ns aidbox-sdk.generator.typescript (:require [aidbox-sdk.generator :as generator] - [aidbox-sdk.generator.helpers :refer [->pascal-case uppercase-first-letter]] + [aidbox-sdk.generator.helpers :refer [->pascal-case uppercase-first-letter + ->camel-case]] [aidbox-sdk.generator.utils :as u] [clojure.java.io :as io] [clojure.string :as str]) @@ -19,16 +20,19 @@ (defn url->resource-name [reference] (last (str/split (str reference) #"/"))) +(defn resource-name->class-name [rn] + (->pascal-case rn)) + (defn datatypes-file-path [] (io/file "datatypes.ts")) (defn resource-file-path [ir-schema] (io/file (package->directory (:package ir-schema)) - (str (->pascal-case (url->resource-name (:url ir-schema))) ".ts"))) + (str (->pascal-case (:resource-name ir-schema)) ".ts"))) (defn constraint-file-path [ir-schema name] (io/file (package->directory (:package ir-schema)) - (str (->pascal-case (url->resource-name name)) ".ts"))) + (str (->pascal-case (:resource-name ir-schema)) ".ts"))) (defn search-param-filepath [ir-schema] (io/file "search" (str (:name ir-schema) "SearchParameters.ts"))) @@ -68,11 +72,9 @@ fhir-type)) (defn class-name - "Generate class name from schema url." - [url] - (str/replace - (uppercase-first-letter (url->resource-name url)) - #"_|-" "")) + "Generate class name from schema resource name." + [resource-name] + (->pascal-case resource-name)) (defn generate-polymorphic-property [{:keys [name required choices]}] (let [type (->> choices @@ -83,7 +85,7 @@ (str name (when-not required "?") ": " type ";"))) (defn ->backbone-type [element] - (str/replace (str (:base element) (uppercase-first-letter (:name element))) "_" "")) + (str/replace (str (:base element) (uppercase-first-letter (:name element))) #"[_-]" "")) (defn generate-property [{:keys [name array required type choices profile] :as element}] (cond choices @@ -101,9 +103,14 @@ (defn generate-class "Generates TypeScript type from IR (intermediate representation) schema." [ir-schema & [inner-classes]] - (let [base-class (url->resource-name (:base ir-schema)) - schema-name (or (:url ir-schema) (:name ir-schema)) - class-name' (class-name schema-name) + (let [base-class (class-name (or (:base-resource-name ir-schema) + ;; need for BackboneElement + (:base ir-schema) + "")) + class-name' (class-name (or (:resource-name ir-schema) + ;; need for BackboneElement + (:name ir-schema) + "")) properties (->> (:elements ir-schema) (remove #(:choice-option %)) (map generate-property) @@ -144,7 +151,9 @@ (defn- path->name [path] (str/replace path #"(\.ts)|[\.\/]" "")) -(defn generate-deps [deps] +(defn generate-deps + "Takes a list of resource names and generates import declarations." + [deps] (->> deps (map (fn [{:keys [module members]}] (if (seq members) @@ -156,7 +165,11 @@ [& {:keys [deps classes] :or {classes []}}] (->> (conj [] - (generate-deps deps) + (->> deps + (map class-name) + (map (fn [d] {:module (str "./" d) :members [d]})) + generate-deps) + classes) (flatten) (str/join "\n\n"))) @@ -168,16 +181,14 @@ (map (fn [ir-schema] {:path (resource-file-path ir-schema) :content (generate-module - :deps (map (fn [d] {:module (str "./" d) :members [d]}) - (:deps ir-schema)) + :deps (:deps ir-schema) :classes [(generate-class ir-schema (map generate-class (:backbone-elements ir-schema)))])}) ir-schemas))) (generate-resource-module [_ ir-schema] {:path (resource-file-path ir-schema) :content (generate-module - {:deps (map (fn [d] {:module (str "./" d) :members [d]}) - (:deps ir-schema)) + {:deps (:deps ir-schema) :classes [(generate-class ir-schema (map generate-class (:backbone-elements ir-schema)))]})}) @@ -185,7 +196,7 @@ (map (fn [ir-schema] {:path (search-param-filepath ir-schema) :content (generate-module - :deps [] + :deps (:deps ir-schema) :classes [(generate-class {:name (format "%sSearchParameters" (:name ir-schema)) :base (when (:base ir-schema) @@ -197,11 +208,11 @@ (mapv (fn [[constraint-name schema]] {:path (constraint-file-path schema constraint-name) :content (generate-module - :deps (map (fn [d] {:module (str "./" d) :members [d]}) - (:deps schema)) + :deps (:deps schema) :classes (generate-class (assoc schema :url constraint-name) (map generate-class (:backbone-elements schema))))}) ir-schemas)) - (generate-sdk-files [this] (generator/prepare-sdk-files :typescript))) + + (generate-sdk-files [_] (generator/prepare-sdk-files :typescript))) (def generator (->TypeScriptCodeGenerator)) diff --git a/test/aidbox_sdk/fixtures/extension_ir_schema.edn b/test/aidbox_sdk/fixtures/extension_ir_schema.edn index b4361eb..e1cc68b 100644 --- a/test/aidbox_sdk/fixtures/extension_ir_schema.edn +++ b/test/aidbox_sdk/fixtures/extension_ir_schema.edn @@ -3,6 +3,8 @@ :derivation "specialization", :name "Extension", :type "Extension", + :resource-name "Extension" + :base-resource-name "Element" :elements [{:name "valueBase64Binary", :base "Extension", diff --git a/test/aidbox_sdk/fixtures/organization_preferred_contact_ir_schema.edn b/test/aidbox_sdk/fixtures/organization_preferred_contact_ir_schema.edn index 73984c8..6702d8b 100644 --- a/test/aidbox_sdk/fixtures/organization_preferred_contact_ir_schema.edn +++ b/test/aidbox_sdk/fixtures/organization_preferred_contact_ir_schema.edn @@ -2,6 +2,7 @@ :derivation "constraint", :name "Extension", :type "Extension", + :resource-name "organization-preferred-Contact" :elements [{:name "url", :base "Extension", diff --git a/test/aidbox_sdk/fixtures/patient_ir_schema.edn b/test/aidbox_sdk/fixtures/patient_ir_schema.edn index 69ee4a5..4447b46 100644 --- a/test/aidbox_sdk/fixtures/patient_ir_schema.edn +++ b/test/aidbox_sdk/fixtures/patient_ir_schema.edn @@ -2,6 +2,7 @@ :derivation "specialization", :name "Patient", :type "Patient", + :resource-name "Patient" :elements [{:name "multipleBirthBoolean", :base "Patient", diff --git a/test/aidbox_sdk/fixtures/patient_nationality_ir_schema.edn b/test/aidbox_sdk/fixtures/patient_nationality_ir_schema.edn index e78323e..ab40883 100644 --- a/test/aidbox_sdk/fixtures/patient_nationality_ir_schema.edn +++ b/test/aidbox_sdk/fixtures/patient_nationality_ir_schema.edn @@ -2,6 +2,8 @@ {:package "hl7.fhir.r4.core", :derivation "specialization", :name "Extension", + :resource-name "patient-nationality" + :base-resource-name "Element" :type "Extension", :elements [{:name "meta", diff --git a/test/aidbox_sdk/generator/typescript_test.clj b/test/aidbox_sdk/generator/typescript_test.clj index f4f3434..8345ee2 100644 --- a/test/aidbox_sdk/generator/typescript_test.clj +++ b/test/aidbox_sdk/generator/typescript_test.clj @@ -1,10 +1,10 @@ (ns aidbox-sdk.generator.typescript-test (:require [aidbox-sdk.fixtures :as fixt] - [aidbox-sdk.fixtures.schemas :as fixtures] [aidbox-sdk.generator :as sut] [aidbox-sdk.generator.typescript :refer [generator] :as gen.typescript] [clojure.java.io :as io] + [clojure.string :as str] [clojure.test :refer [deftest is testing use-fixtures]])) (use-fixtures :once fixt/prepare-examples) @@ -65,7 +65,7 @@ ) (testing "element with meta" - (is (= "meta: Meta = { profile: \"http://hl7.org/fhir/StructureDefinition/vitalsigns\" }" + (is (= "meta: Meta & { profile: [\"http://hl7.org/fhir/StructureDefinition/vitalsigns\"] }" (gen.typescript/generate-property {:name "meta", :required true, :value "Meta", @@ -96,7 +96,24 @@ (deftest test-generate-class (testing "base" - (is (= "export type Patient = DomainResource & {\n address?: Address[];\n managingOrganization?: Reference;\n name?: HumanName[];\n birthDate?: string;\n multipleBirth?: boolean | number;\n deceased?: string | boolean;\n photo?: Attachment[];\n link?: PatientLink[];\n active?: boolean;\n communication?: PatientCommunication[];\n identifier?: Identifier[];\n telecom?: ContactPoint[];\n generalPractitioner?: Reference[];\n gender?: string;\n maritalStatus?: CodeableConcept;\n contact?: PatientContact[];\n};" + (is (= (str/join "\n" ["export type Patient = DomainResource & {" + " address?: Address[];" + " managingOrganization?: Reference;" + " name?: HumanName[];" + " birthDate?: string;" + " multipleBirth?: boolean | number;" + " deceased?: string | boolean;" + " photo?: Attachment[];" + " link?: PatientLink[];" + " active?: boolean;" + " communication?: PatientCommunication[];" + " identifier?: Identifier[];" + " telecom?: ContactPoint[];" + " generalPractitioner?: Reference[];" + " gender?: string;" + " maritalStatus?: CodeableConcept;" + " contact?: PatientContact[];" + "};"]) (gen.typescript/generate-class (fixt/get-data :patient-ir-schema))))) (testing "empty elements" @@ -106,13 +123,51 @@ :type "Base", :elements (), :url "http://hl7.org/fhir/StructureDefinition/Base", + :resource-name "Base" :base-resource-name nil, :backbone-elements (), :base nil, :deps #{}})))) (testing "with inner classes" - (is (= "export 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 multipleBirth?: boolean | number;\n deceased?: string | boolean;\n photo?: Attachment[];\n link?: PatientLink[];\n active?: boolean;\n communication?: PatientCommunication[];\n identifier?: Identifier[];\n telecom?: ContactPoint[];\n generalPractitioner?: Reference[];\n gender?: string;\n maritalStatus?: CodeableConcept;\n contact?: PatientContact[];\n};" + (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;" + " multipleBirth?: boolean | number;" + " deceased?: string | boolean;" + " photo?: Attachment[];" + " link?: PatientLink[];" + " active?: boolean;" + " communication?: PatientCommunication[];" + " identifier?: Identifier[];" + " telecom?: ContactPoint[];" + " generalPractitioner?: Reference[];" + " gender?: string;" + " 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)))))))) @@ -167,7 +222,7 @@ (comment (fixt/load-data!) - (fixt/get-data :patient-search-params-ir-schema) + (fixt/get-data :patient-ir-schema) @fixt/data