diff --git a/src/aidbox_sdk/converter.clj b/src/aidbox_sdk/converter.clj index 26d147d..4bfe81f 100644 --- a/src/aidbox_sdk/converter.clj +++ b/src/aidbox_sdk/converter.clj @@ -197,6 +197,7 @@ (url->resource-name (get schema :base))) :fhir-version (get schema :fhir-version) :package (get schema :package) + :service-type? (fhir/service-type? schema) :url (get schema :url) :type (get schema :type) :derivation (get schema :derivation)))))) @@ -216,6 +217,23 @@ x)) elements)))) +(defn add-service-type-flag + "Adds `service-type` flag to each element. The value will be `true` if type of + element belongs to service-types, and `false` otherwise." + [schema] + (letfn [(update-elements [elements] + (walk/postwalk + (fn [x] + (if (:type x) + (let [service-type? (fhir/service-type-element? (:fhir-version schema) x)] + (assoc x :service-type service-type?)) + x)) + elements))] + (-> schema + (update :elements update-elements) + (update :backbone-elements (fn [backbone-elements] + (for [backbone backbone-elements] + (update backbone :elements update-elements))))))) ;; (defn find-elements-by-names [element-names schema] @@ -267,7 +285,6 @@ ;; Convert main function ;; - (defn convert [schemas] (->> schemas (map resolve-element-references) @@ -276,7 +293,8 @@ (map (fn [schema] (update schema :backbone-elements #(resolve-choices (flatten-backbones % []))))) (resolve-choices) - (resolve-dependencies))) + (resolve-dependencies) + (map add-service-type-flag))) ;; ;; Search Params diff --git a/src/aidbox_sdk/fhir.clj b/src/aidbox_sdk/fhir.clj index 472dcd8..191c693 100644 --- a/src/aidbox_sdk/fhir.clj +++ b/src/aidbox_sdk/fhir.clj @@ -5,7 +5,10 @@ ;; Base Types and Datatypes -(def r4-base-types #{"Element" "Resource" "DomainResource"}) +(def r4-base-types #{"Element" "Resource" "DomainResource" + ;; NOTE: technically `Bundle` is not a base type, + ;; but it's here for a reason. + "Bundle"}) (def r4-primitive-types #{"boolean" "integer" "string" "decimal" "uri" "url" "canonical" "base64Binary" @@ -32,6 +35,13 @@ r4-metadata-types r4-special-purpose-datatypes)) +(def r4-service-types + "This is special set of types that are not represented in FHIR in no way, + but are needed for SDK generation. Essentially, service schemas are: + base types + datatypes - primitive-types." + (set/union (set/difference r4-datatypes r4-primitive-types) + r4-base-types)) + (def r4b-base-types r4-base-types) (def r4b-primitive-types r4-primitive-types) @@ -51,6 +61,13 @@ r4b-metadata-types r4b-special-purpose-datatypes)) +(def r4b-service-types + "This is special set of types that are not represented in FHIR in no way, + but are needed for SDK generation. Essentially, service schemas are: + base types + datatypes - primitive-types." + (set/union (set/difference r4b-datatypes r4b-primitive-types) + r4b-base-types)) + (def r5-base-types #{"Base" "Element" "BackboneElement" "DataType" "PrimitiveType" "BackboneType" "Resource" "DomainResource" "CanonicalResource" "MetadataResource"}) @@ -73,6 +90,13 @@ r5-metadata-types r5-special-purpose-datatypes)) +(def r5-service-types + "This is special set of types that are not represented in FHIR in no way, + but are needed for SDK generation. Essentially, service schemas are: + base types + datatypes - primitive-types." + (set/union (set/difference r5-datatypes r5-primitive-types) + r5-base-types)) + ;; Predicates (defn resource-type-pred [rt] (fn [schema] (= rt (:resourceType schema)))) (defn kind-pred [kind] (fn [schema] (= kind (:kind schema)))) @@ -162,3 +186,13 @@ (defn base-package? [schema] (contains? #{"hl7.fhir.r4.core" "hl7.fhir.r4b.core" "hl7.fhir.r5.core"} (:package schema))) + +(defmulti service-type? :fhir-version) +(defmethod service-type? "hl7.fhir.r4.core" [schema] (contains? r4-service-types (:id schema))) +(defmethod service-type? "hl7.fhir.r4b.core" [schema] (contains? r4b-service-types (:id schema))) +(defmethod service-type? "hl7.fhir.r5.core" [schema] (contains? r5-service-types (:id schema))) + +(defmulti service-type-element? (fn [package _element] package)) +(defmethod service-type-element? "hl7.fhir.r4.core" [_ {:keys [type]}] (contains? r4-service-types type)) +(defmethod service-type-element? "hl7.fhir.r4b.core" [_ {:keys [type]}] (contains? r4b-service-types type)) +(defmethod service-type-element? "hl7.fhir.r5.core" [_ {:keys [type]}] (contains? r5-service-types type)) diff --git a/src/aidbox_sdk/generator/dotnet.clj b/src/aidbox_sdk/generator/dotnet.clj index 915ffa9..6e4a31a 100644 --- a/src/aidbox_sdk/generator/dotnet.clj +++ b/src/aidbox_sdk/generator/dotnet.clj @@ -9,37 +9,45 @@ (:import [aidbox_sdk.generator CodeGenerator])) -(defn ->lang-type [fhir-type] - (case fhir-type - ;; Primitive Types - "boolean" "bool" - "instant" "string" - "time" "string" - "date" "string" - "dateTime" "string" - "decimal" "number" - - "integer" "int" - "unsignedInt" "long" - "positiveInt" "long" - - "integer64" "long" - "base64Binary" "string" +(def base-namespace "Aidbox.FHIR.Base") +(def utils-namespace "Aidbox.FHIR.Utils") + +(def fhir-type->lang-type + {"boolean" "bool" + "instant" "string" + "time" "string" + "date" "string" + "dateTime" "string" + + "decimal" "decimal" + "integer" "int" + "unsignedInt" "long" + "positiveInt" "long" + "integer64" "long" + "base64Binary" "string" + + "uri" "string" + "url" "string" + "canonical" "string" + "oid" "string" + "uuid" "string" + + "string" "string" + "code" "string" + "markdown" "string" + "id" "string" + "xhtml" "string"}) - "uri" "string" - "url" "string" - "canonical" "string" - "oid" "string" - "uuid" "string" - - "string" "string" - "code" "string" - "markdown" "string" - "id" "string" - "xhtml" "string" +(defn ->lang-type [fhir-type] + (get fhir-type->lang-type fhir-type fhir-type)) - ;; else - fhir-type)) +(defn get-type [{:keys [type service-type] :as _element}] + (cond + (= type "Expression") "Base.ResourceExpression" + (= type "Reference") "Base.ResourceReference" + :else (if service-type + (str "Base." type) + (->lang-type type)))) (defn generate-polymorphic-property [element] (str "public object?" @@ -85,13 +93,13 @@ (->> (:choices element) (map (fn [choice] (str (u/x2 u/indent) - "if (value?.GetType() == typeof(" (:value choice) "))\n" + "if (value?.GetType() == typeof(" (get-type choice) "))\n" (u/x2 u/indent) "{" "\n" u/indent (u/x2 u/indent) - (->pascal-case (:name choice)) " = (" (:value choice) ")value;" + (->pascal-case (:name choice)) " = (" (get-type choice) ")value;" "\n" (u/x3 u/indent) "return;" @@ -111,70 +119,70 @@ (map #(str u/indent %)) (str/join "\n")))) -(defn url->resource-name [url] - (last (str/split (str url) #"/"))) - -(defn ->backbone-type [element] - (str/replace (str (:base element) (uppercase-first-letter (:name element))) "[-_]" "")) - (defn generate-property "Generates class property from schema element." - [{:keys [name array required value type choices] :as element}] + [{:keys [name array required type choices service-type] :as element}] (let [name' (->pascal-case name) - lang-type (str/replace (or value type "") #"[_-]" "") + type' (get-type element) + lang-type (str/replace (or type' "") #"[_-]" "") required' (if required "required " "") - type (str - required' - lang-type - (:generic element) - (when array "[]") - (when (and (not required) - (not (:literal element))) "?"))] - (cond choices - (generate-polymorphic-property element) + nullable (if required "" "?") + type (str required' + lang-type + (:generic element) + (when array "[]") + (when (and (not required) + (not (:literal element))) "?"))] + (cond + choices + #_(generate-polymorphic-property element) + nil - (= (:type element) "Meta") - (if (:profile element) - (format "public new Meta Meta { get; } = new() { Profile = [\"%s\"] };" (:profile element)) - (format "public %sMeta %s { get; set; }" required' name')) + (= (:type element) "Meta") + (if (:profile element) + (format "public new Meta Meta { get; } = new() { Profile = [\"%s\"] };" (:profile element)) + (format "public %sMeta%s %s { get; set; }" required' nullable name')) - :else - (str "public " type " " name' " { get; set; }" - (when (and (:required element) - (:codeable-concept-pattern element)) " = new()") - (:meta element))))) + :else + (str "public " type " " name' " { get; set; }" + (when (and (:required element) + (:codeable-concept-pattern element)) " = new()") + (:meta element))))) + +(defn url->resource-name [url] + (last (str/split (str url) #"/"))) (defn class-name "Generate class name from schema url." [resource-name] (let [n (->pascal-case resource-name)] (cond - (= n "Expression") "ResourceExpression" - (= n "Reference") "ResourceReference" + (= n "Expression") "ResourceExpression" + (= n "Reference") "ResourceReference" :else n))) (defn generate-class [schema & [inner-classes]] - (let [base-class (url->resource-name (:base schema)) - schema-name (or (:url schema) (:name schema)) - generic (when (= (:type schema) "Bundle") "") - class-name' (class-name (str (or (:resource-name schema) - ;; need for BackboneElement - (:name schema) - "") generic)) - elements (->> (:elements schema) - (map #(if (and (= (:base %) "Bundle_Entry") - (= (:name %) "resource")) - (assoc % :value "T") - %))) - - properties (->> elements - (map generate-property) - (map u/add-indent) - (str/join "\n")) - + (let [base-class (url->resource-name (:base schema)) + class-name' (if (= (:type schema) "Bundle") + "Bundle" + (class-name (:resource-name schema))) + elements (->> (:elements schema) + ;; NOTE: this is a hack for Bundle since it's a generic + ;; class and currently we do not have a solution for + ;; generating generic classes + (map #(if (and (= (:base %) "Bundle_Entry") + (= (:name %) "resource")) + (-> % + (assoc :type "T") + (assoc :service-type false)) + %))) + properties (->> elements + (map generate-property) + (remove nil?) + (map u/add-indent) + (str/join "\n")) base-class-name (when-not (str/blank? base-class) - (str " : " (uppercase-first-letter base-class)))] - + (str " : " (class-name base-class)))] (str "public class " class-name' base-class-name "\n{" (when-not (str/blank? properties) "\n") @@ -209,31 +217,19 @@ "Generate directory name from package name. hl7.fhir.r4.core#4.0.1 -> hl7-fhir-r4-core" [x] - (-> x - (str/replace #"[\.#]" "-"))) + (str/replace x #"[\.#]" "-")) (defn package->module-name "Convert package name to namespace. hl7.fhir.r4.core#4.0.1 -> Aidbox.FHIR.R4.Core" [x] (str "Aidbox.FHIR." - (->> (-> x - (str/replace #"hl7.fhir." "") - (str/split #"\.")) + (->> (str/split + (str/replace x #"hl7.fhir." "") + #"\.") (map ->pascal-case) (str/join ".")))) -;; -;; Constraints -;; - -(defn generate-constraint-module [schema] - (generate-module - :name (package->module-name (:package schema)) - :deps [{:module "Aidbox.FHIR.Base" :members []} - {:module "Aidbox.FHIR.Utils" :members []}] - :classes (generate-class schema (map generate-class (:backbone-elements schema))))) - ;; ;; main ;; @@ -246,22 +242,15 @@ (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) + (remove fhir/primitive-type?) + (remove fhir/extension?) + (remove fhir/logical?) + (remove :service-type?) + (sort-by :resource-name) (map (fn [schema] (let [class-name' (class-name (:resource-name schema)) - module-name (if (:base? schema) - "Aidbox.FHIR.Base" - (package->module-name (:package schema)))] + module-name (package->module-name (:package schema))] (format "{ typeof(%s.%s), \"%s\"}" module-name class-name' @@ -304,16 +293,16 @@ (generate-datatypes [_ ir-schemas] [{:path (datatypes-file-path) :content (generate-module - :name "Aidbox.FHIR.Base" + :name base-namespace :deps [] - :classes (map generate-class ir-schemas))}]) + :classes (map #(generate-class % (map generate-class (:backbone-elements %))) ir-schemas))}]) (generate-resource-module [_ ir-schema] {:path (resource-file-path ir-schema) :content (generate-module :name (package->module-name (:package ir-schema)) - :deps [{:module "Aidbox.FHIR.Base" :members []} - {:module "Aidbox.FHIR.Utils" :members []}] + :deps [{:module base-namespace :members []} + {:module utils-namespace :members []}] :classes [(generate-class ir-schema (map generate-class (:backbone-elements ir-schema)))])}) @@ -335,8 +324,13 @@ (generate-constraints [_ constrained-ir-schemas] (map (fn [ir-schema] {:path (resource-file-path ir-schema) - :content (generate-constraint-module - (assoc ir-schema :url (:url ir-schema)))}) + :content (generate-module + :name (package->module-name (:package ir-schema)) + :deps [{:module base-namespace :members []} + {:module utils-namespace :members []}] + :classes (generate-class + ir-schema + (map generate-class (:backbone-elements ir-schema))))}) constrained-ir-schemas)) (generate-sdk-files [_ ir-schemas] diff --git a/src/aidbox_sdk/generator/python.clj b/src/aidbox_sdk/generator/python.clj index 91eafca..332a70a 100644 --- a/src/aidbox_sdk/generator/python.clj +++ b/src/aidbox_sdk/generator/python.clj @@ -229,7 +229,7 @@ {:module "typing" :members ["Optional" "List"]} {:module "dataclasses" :members ["dataclass", "field"]}] :classes - (map generate-class ir-schemas))}]) + (map #(generate-class % (map generate-class (:backbone-elements %))) ir-schemas))}]) (generate-resource-module [_ ir-schema] {:path (resource-file-path ir-schema) diff --git a/test/aidbox_sdk/converter_test.clj b/test/aidbox_sdk/converter_test.clj index bd96557..dfb2d05 100644 --- a/test/aidbox_sdk/converter_test.clj +++ b/test/aidbox_sdk/converter_test.clj @@ -2,9 +2,9 @@ (:require [aidbox-sdk.converter :as sut] [aidbox-sdk.fixtures :as fixt] - [matcho.core :refer [match]] [aidbox-sdk.fixtures.schemas :as fixtures] - [clojure.test :refer [deftest is testing use-fixtures]])) + [clojure.test :refer [deftest is testing use-fixtures]] + [matcho.core :refer [match]])) (use-fixtures :once fixt/prepare-examples) @@ -103,9 +103,7 @@ (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 d5b1565..9fa29da 100644 --- a/test/aidbox_sdk/fixtures/organization_preferred_contact_ir_schema.edn +++ b/test/aidbox_sdk/fixtures/organization_preferred_contact_ir_schema.edn @@ -1,40 +1,43 @@ {:package "hl7.fhir.r4.core", - :derivation "constraint", - :name "Extension", - :type "Extension", - :fhir-version "hl7.fhir.r4.core" + :derivation "constraint", + :name "Extension", + :resource-name "organization-preferred-Contact", + :type "Extension", + :elements + [{:name "url", + :base "Extension", + :array false, + :required false, + :value "string", + :type nil, + :choice-option false} + {:name "value", + :choices + [{:name "valueBoolean", + :base "Extension", + :array false, + :required false, + :value "bool", + :type "boolean", + :choice-option true, + :service-type false}], + :base "Extension", + :array false, + :required false} + {:name "valueBoolean", + :base "Extension", + :array false, + :required false, + :value "bool", + :type "boolean", + :choice-option true, + :service-type false}], :id "organization-preferredContact", :kind "complex-type", - :resource-name "organization-preferred-Contact" - :elements - [{:name "url", - :base "Extension", - :array false, - :required false, - :value "string", - :type nil, - :choice-option false} - {:name "value", - :choices - [{:name "valueBoolean", - :base "Extension", - :array false, - :required false, - :value "bool", - :type "boolean", - :choice-option true}], - :base "Extension", - :array false, - :required false} - {:name "valueBoolean", - :base "Extension", - :array false, - :required false, - :value "bool", - :type "boolean", - :choice-option true}], - :url "http://hl7.org/fhir/StructureDefinition/organization-preferredContact", - :base-resource-name "Extension", - :backbone-elements (), - :base "http://hl7.org/fhir/StructureDefinition/Extension", - :deps #{"Extension" "Meta"}} + :url "http://hl7.org/fhir/StructureDefinition/organization-preferredContact", + :base-resource-name "Extension", + :backbone-elements (), + :base "http://hl7.org/fhir/StructureDefinition/Extension", + :service-type? false, + :fhir-version "hl7.fhir.r4.core", + :deps #{"Meta" "Extension"}} diff --git a/test/aidbox_sdk/fixtures/schemas.clj b/test/aidbox_sdk/fixtures/schemas.clj index ba39c24..4817b09 100644 --- a/test/aidbox_sdk/fixtures/schemas.clj +++ b/test/aidbox_sdk/fixtures/schemas.clj @@ -1442,45 +1442,62 @@ :value "string"}], :name "TestReport_Test"}]) -(def coding-ir-schema {:package "hl7.fhir.r4.core", - :derivation "specialization", - :name "Coding", - :type "Coding", - :elements - [{:name "code", - :base "Coding", - :array false, - :required false, - :value "string" - :type "string"} - {:name "system", - :base "Coding", - :array false, - :required false, - :value "string" - :type "string"} - {:name "display", - :base "Coding", - :array false, - :required false, - :type "string" - :value "string"} - {:name "version", - :base "Coding", - :array false, - :required false, - :type "string" - :value "string"} - {:name "userSelected", - :base "Coding", - :array false, - :required false, - :type "boolean" - :value "bool"}], - :url "http://hl7.org/fhir/StructureDefinition/Coding", - :backbone-elements (), - :base "http://hl7.org/fhir/StructureDefinition/Element" - :base-resource-name "Element"}) +(def coding-ir-schema + {:package "hl7.fhir.r4.core", + :derivation "specialization", + :name "Coding", + :resource-name "Coding", + :type "Coding", + :elements + [{:name "code", + :base "Coding", + :array false, + :required false, + :value "string", + :type "code", + :choice-option false, + :service-type false} + {:name "system", + :base "Coding", + :array false, + :required false, + :value "string", + :type "uri", + :choice-option false, + :service-type false} + {:name "display", + :base "Coding", + :array false, + :required false, + :value "string", + :type "string", + :choice-option false, + :service-type false} + {:name "version", + :base "Coding", + :array false, + :required false, + :value "string", + :type "string", + :choice-option false, + :service-type false} + {:name "userSelected", + :base "Coding", + :array false, + :required false, + :value "bool", + :type "boolean", + :choice-option false, + :service-type false}], + :id "Coding", + :kind "complex-type", + :url "http://hl7.org/fhir/StructureDefinition/Coding", + :base-resource-name "Element", + :backbone-elements [], + :base "http://hl7.org/fhir/StructureDefinition/Element", + :service-type? true, + :fhir-version "hl7.fhir.r4.core", + :deps #{"Element"}}) (def patient-search-params-schemas [{:description diff --git a/test/aidbox_sdk/generator/dotnet_test.clj b/test/aidbox_sdk/generator/dotnet_test.clj index f65ed7c..547a52c 100644 --- a/test/aidbox_sdk/generator/dotnet_test.clj +++ b/test/aidbox_sdk/generator/dotnet_test.clj @@ -30,6 +30,7 @@ (is (= "public Base.Address[]? Address { get; set; }" (gen.dotnet/generate-property {:name "address", :base "Patient", + :service-type true :array true, :required false, :type "Address" @@ -62,67 +63,67 @@ :value "Meta", :type "Meta"}))) - (is (= "public Meta Meta { get; set; }" + (is (= "public Meta? Meta { get; set; }" (gen.dotnet/generate-property {:name "meta" :required false :value "Meta" :type "Meta"})))) - (testing "element with choices" - (is (= (str/join "\n" - ["public object? Value " - " {" - " get" - " {" - " if (ValueReference is not null)" - " {" - " return ValueReference;" - " }" - " " - " if (ValueInteger is not null)" - " {" - " return ValueInteger;" - " }" - " " - " return null;" - " }" - " " - " set" - " {" - " if (value?.GetType() == typeof(Base.ResourceReference))" - " {" - " ValueReference = (Base.ResourceReference)value;" - " return;" - " }" - " " - " if (value?.GetType() == typeof(int))" - " {" - " ValueInteger = (int)value;" - " return;" - " }" - " " - " throw new ArgumentException(\"Invalid type provided\");" - " }" - " }"]) - (gen.dotnet/generate-property {:name "value", - :choices - [{:name "valueReference" - :base "Observation" - :array false - :required false - :value "Base.ResourceReference" - :type "Reference" - :choice-option true} - {:name "valueInteger" - :base "Observation" - :array false - :required false - :value "int" - :type "integer" - :choice-option true}], - :base "Observation", - :array false, - :required false}))))) + #_(testing "element with choices" + (is (= (str/join "\n" + ["public object? Value " + " {" + " get" + " {" + " if (ValueReference is not null)" + " {" + " return ValueReference;" + " }" + " " + " if (ValueInteger is not null)" + " {" + " return ValueInteger;" + " }" + " " + " return null;" + " }" + " " + " set" + " {" + " if (value?.GetType() == typeof(Base.ResourceReference))" + " {" + " ValueReference = (Base.ResourceReference)value;" + " return;" + " }" + " " + " if (value?.GetType() == typeof(int))" + " {" + " ValueInteger = (int)value;" + " return;" + " }" + " " + " throw new ArgumentException(\"Invalid type provided\");" + " }" + " }"]) + (gen.dotnet/generate-property {:name "value", + :choices + [{:name "valueReference" + :base "Observation" + :array false + :required false + :value "Base.ResourceReference" + :type "Reference" + :choice-option true} + {:name "valueInteger" + :base "Observation" + :array false + :required false + :value "int" + :type "integer" + :choice-option true}], + :base "Observation", + :array false, + :required false}))))) #_(deftest test-generate-class @@ -140,7 +141,9 @@ (= (sut/generate-resource-module generator fixtures/patient-ir-schema) {: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\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;\n return;\n }\n \n if (value?.GetType() == typeof(int))\n {\n MultipleBirthInteger = (int)value;\n 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;\n return;\n }\n \n if (value?.GetType() == typeof(bool))\n {\n DeceasedBoolean = (bool)value;\n return;\n }\n \n throw new ArgumentException(\"Invalid type provided\");\n }\n }\n public Base.Attachment[]? Photo { get; set; }\n public PatientLink[]? Link { get; set; }\n public bool? Active { get; set; }\n public PatientCommunication[]? 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 PatientContact[]? Contact { get; set; }\n\n public class PatientLink : BackboneElement\n {\n public required string Type { get; set; }\n public required Base.ResourceReference Other { get; set; }\n }\n\n public class PatientCommunication : BackboneElement\n {\n public required Base.CodeableConcept Language { get; set; }\n public bool? Preferred { get; set; }\n }\n\n public class PatientContact : 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}"}))) + "using Aidbox.FHIR.Base;\nusing Aidbox.FHIR.Utils;\n\nnamespace Aidbox.FHIR.R4.Core;\n\npublic class Patient : DomainResource\n{\n public bool? MultipleBirthBoolean { get; set; }\n public 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 HumanName[]? Name { get; set; }\n public string? BirthDate { get; set; }\n public int? MultipleBirthInteger { get; set; }\n public Attachment[]? Photo { get; set; }\n public BackboneElement[]? Link { get; set; }\n public bool? Active { get; set; }\n public BackboneElement[]? Communication { get; set; }\n public Identifier[]? Identifier { get; set; }\n public ContactPoint[]? Telecom { get; set; }\n public Base.ResourceReference[]? GeneralPractitioner { get; set; }\n public string? Gender { get; set; }\n public CodeableConcept? MaritalStatus { get; set; }\n public BackboneElement[]? Contact { get; set; }\n\n public class PatientLink : BackboneElement\n {\n public required string Type { get; set; }\n public required Base.ResourceReference Other { get; set; }\n }\n\n public class PatientCommunication : BackboneElement\n {\n public required CodeableConcept Language { get; set; }\n public bool? Preferred { get; set; }\n }\n\n public class PatientContact : BackboneElement\n {\n public HumanName? Name { get; set; }\n public string? Gender { get; set; }\n public Period? Period { get; set; }\n public Address? Address { get; set; }\n public ContactPoint[]? Telecom { get; set; }\n public Base.ResourceReference? Organization { get; set; }\n public CodeableConcept[]? Relationship { get; set; }\n }\n}"}))) + +(str/split-lines "using Aidbox.FHIR.Base;\nusing Aidbox.FHIR.Utils;\n\nnamespace Aidbox.FHIR.R4.Core;\n\npublic class Patient : DomainResource\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;\n return;\n }\n \n if (value?.GetType() == typeof(int))\n {\n MultipleBirthInteger = (int)value;\n 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;\n return;\n }\n \n if (value?.GetType() == typeof(bool))\n {\n DeceasedBoolean = (bool)value;\n return;\n }\n \n throw new ArgumentException(\"Invalid type provided\");\n }\n }\n public Base.Attachment[]? Photo { get; set; }\n public PatientLink[]? Link { get; set; }\n public bool? Active { get; set; }\n public PatientCommunication[]? 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 PatientContact[]? Contact { get; set; }\n\n public class PatientLink : BackboneElement\n {\n public required string Type { get; set; }\n public required Base.ResourceReference Other { get; set; }\n }\n\n public class PatientCommunication : BackboneElement\n {\n public required Base.CodeableConcept Language { get; set; }\n public bool? Preferred { get; set; }\n }\n\n public class PatientContact : 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-search-params (is