diff --git a/src/aidbox_sdk/converter.clj b/src/aidbox_sdk/converter.clj index e634859..26d147d 100644 --- a/src/aidbox_sdk/converter.clj +++ b/src/aidbox_sdk/converter.clj @@ -188,6 +188,10 @@ (url->resource-name (:url schema))) (safe-conj (hash-map :base (get schema :base) + ;; FIXME `id`and `kind` need only for .NET resource map generation + ;; remove them from IR Schema after refactoring + :id (get schema :id) + :kind (get schema :kind) :resource-name (url->resource-name (get schema :url)) :base-resource-name (when (get schema :base) (url->resource-name (get schema :base))) diff --git a/src/aidbox_sdk/core.clj b/src/aidbox_sdk/core.clj index fdc9131..7199c3e 100644 --- a/src/aidbox_sdk/core.clj +++ b/src/aidbox_sdk/core.clj @@ -115,7 +115,7 @@ (save-files! (generator/generate-search-params generator' search-param-ir-schemas)) (println "Generating common SDK files") - (save-files! (generator/generate-sdk-files generator')) + (save-files! (generator/generate-sdk-files generator' ir-schemas)) (println "Finished succesfully!") (println "Output dir: " (.getAbsolutePath output-dir)))) diff --git a/src/aidbox_sdk/generator.clj b/src/aidbox_sdk/generator.clj index d98e0dc..a65013f 100644 --- a/src/aidbox_sdk/generator.clj +++ b/src/aidbox_sdk/generator.clj @@ -20,4 +20,4 @@ (generate-resource-module [this ir-schema]) (generate-search-params [this ir-schemas]) (generate-constraints [this ir-schemas]) - (generate-sdk-files [this])) + (generate-sdk-files [this ir-schemas])) diff --git a/src/aidbox_sdk/generator/dotnet.clj b/src/aidbox_sdk/generator/dotnet.clj index 2972dae..f07a872 100644 --- a/src/aidbox_sdk/generator/dotnet.clj +++ b/src/aidbox_sdk/generator/dotnet.clj @@ -4,7 +4,8 @@ [aidbox-sdk.generator.helpers :refer [->pascal-case uppercase-first-letter]] [aidbox-sdk.generator.utils :as u] [clojure.java.io :as io] - [clojure.string :as str]) + [clojure.string :as str] + [aidbox-sdk.fhir :as fhir]) (:import [aidbox_sdk.generator CodeGenerator])) @@ -244,6 +245,60 @@ (io/file (package->directory (:package ir-schema)) (str (->pascal-case (:resource-name ir-schema)) ".cs"))) +(defn generate-resource-map [schemas] + ;; TODO refactor + ;; The base or not base should be decided on converter layer + (->> schemas + (remove (fn [schema] + (fhir/primitive-type? schema))) + (map (fn [schema] + (assoc schema :base? (or (fhir/base-type? schema) + (and (fhir/datatype? schema) + (not (fhir/primitive-type? schema))))))) + (sort-by :base?) + (reverse) + (map (fn [schema] + (let [class-name' (class-name (:resource-name schema)) + module-name (if (:base? schema) + "Base" + (package->module-name (:package schema)))] + (format "{ typeof(Aidbox.FHIR.%s.%s), \"%s\"}" + module-name + class-name' + class-name')))))) + +(defn generate-utils-namespace [ir-schemas] + (str/join "\n" ["using System.Text.Json;" + "using System.Text.Json.Serialization;" + "" + "namespace Aidbox.FHIR.Utils;" + "" + "public interface IResource { string? Id { get; set; } }" + "" + "public class LowercaseNamingPolicy : JsonNamingPolicy" + "{" + " public override string ConvertName(string name) => name.ToLower();" + "}" + "" + "public class Config" + "{" + " public static readonly JsonSerializerOptions JsonSerializerOptions = new()" + " {" + " DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull," + " PropertyNamingPolicy = JsonNamingPolicy.CamelCase," + " Converters = { new JsonStringEnumConverter(new LowercaseNamingPolicy()) }," + " WriteIndented = true" + " };" + "" + " public static readonly Dictionary ResourceMap = new()" + " {" + (->> (generate-resource-map ir-schemas) + (map u/add-indent) + (map u/add-indent) + (str/join ",\n")) + " };" + "}"])) + (defrecord DotNetCodeGenerator [] CodeGenerator (generate-datatypes [_ ir-schemas] @@ -284,6 +339,9 @@ (assoc ir-schema :url (:url ir-schema)))}) constrained-ir-schemas)) - (generate-sdk-files [_] (generator/prepare-sdk-files :dotnet))) + (generate-sdk-files [_ ir-schemas] + (let [common-sdk-files (generator/prepare-sdk-files :dotnet) + utils (generate-utils-namespace ir-schemas)] + (conj common-sdk-files {:path (io/file "Utils.cs") :content utils})))) (def generator (->DotNetCodeGenerator)) diff --git a/src/aidbox_sdk/generator/python.clj b/src/aidbox_sdk/generator/python.clj index b8be141..91eafca 100644 --- a/src/aidbox_sdk/generator/python.clj +++ b/src/aidbox_sdk/generator/python.clj @@ -272,6 +272,6 @@ (map generate-class (:backbone-elements ir-schema)))])}) constraint-ir-schemas)) - (generate-sdk-files [_] (generator/prepare-sdk-files :python))) + (generate-sdk-files [_ _] (generator/prepare-sdk-files :python))) (def generator (->PythonCodeGenerator)) diff --git a/src/aidbox_sdk/generator/typescript.clj b/src/aidbox_sdk/generator/typescript.clj index 16572b9..1f83902 100644 --- a/src/aidbox_sdk/generator/typescript.clj +++ b/src/aidbox_sdk/generator/typescript.clj @@ -214,6 +214,6 @@ (map generate-class (:backbone-elements ir-schema)))]})}) ir-schemas)) - (generate-sdk-files [_] (generator/prepare-sdk-files :typescript))) + (generate-sdk-files [_ _] (generator/prepare-sdk-files :typescript))) (def generator (->TypeScriptCodeGenerator)) diff --git a/test/aidbox_sdk/converter_test.clj b/test/aidbox_sdk/converter_test.clj index 4955819..bd96557 100644 --- a/test/aidbox_sdk/converter_test.clj +++ b/test/aidbox_sdk/converter_test.clj @@ -103,7 +103,9 @@ (testing "convert constraint" (is (= [(fixt/get-data :organization-preferred-contact-ir-schema)] - (sut/convert [(fixt/get-data :organization-preferred-contact-fhir-schema)]))))) + (sut/convert [(fixt/get-data :organization-preferred-contact-fhir-schema)]) + + )))) (deftest test-apply-constraints (testing "constraints" 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 bfc1463..d5b1565 100644 --- a/test/aidbox_sdk/fixtures/organization_preferred_contact_ir_schema.edn +++ b/test/aidbox_sdk/fixtures/organization_preferred_contact_ir_schema.edn @@ -3,6 +3,8 @@ :name "Extension", :type "Extension", :fhir-version "hl7.fhir.r4.core" + :id "organization-preferredContact", + :kind "complex-type", :resource-name "organization-preferred-Contact" :elements [{:name "url", diff --git a/test/aidbox_sdk/fixtures/schemas.clj b/test/aidbox_sdk/fixtures/schemas.clj index 14f637d..ba39c24 100644 --- a/test/aidbox_sdk/fixtures/schemas.clj +++ b/test/aidbox_sdk/fixtures/schemas.clj @@ -796,6 +796,8 @@ {:package "hl7.fhir.r4.core", :derivation "specialization", :name "Patient", + :id "Patient", + :kind "resource", :resource-name "Patient", :type "Patient", :elements diff --git a/test/aidbox_sdk/generator/dotnet_test.clj b/test/aidbox_sdk/generator/dotnet_test.clj index 39312ca..9d68cbc 100644 --- a/test/aidbox_sdk/generator/dotnet_test.clj +++ b/test/aidbox_sdk/generator/dotnet_test.clj @@ -190,3 +190,25 @@ {:path (io/file "hl7-fhir-r4-core/Patient.cs"), :content "using Aidbox.FHIR.Base;\nusing Aidbox.FHIR.Utils;\n\nnamespace Aidbox.FHIR.R4.Core;\n\npublic class Patient : DomainResource, IResource\n{\n public bool? MultipleBirthBoolean { get; set; }\n public Base.Address[]? Address { get; set; }\n public string? DeceasedDateTime { get; set; }\n public Base.ResourceReference? ManagingOrganization { get; set; }\n public bool? DeceasedBoolean { get; set; }\n public Base.HumanName[]? Name { get; set; }\n public string? BirthDate { get; set; }\n public int? MultipleBirthInteger { get; set; }\n public object? MultipleBirth \n {\n get\n {\n if (MultipleBirthBoolean is not null)\n {\n return MultipleBirthBoolean;\n }\n \n if (MultipleBirthInteger is not null)\n {\n return MultipleBirthInteger;\n }\n \n return null;\n }\n \n set\n {\n if (value?.GetType() == typeof(bool))\n {\n MultipleBirthBoolean = (bool)value;return;\n }\n \n if (value?.GetType() == typeof(int))\n {\n MultipleBirthInteger = (int)value;return;\n }\n \n throw new ArgumentException(\"Invalid type provided\");\n }\n }\n public object? Deceased \n {\n get\n {\n if (DeceasedDateTime is not null)\n {\n return DeceasedDateTime;\n }\n \n if (DeceasedBoolean is not null)\n {\n return DeceasedBoolean;\n }\n \n return null;\n }\n \n set\n {\n if (value?.GetType() == typeof(string))\n {\n DeceasedDateTime = (string)value;return;\n }\n \n if (value?.GetType() == typeof(bool))\n {\n DeceasedBoolean = (bool)value;return;\n }\n \n throw new ArgumentException(\"Invalid type provided\");\n }\n }\n public Base.Attachment[]? Photo { get; set; }\n public Patient_Link[]? Link { get; set; }\n public bool? Active { get; set; }\n public Patient_Communication[]? Communication { get; set; }\n public Base.Identifier[]? Identifier { get; set; }\n public Base.ContactPoint[]? Telecom { get; set; }\n public Base.ResourceReference[]? GeneralPractitioner { get; set; }\n public string? Gender { get; set; }\n public Base.CodeableConcept? MaritalStatus { get; set; }\n public Patient_Contact[]? Contact { get; set; }\n\n public class Patient_Link : BackboneElement\n {\n public required string Type { get; set; }\n public required Base.ResourceReference Other { get; set; }\n }\n\n public class Patient_Communication : BackboneElement\n {\n public required Base.CodeableConcept Language { get; set; }\n public bool? Preferred { get; set; }\n }\n\n public class Patient_Contact : BackboneElement\n {\n public Base.HumanName? Name { get; set; }\n public string? Gender { get; set; }\n public Base.Period? Period { get; set; }\n public Base.Address? Address { get; set; }\n public Base.ContactPoint[]? Telecom { get; set; }\n public Base.ResourceReference? Organization { get; set; }\n public Base.CodeableConcept[]? Relationship { get; set; }\n }\n}"}))) + +(deftest test-generate-resource-map + (is + (= ["{ typeof(Aidbox.FHIR.Aidbox.FHIR.R4.Core.Observation), \"Observation\"}" + "{ typeof(Aidbox.FHIR.Aidbox.FHIR.Uv.Sdc.SdcQuestionLibrary), \"SdcQuestionLibrary\"}"] + (gen.dotnet/generate-resource-map [{:package "hl7.fhir.uv.sdc", + :name "Demographics", + :id "sdc-question-library" + :resource-name "sdc-question-library", + :type "Demographics", + :url "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-question-library", + :base "http://hl7.org/fhir/StructureDefinition/Resource", + :fhir-version "hl7.fhir.r4.core"} + {:package "hl7.fhir.r4.core", + :derivation "specialization", + :name "Observation", + :resource-name "Observation", + :type "Observation", + :url "http://hl7.org/fhir/StructureDefinition/Observation", + :base-resource-name "Domain-Resource", + :base "http://hl7.org/fhir/StructureDefinition/DomainResource", + :fhir-version "hl7.fhir.r4.core"}]))))