-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
[#7] Verify dependencies
- Loading branch information
Showing
19 changed files
with
397 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
File renamed without changes.
File renamed without changes.
Binary file not shown.
File renamed without changes.
Binary file not shown.
Binary file not shown.
File renamed without changes.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
(ns aidbox-sdk.schema | ||
(:require [aidbox-sdk.schema.verify :as verify] | ||
[clojure.data.json :as json] | ||
[clojure.java.io :as io] | ||
[clojure.string :as str])) | ||
|
||
(defn get-packages-from-directory | ||
"Returns all packages in the given directory, including files in subdirectories. | ||
NOTE: right now it'll filter out all files which do not contains hl7.fhir | ||
in their name." | ||
[path] | ||
(let [packages (->> path | ||
file-seq | ||
(remove #(.isDirectory %)) | ||
;; FIXME only gzip, but there is no problem to accept unpacked ndjson | ||
;; see https://github.com/Aidbox/aidbox-sdk/issues/11 | ||
(filter #(str/ends-with? (.getName %) ".gz")))] | ||
(println "✅ Found packages:" (count packages)) | ||
packages)) | ||
|
||
(defn create-gzip-reader [path] | ||
(-> path | ||
(io/input-stream) | ||
(java.util.zip.GZIPInputStream.) | ||
(io/reader))) | ||
|
||
;; TODO derive some criteria to determine whether it's a package (is it valid?) | ||
;; see https://github.com/Aidbox/aidbox-sdk/issues/10 | ||
(defn parse-package [path] | ||
(println "Parsing package:" (str path)) | ||
(with-open [rdr (create-gzip-reader path)] | ||
(->> rdr | ||
line-seq | ||
(mapv (fn [line] | ||
(json/read-str line :key-fn keyword)))))) | ||
|
||
(defn remove-invalid-schemas [schemas] | ||
(remove #(nil? (:package-meta %)) schemas)) | ||
|
||
(defn merge-duplicates [schemas] | ||
(->> schemas | ||
(group-by :url) | ||
(map (fn [[_url same-url-schemas]] | ||
(apply merge same-url-schemas))))) | ||
|
||
(defn prepare-schemas [schemas] | ||
(map #(->> (get-in % [:package-meta :name]) | ||
(assoc % :package)) | ||
schemas)) | ||
|
||
|
||
(defmulti retrieve class) | ||
|
||
;; ! According to an example here: | ||
;; ! https://clojuredocs.org/clojure.java.io/file | ||
;; ! it's possible to create a File instance from url, which may lead to bugs | ||
(defmethod retrieve java.io.File | ||
[source] | ||
(println "Retrieving packages from: " (str source)) | ||
(->> (get-packages-from-directory source) | ||
(mapv parse-package) | ||
(verify/check-compatibility!) | ||
(flatten) | ||
(remove-invalid-schemas) | ||
(prepare-schemas) | ||
(merge-duplicates))) | ||
|
||
(defmethod retrieve java.net.URL | ||
[source] | ||
(do "something")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
(ns aidbox-sdk.schema.verify | ||
(:require [clojure.string :as str])) | ||
|
||
(def fhir-version-pattern | ||
#"^(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))?)?$") | ||
|
||
(defn versions-match? [v1 v2] | ||
(let [->groups (fn [v] (->> v (re-matcher fhir-version-pattern) re-find)) | ||
->map (fn [[_ major minor patch label]] | ||
{:major major, :minor minor, :patch patch, :label label}) | ||
|
||
v1-groups (-> v1 ->groups ->map) | ||
v2-groups (-> v2 ->groups ->map)] | ||
|
||
(if (and v1-groups v2-groups) | ||
(and (= (:major v1-groups) (:major v2-groups)) | ||
(= (:minor v1-groups) (:minor v2-groups))) | ||
false))) | ||
|
||
(defn simplify-package-meta [package] | ||
(select-keys package [:name :version])) | ||
|
||
|
||
;; FIXME: is it reliable to use first element of the list? | ||
;; ! seems like it's not for original packages (without Aidbox processing). | ||
(defn extract-meta-from-package | ||
"Extracts meta information from the package." | ||
[package] | ||
(first package)) | ||
|
||
(defn find-core-package | ||
"Finds core package in the list of packages. | ||
Throws an exception if there are more then one core package." | ||
[packages] | ||
(let [cores (filter #(= "fhir.core" (:type %)) packages) | ||
core (first cores)] | ||
(cond | ||
(= (count cores) 0) | ||
(throw (ex-info "No core package found" {})) | ||
|
||
(> (count cores) 1) | ||
(throw (ex-info "Found more then one core package" | ||
{:packages (mapv #(simplify-package-meta %) cores)})) | ||
|
||
:else | ||
core))) | ||
|
||
(defn find-extra-packages | ||
"Finds extra packages in the list of packages. | ||
Throws an exception if there are a few packages with same name." | ||
[packages] | ||
(let [extra (remove #(= "fhir.core" (:type %)) packages) | ||
|
||
duplicates (reduce (fn [duplicates [k v]] | ||
(if (= (count v) 1) | ||
duplicates | ||
(assoc duplicates k v))) | ||
{} (group-by :name extra))] | ||
(cond | ||
(> (count duplicates) 0) | ||
(throw (ex-info "Found more then one package with same name" | ||
{:packages (keys duplicates)})) | ||
|
||
:else | ||
extra)) | ||
|
||
(remove #(= "fhir.core" (:type %)) packages)) | ||
|
||
(defn find-core-package-mismatch | ||
"Finds packages which do not support a core package version." | ||
[version packages] | ||
(->> packages | ||
(mapv (fn [package] | ||
(assoc package :match-with-core? | ||
(if (> (count (:fhirVersions package)) 0) | ||
(->> (:fhirVersions package) | ||
(map #(versions-match? version %)) | ||
(some #{true}) | ||
(boolean)) | ||
true)))) | ||
|
||
(filterv #(not (:match-with-core? %))))) | ||
|
||
(defn- find-failed-dependencies | ||
"Finds failed dependencies for the package looking up in all packages. | ||
In case of failure will return a vec of failed dependencies." | ||
[packages {:keys [dependencies]}] | ||
(reduce (fn [mismatch dependency] | ||
(let [dependency | ||
(if (str/starts-with? dependency ":") | ||
(subs dependency 1) | ||
dependency) | ||
|
||
[dep-name dep-version] | ||
(str/split dependency #"#") | ||
|
||
found | ||
(->> packages | ||
(filterv #(= (:name %) dep-name)) | ||
(mapv #(simplify-package-meta %)))] | ||
|
||
(if (and (= (count found) 1) | ||
(every? #(= (:version %) dep-version) found)) | ||
mismatch | ||
(conj mismatch {:required {:name dep-name | ||
:version dep-version} | ||
:found found})))) | ||
[] dependencies)) | ||
|
||
(defn find-dependencies-mismatch | ||
"Finds packages with failed dependencies check." | ||
[packages] | ||
(reduce (fn [mismatches package] | ||
(let [failed-dependencies (find-failed-dependencies packages package)] | ||
(if-not (empty? failed-dependencies) | ||
(->> failed-dependencies | ||
(assoc package :failed-dependencies) | ||
(conj mismatches)) | ||
mismatches))) | ||
[] packages)) | ||
|
||
(defn check-compatibility! [packages] | ||
(println "Verifying compatibility...") | ||
(let [all (map extract-meta-from-package packages) | ||
core (find-core-package all) | ||
extra (find-extra-packages all)] | ||
(println "✅ Core package found:" (simplify-package-meta core)) | ||
|
||
(println "Checking core version match...") | ||
(let [core-mismatch (find-core-package-mismatch (:version core) extra)] | ||
(when-not (empty? core-mismatch) | ||
(throw (ex-info "Some packages do not match with core version" | ||
{:version (:version core) | ||
:packages (mapv :name core-mismatch)})))) | ||
(println "✅ Core version match check passed") | ||
|
||
(println "Checking dependencies match...") | ||
(let [deps-mismatch (find-dependencies-mismatch all)] | ||
(when-not (empty? deps-mismatch) | ||
(throw (ex-info "Some packages failed dependencies check" | ||
{:packages (mapv #(select-keys % [:name :failed-dependencies]) | ||
deps-mismatch)})))) | ||
(println "✅ Dependencies match check passed")) | ||
packages) |
File renamed without changes.
Oops, something went wrong.