Skip to content

Commit c82a396

Browse files
authored
Merge pull request #9 from Aidbox/worm2fed/#7-verify-deps
[#7] Verify dependencies
2 parents 25ea8d5 + acec24d commit c82a396

19 files changed

+397
-71
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
build-jar:
22
clj -T:build uberjar
33

4-
test:
4+
run-tests:
55
clj -M:test -m kaocha.runner
66

77
PATH_TO_JAR := $(project_dir)/$(jar_path)

deps.edn

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
:aliases
99
{:dev {:extra-paths ["dev"]}
1010

11-
:test {:extra-paths ["tests"]
11+
:test {:extra-paths ["test"]
1212
:exec-fn kaocha.runner/exec-fn
13-
:extra-deps {com.health-samurai/matcho {:mvn/version "0.3.11"}
14-
lambdaisland/kaocha {:mvn/version "1.91.1392"}}}
13+
:extra-deps {com.health-samurai/matcho {:mvn/version "0.3.11"}
14+
lambdaisland/kaocha {:mvn/version "1.91.1392"}
15+
nubank/matcher-combinators {:mvn/version "3.9.1"}}}
1516

1617
:build {:deps {io.github.clojure/tools.build {:mvn/version "0.10.5"}}
1718
:ns-default build}}}

dev/user.clj

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
(ns user
22
(:require [aidbox-sdk.generator :as gen]
3+
[aidbox-sdk.schema.verify :refer [fhir-version-pattern]]
34
[clojure.data]
4-
[clojure.java.io :as io]))
5+
[clojure.java.io :as io]
6+
[clojure.string :as str]))
57

68
(def source' (io/file "resources/schemas"))
79

8-
(def target (io/file "/tmp/sdk"))
10+
(def target (io/file "out/"))
911

1012
(defn vector-to-map [v]
1113
(->> (map (fn [item] (hash-map (:url item) item)) v)
1214
(into {})))
1315

14-
(apply merge [{:a 1 :b 2} {:a 3 :c 4}])
16+
;; (apply merge [{:a 1 :b 2} {:a 3 :c 4}])
1517

1618
(comment
1719

@@ -71,5 +73,4 @@
7173

7274
(gen/build-all! source' target)
7375

74-
;;
75-
)
76+
:rcf)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
9.2 MB
Binary file not shown.

src/aidbox_sdk/core.clj

+5-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
(println "Error: please provide an output argument"))
3131

3232
:else
33-
(generator/build-all!
34-
(resource input)
35-
(io/as-file output)))))
33+
(do
34+
(println "Building FHIR SDK...")
35+
(generator/build-all!
36+
(resource input)
37+
(io/as-file output))))))

src/aidbox_sdk/generator.clj

+13-58
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
(ns aidbox-sdk.generator
22
(:refer-clojure :exclude [namespace])
3-
(:require
4-
[clojure.data.json :as json]
5-
[aidbox-sdk.generator.dotnet.templates :as dotnettpl]
6-
[aidbox-sdk.generator.helpers :refer [->pascal-case safe-conj
7-
uppercase-first-letter vector-to-map]]
8-
[clojure.java.io :as io]
9-
[clojure.set :as set]
10-
[clojure.string :as str]
11-
[clojure.walk])
12-
(:import
13-
[java.util.zip ZipEntry ZipOutputStream]))
3+
(:require [aidbox-sdk.generator.dotnet.templates :as dotnettpl]
4+
[aidbox-sdk.generator.helpers :refer [->pascal-case safe-conj
5+
uppercase-first-letter
6+
vector-to-map]]
7+
[aidbox-sdk.schema :as schema]
8+
[clojure.java.io :as io]
9+
[clojure.set :as set]
10+
[clojure.string :as str]
11+
[clojure.walk])
12+
(:import [java.util.zip ZipEntry ZipOutputStream]))
1413

1514
;;
1615
;; FHIR
@@ -646,6 +645,7 @@
646645
(delete-directory! dir)
647646
(create-directory! dir))
648647

648+
;; FIXME do we need it?
649649
(defn zip-dir! [path zip-name]
650650
(with-open [zip (ZipOutputStream. (io/output-stream zip-name))]
651651
(doseq [f (file-seq (io/file path)) :when (.isFile f)]
@@ -654,6 +654,7 @@
654654
(.closeEntry zip)))
655655
(io/file zip-name))
656656

657+
;; FIXME do we need it?
657658
(defn copy-files! [src-dir target-dir]
658659
(doseq [file (remove #(.isDirectory %) (file-seq src-dir))]
659660
(io/copy file (io/file target-dir (.getName file)))))
@@ -691,61 +692,15 @@
691692
(conj schema {:backbone-elements
692693
(flat-backbones (:backbone-elements schema) [])})))))
693694

694-
(defn get-directory-files [path]
695-
(->> path
696-
file-seq
697-
(remove #(.isDirectory %))))
698-
699-
(defn fetch-packages [source-path]
700-
(->> source-path
701-
(get-directory-files)
702-
(remove #(.isDirectory %))
703-
(filter #(str/includes? (.getName %) "hl7.fhir"))))
704-
705-
(defn create-gzip-reader [path]
706-
(-> path
707-
(io/input-stream)
708-
(java.util.zip.GZIPInputStream.)
709-
(io/reader)))
710-
711-
(defn parse-ndjson-gz [path]
712-
(with-open [rdr (create-gzip-reader path)]
713-
(->> rdr
714-
line-seq
715-
(mapv (fn [line]
716-
(json/read-str line :key-fn keyword))))))
717-
718-
(defn merge-duplicates [schemas]
719-
(->> schemas
720-
(group-by :url)
721-
(map (fn [[_url same-url-schemas]]
722-
(apply merge same-url-schemas)))))
723-
724-
(defmulti retrieve-schemas class)
725-
726-
(defmethod retrieve-schemas java.io.File
727-
[source]
728-
(->> (fetch-packages source)
729-
(map parse-ndjson-gz)
730-
(flatten)
731-
(remove #(nil? (:package-meta %)))
732-
(map (fn [schema]
733-
(assoc schema :package (get-in schema [:package-meta :name]))))
734-
(merge-duplicates)))
735-
736-
(defmethod retrieve-schemas java.net.URL
737-
[source] (do "something"))
738-
739695
(defn build-all! [input output]
740696
(let [search-parameters-dir (io/file output "search")
741-
all-schemas (retrieve-schemas input)
697+
all-schemas (schema/retrieve input)
742698
;; search-params-schemas (retrieve-search-params source-dir)
743699
search-params-schemas all-schemas
744700
constraints (->> all-schemas
745701
(filter #(and
746702
(constraint? %)
747703
(not (from-extension? %)))))]
748-
749704
(prepare-target-directory! output)
750705

751706
;; create base namespace (all FHIR datatypes) file

src/aidbox_sdk/schema.clj

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
(ns aidbox-sdk.schema
2+
(:require [aidbox-sdk.schema.verify :as verify]
3+
[clojure.data.json :as json]
4+
[clojure.java.io :as io]
5+
[clojure.string :as str]))
6+
7+
(defn get-packages-from-directory
8+
"Returns all packages in the given directory, including files in subdirectories.
9+
NOTE: right now it'll filter out all files which do not contains hl7.fhir
10+
in their name."
11+
[path]
12+
(let [packages (->> path
13+
file-seq
14+
(remove #(.isDirectory %))
15+
;; FIXME only gzip, but there is no problem to accept unpacked ndjson
16+
;; see https://github.com/Aidbox/aidbox-sdk/issues/11
17+
(filter #(str/ends-with? (.getName %) ".gz")))]
18+
(println "✅ Found packages:" (count packages))
19+
packages))
20+
21+
(defn create-gzip-reader [path]
22+
(-> path
23+
(io/input-stream)
24+
(java.util.zip.GZIPInputStream.)
25+
(io/reader)))
26+
27+
;; TODO derive some criteria to determine whether it's a package (is it valid?)
28+
;; see https://github.com/Aidbox/aidbox-sdk/issues/10
29+
(defn parse-package [path]
30+
(println "Parsing package:" (str path))
31+
(with-open [rdr (create-gzip-reader path)]
32+
(->> rdr
33+
line-seq
34+
(mapv (fn [line]
35+
(json/read-str line :key-fn keyword))))))
36+
37+
(defn remove-invalid-schemas [schemas]
38+
(remove #(nil? (:package-meta %)) schemas))
39+
40+
(defn merge-duplicates [schemas]
41+
(->> schemas
42+
(group-by :url)
43+
(map (fn [[_url same-url-schemas]]
44+
(apply merge same-url-schemas)))))
45+
46+
(defn prepare-schemas [schemas]
47+
(map #(->> (get-in % [:package-meta :name])
48+
(assoc % :package))
49+
schemas))
50+
51+
52+
(defmulti retrieve class)
53+
54+
;; ! According to an example here:
55+
;; ! https://clojuredocs.org/clojure.java.io/file
56+
;; ! it's possible to create a File instance from url, which may lead to bugs
57+
(defmethod retrieve java.io.File
58+
[source]
59+
(println "Retrieving packages from: " (str source))
60+
(->> (get-packages-from-directory source)
61+
(mapv parse-package)
62+
(verify/check-compatibility!)
63+
(flatten)
64+
(remove-invalid-schemas)
65+
(prepare-schemas)
66+
(merge-duplicates)))
67+
68+
(defmethod retrieve java.net.URL
69+
[source]
70+
(do "something"))

src/aidbox_sdk/schema/verify.clj

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
(ns aidbox-sdk.schema.verify
2+
(:require [clojure.string :as str]))
3+
4+
(def fhir-version-pattern
5+
#"^(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))?)?$")
6+
7+
(defn versions-match? [v1 v2]
8+
(let [->groups (fn [v] (->> v (re-matcher fhir-version-pattern) re-find))
9+
->map (fn [[_ major minor patch label]]
10+
{:major major, :minor minor, :patch patch, :label label})
11+
12+
v1-groups (-> v1 ->groups ->map)
13+
v2-groups (-> v2 ->groups ->map)]
14+
15+
(if (and v1-groups v2-groups)
16+
(and (= (:major v1-groups) (:major v2-groups))
17+
(= (:minor v1-groups) (:minor v2-groups)))
18+
false)))
19+
20+
(defn simplify-package-meta [package]
21+
(select-keys package [:name :version]))
22+
23+
24+
;; FIXME: is it reliable to use first element of the list?
25+
;; ! seems like it's not for original packages (without Aidbox processing).
26+
(defn extract-meta-from-package
27+
"Extracts meta information from the package."
28+
[package]
29+
(first package))
30+
31+
(defn find-core-package
32+
"Finds core package in the list of packages.
33+
Throws an exception if there are more then one core package."
34+
[packages]
35+
(let [cores (filter #(= "fhir.core" (:type %)) packages)
36+
core (first cores)]
37+
(cond
38+
(= (count cores) 0)
39+
(throw (ex-info "No core package found" {}))
40+
41+
(> (count cores) 1)
42+
(throw (ex-info "Found more then one core package"
43+
{:packages (mapv #(simplify-package-meta %) cores)}))
44+
45+
:else
46+
core)))
47+
48+
(defn find-extra-packages
49+
"Finds extra packages in the list of packages.
50+
Throws an exception if there are a few packages with same name."
51+
[packages]
52+
(let [extra (remove #(= "fhir.core" (:type %)) packages)
53+
54+
duplicates (reduce (fn [duplicates [k v]]
55+
(if (= (count v) 1)
56+
duplicates
57+
(assoc duplicates k v)))
58+
{} (group-by :name extra))]
59+
(cond
60+
(> (count duplicates) 0)
61+
(throw (ex-info "Found more then one package with same name"
62+
{:packages (keys duplicates)}))
63+
64+
:else
65+
extra))
66+
67+
(remove #(= "fhir.core" (:type %)) packages))
68+
69+
(defn find-core-package-mismatch
70+
"Finds packages which do not support a core package version."
71+
[version packages]
72+
(->> packages
73+
(mapv (fn [package]
74+
(assoc package :match-with-core?
75+
(if (> (count (:fhirVersions package)) 0)
76+
(->> (:fhirVersions package)
77+
(map #(versions-match? version %))
78+
(some #{true})
79+
(boolean))
80+
true))))
81+
82+
(filterv #(not (:match-with-core? %)))))
83+
84+
(defn- find-failed-dependencies
85+
"Finds failed dependencies for the package looking up in all packages.
86+
In case of failure will return a vec of failed dependencies."
87+
[packages {:keys [dependencies]}]
88+
(reduce (fn [mismatch dependency]
89+
(let [dependency
90+
(if (str/starts-with? dependency ":")
91+
(subs dependency 1)
92+
dependency)
93+
94+
[dep-name dep-version]
95+
(str/split dependency #"#")
96+
97+
found
98+
(->> packages
99+
(filterv #(= (:name %) dep-name))
100+
(mapv #(simplify-package-meta %)))]
101+
102+
(if (and (= (count found) 1)
103+
(every? #(= (:version %) dep-version) found))
104+
mismatch
105+
(conj mismatch {:required {:name dep-name
106+
:version dep-version}
107+
:found found}))))
108+
[] dependencies))
109+
110+
(defn find-dependencies-mismatch
111+
"Finds packages with failed dependencies check."
112+
[packages]
113+
(reduce (fn [mismatches package]
114+
(let [failed-dependencies (find-failed-dependencies packages package)]
115+
(if-not (empty? failed-dependencies)
116+
(->> failed-dependencies
117+
(assoc package :failed-dependencies)
118+
(conj mismatches))
119+
mismatches)))
120+
[] packages))
121+
122+
(defn check-compatibility! [packages]
123+
(println "Verifying compatibility...")
124+
(let [all (map extract-meta-from-package packages)
125+
core (find-core-package all)
126+
extra (find-extra-packages all)]
127+
(println "✅ Core package found:" (simplify-package-meta core))
128+
129+
(println "Checking core version match...")
130+
(let [core-mismatch (find-core-package-mismatch (:version core) extra)]
131+
(when-not (empty? core-mismatch)
132+
(throw (ex-info "Some packages do not match with core version"
133+
{:version (:version core)
134+
:packages (mapv :name core-mismatch)}))))
135+
(println "✅ Core version match check passed")
136+
137+
(println "Checking dependencies match...")
138+
(let [deps-mismatch (find-dependencies-mismatch all)]
139+
(when-not (empty? deps-mismatch)
140+
(throw (ex-info "Some packages failed dependencies check"
141+
{:packages (mapv #(select-keys % [:name :failed-dependencies])
142+
deps-mismatch)}))))
143+
(println "✅ Dependencies match check passed"))
144+
packages)
File renamed without changes.

0 commit comments

Comments
 (0)