From 0531d8c1d312b010a066a67b9b54c1f298e1f640 Mon Sep 17 00:00:00 2001 From: Joshua Miller Date: Wed, 27 Jul 2016 11:43:44 -0700 Subject: [PATCH 01/11] Add included data in get-many. --- src/argo/core.clj | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/argo/core.clj b/src/argo/core.clj index e08ba0b..000cfc6 100644 --- a/src/argo/core.clj +++ b/src/argo/core.clj @@ -34,10 +34,10 @@ (def base-url "") (defn ok - [data & {:keys [status headers links meta]}] + [data & {:keys [status headers links meta included]}] {:status (or status 200) :headers (merge {"Content-Type" "application/vnd.api+json"} headers) - :body (merge {:data data} (when links {:links links}) (when meta {:meta meta}))}) + :body (merge {:data data} (when links {:links links}) (when meta {:meta meta}) (when included {:included included}))}) (defn flatten-errors ([errors] @@ -92,15 +92,21 @@ (merge (when (> offset 0) {:first (gen-qs uri params-encoded 0 limit)})))))) +(comment (dissoc (apply dissoc x (map (fn [[k v]] (:foreign-key v)) rels)) primary-key)) + (defn x-to-api [type x primary-key & [rels]] (when x (merge {:type type :id (str (get x primary-key)) - :attributes (dissoc (apply dissoc x (map (fn [[k v]] (:foreign-key v)) rels)) primary-key) + :attributes (-> x + (dissoc (map (fn [[k v]] (:foreign-key v)) rels)) + (dissoc primary-key) + (dissoc :included)) :links {:self (str base-url "/" type "/" (get x primary-key))}} (when rels {:relationships (apply merge (map (fn [[k v]] - {k {:links {:related (str base-url "/" type "/" (get x primary-key) "/" (name k))}}}) + {k {:links {:related (str base-url "/" type "/" (get x primary-key) "/" (name k))} + :data (get-in x [:included k])}}) rels))})))) (defn wrap-pagination @@ -217,12 +223,13 @@ exclude-source# :exclude-source status# :status total# :count - m# :meta} (~get-many ~req) + m# :meta + included# :included} (~get-many ~req) pag# (assoc (:page ~req) :count total#) links# (gen-pagination-links ~req pag#)] (if errors# (bad-req errors# :status status# :exclude-source exclude-source#) - (ok (map (fn [x#] (x-to-api ~typ x# ~primary-key ~rels)) data#) :links links# :meta m#))))) + (ok (map (fn [x#] (x-to-api ~typ x# ~primary-key ~rels)) data#) :links links# :meta m# :included included#))))) ~@(when create `(:post (let [{data# :data From 3ed8b97efe7ee6111e2bacbb52d6f2f16f386813 Mon Sep 17 00:00:00 2001 From: Joshua Miller Date: Tue, 2 Aug 2016 10:58:33 -0700 Subject: [PATCH 02/11] Add included data in get-one. --- src/argo/core.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/argo/core.clj b/src/argo/core.clj index 000cfc6..53223d8 100644 --- a/src/argo/core.clj +++ b/src/argo/core.clj @@ -252,11 +252,12 @@ status# :status exclude-source# :exclude-source errors# :errors - m# :meta} (~get-one ~req)] + m# :meta + included# :included} (~get-one ~req)] (cond errors# (bad-req errors# :status status# :exclude-source exclude-source#) (nil? data#) (not-found) - :else (ok (x-to-api ~typ data# ~primary-key ~rels) :meta m#))))) + :else (ok (x-to-api ~typ data# ~primary-key ~rels) :meta m# :included included#))))) ~@(when update `(:patch (let [{data# :data From 73597833b138951cbba94e8fb8d50db287cf5d75 Mon Sep 17 00:00:00 2001 From: Joshua Miller Date: Wed, 17 Aug 2016 13:58:41 -0700 Subject: [PATCH 03/11] Properly handle included data. --- src/argo/core.clj | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/argo/core.clj b/src/argo/core.clj index 53223d8..fcee417 100644 --- a/src/argo/core.clj +++ b/src/argo/core.clj @@ -104,10 +104,14 @@ (dissoc primary-key) (dissoc :included)) :links {:self (str base-url "/" type "/" (get x primary-key))}} - (when rels {:relationships (apply merge (map (fn [[k v]] - {k {:links {:related (str base-url "/" type "/" (get x primary-key) "/" (name k))} - :data (get-in x [:included k])}}) - rels))})))) + (when rels {:relationships + (apply merge + (map (fn [[k v]] + {k (merge {:links + {:related (str base-url "/" type "/" (get x primary-key) "/" (name k))}} + (when-let [included (get-in x [:included k])] + {:data included}))}) + rels))})))) (defn wrap-pagination [default-limit max-limit] From 87e57225135bb67e80a8d26a6304215f4e78599e Mon Sep 17 00:00:00 2001 From: Joshua Miller Date: Wed, 31 Aug 2016 13:22:59 -0700 Subject: [PATCH 04/11] Return full 201 response for rel create. --- src/argo/core.clj | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/argo/core.clj b/src/argo/core.clj index fcee417..a7655d9 100644 --- a/src/argo/core.clj +++ b/src/argo/core.clj @@ -317,7 +317,14 @@ :meta ~m)) `((ok (x-to-api ~typ ~data ~primary-key ~relations) :meta ~m)))))))) ~@(when create - `(:post (rel-req ~create ~req))) + `(:post (let [{data# :data + errors# :errors + exclude-source# :exclude-source + status# :status + m# :meta} (~create ~req)] + (if errors# + (bad-req errors# :status status# :exclude-source exclude-source#) + (ok (x-to-api ~typ data# ~primary-key ~rels) :status 201 :meta m#))))) ~@(when update `(:patch (rel-req ~update ~req))) From 7568f7d6bb616549af5851bdeb4fb0758be43465 Mon Sep 17 00:00:00 2001 From: Christopher Plummer Date: Wed, 25 Jan 2017 11:03:33 -0500 Subject: [PATCH 05/11] added support for resource objects in relationships --- src/argo/core.clj | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/argo/core.clj b/src/argo/core.clj index a7655d9..1c31748 100644 --- a/src/argo/core.clj +++ b/src/argo/core.clj @@ -94,6 +94,17 @@ (comment (dissoc (apply dissoc x (map (fn [[k v]] (:foreign-key v)) rels)) primary-key)) +(defn build-relationships + "takes object relationship map, primary key and returns json-api formatted map for relationships" + [type x primary-key rels] + (apply merge + (map (fn [[k v]] + {k (merge {:links + {:related (str base-url "/" type "/" (get x primary-key) "/" (name k))}} + (when-let [data (k (:resource-objects x))] + {:data data}))}) + rels))) + (defn x-to-api [type x primary-key & [rels]] (when x @@ -102,16 +113,9 @@ :attributes (-> x (dissoc (map (fn [[k v]] (:foreign-key v)) rels)) (dissoc primary-key) - (dissoc :included)) + (dissoc :resource-objects)) :links {:self (str base-url "/" type "/" (get x primary-key))}} - (when rels {:relationships - (apply merge - (map (fn [[k v]] - {k (merge {:links - {:related (str base-url "/" type "/" (get x primary-key) "/" (name k))}} - (when-let [included (get-in x [:included k])] - {:data included}))}) - rels))})))) + (when rels {:relationships (build-relationships type x primary-key rels)})))) (defn wrap-pagination [default-limit max-limit] From 3c0a4bb9c0eaf9f8e533f4db8b23b301e7038306 Mon Sep 17 00:00:00 2001 From: Christopher Plummer Date: Mon, 30 Jan 2017 19:47:30 -0500 Subject: [PATCH 06/11] Added some documentation. --- README.md | 7 +++++-- src/argo/core.clj | 14 +++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5554f9c..f1c0b5e 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ Argo expects as the result of these functions a map with the following keys: * `:status`: Use to override the status of responses. Defaults to 400 for error responses and 200 for valid responses. * `:exclude-source`: Use this to exclude the source object as per the JSON API spec in error responses. * `:count`: argo provides automatic generation of pagination links if using pagination for `:find`. Use `:count` to let argo know how many total objects exist when implementing pagination. +* `:included` You may optionally include top-level, related resource objects. +* `:resource-objects` You may optionally include related resource identifier objects. In most circumstances it will probably only be necessary to include either `:data` or `:errors`. @@ -266,8 +268,9 @@ This should return the following reponse. "hero": { "links": { "related": "/v1/achievements/1/hero" - } - } + }, + "data": { "type": "heroes", "id": 1 } + }, }, "type": "achievements" } diff --git a/src/argo/core.clj b/src/argo/core.clj index 1c31748..81c6dfc 100644 --- a/src/argo/core.clj +++ b/src/argo/core.clj @@ -95,27 +95,27 @@ (comment (dissoc (apply dissoc x (map (fn [[k v]] (:foreign-key v)) rels)) primary-key)) (defn build-relationships - "takes object relationship map, primary key and returns json-api formatted map for relationships" - [type x primary-key rels] + "takes , primary key and returns json-api formatted map for relationships" + [rel-type x primary-key rels] (apply merge (map (fn [[k v]] {k (merge {:links - {:related (str base-url "/" type "/" (get x primary-key) "/" (name k))}} + {:related (str base-url "/" rel-type "/" (get x primary-key) "/" (name k))}} (when-let [data (k (:resource-objects x))] {:data data}))}) rels))) (defn x-to-api - [type x primary-key & [rels]] + [rel-type x primary-key & [rels]] (when x - (merge {:type type + (merge {:type rel-type :id (str (get x primary-key)) :attributes (-> x (dissoc (map (fn [[k v]] (:foreign-key v)) rels)) (dissoc primary-key) (dissoc :resource-objects)) - :links {:self (str base-url "/" type "/" (get x primary-key))}} - (when rels {:relationships (build-relationships type x primary-key rels)})))) + :links {:self (str base-url "/" rel-type "/" (get x primary-key))}} + (when rels {:relationships (build-relationships rel-type x primary-key rels)})))) (defn wrap-pagination [default-limit max-limit] From aa295aaa499d076c966ff1e20129a26b219e84c4 Mon Sep 17 00:00:00 2001 From: Christopher Plummer Date: Sat, 11 Feb 2017 14:59:29 -0500 Subject: [PATCH 07/11] changed :resource-objects to :resource-identifiers --- README.md | 4 ++-- src/argo/core.clj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f1c0b5e..1053018 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ Argo expects as the result of these functions a map with the following keys: * `:status`: Use to override the status of responses. Defaults to 400 for error responses and 200 for valid responses. * `:exclude-source`: Use this to exclude the source object as per the JSON API spec in error responses. * `:count`: argo provides automatic generation of pagination links if using pagination for `:find`. Use `:count` to let argo know how many total objects exist when implementing pagination. -* `:included` You may optionally include top-level, related resource objects. -* `:resource-objects` You may optionally include related resource identifier objects. +* `:included` You may _optionally_ include top-level, related resource objects. +* `:resource-identifiers` You may _optionally_ include related resource identifier objects. In most circumstances it will probably only be necessary to include either `:data` or `:errors`. diff --git a/src/argo/core.clj b/src/argo/core.clj index 81c6dfc..d07db43 100644 --- a/src/argo/core.clj +++ b/src/argo/core.clj @@ -101,7 +101,7 @@ (map (fn [[k v]] {k (merge {:links {:related (str base-url "/" rel-type "/" (get x primary-key) "/" (name k))}} - (when-let [data (k (:resource-objects x))] + (when-let [data (k (:resource-identifiers x))] {:data data}))}) rels))) @@ -113,7 +113,7 @@ :attributes (-> x (dissoc (map (fn [[k v]] (:foreign-key v)) rels)) (dissoc primary-key) - (dissoc :resource-objects)) + (dissoc :resource-identifiers)) :links {:self (str base-url "/" rel-type "/" (get x primary-key))}} (when rels {:relationships (build-relationships rel-type x primary-key rels)})))) From 19e5462bb80e38e71e3c9760ce29a66cce5d036c Mon Sep 17 00:00:00 2001 From: Christopher Plummer Date: Sat, 11 Feb 2017 19:02:07 -0500 Subject: [PATCH 08/11] Fixed support for included resource objects. --- README.md | 10 +++++++++- src/argo/core.clj | 50 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1053018..548f536 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,20 @@ Each operation expects its implementation to be a function with a ring request o Argo expects as the result of these functions a map with the following keys: * `:data`: The data which will be returned in the API response. This should be a map for a single resource (as implemented with `:get`) or a vector/sequence for `:find`. + - Optional data: `:resource-identifiers` You may _optionally_ include related resource identifier objects. * `:errors`: A map of keywords and string values. Will be converted to the JSON API error format. Works well with Prismatic schema error types. * `:status`: Use to override the status of responses. Defaults to 400 for error responses and 200 for valid responses. * `:exclude-source`: Use this to exclude the source object as per the JSON API spec in error responses. * `:count`: argo provides automatic generation of pagination links if using pagination for `:find`. Use `:count` to let argo know how many total objects exist when implementing pagination. * `:included` You may _optionally_ include top-level, related resource objects. -* `:resource-identifiers` You may _optionally_ include related resource identifier objects. + - for example: + ```clojure + { + :data {...} + :included {:heroes [{:id "12345" + :name "Jason"}]} + } + ``` In most circumstances it will probably only be necessary to include either `:data` or `:errors`. diff --git a/src/argo/core.clj b/src/argo/core.clj index d07db43..8eb0294 100644 --- a/src/argo/core.clj +++ b/src/argo/core.clj @@ -37,7 +37,11 @@ [data & {:keys [status headers links meta included]}] {:status (or status 200) :headers (merge {"Content-Type" "application/vnd.api+json"} headers) - :body (merge {:data data} (when links {:links links}) (when meta {:meta meta}) (when included {:included included}))}) + :body (merge + {:data data} + (when links {:links links}) + (when meta {:meta meta}) + (when included {:included included}))}) (defn flatten-errors ([errors] @@ -95,27 +99,40 @@ (comment (dissoc (apply dissoc x (map (fn [[k v]] (:foreign-key v)) rels)) primary-key)) (defn build-relationships - "takes , primary key and returns json-api formatted map for relationships" + "builds json-api formatted map for relationships, + including resource identifier objecs if in x" [rel-type x primary-key rels] (apply merge - (map (fn [[k v]] - {k (merge {:links - {:related (str base-url "/" rel-type "/" (get x primary-key) "/" (name k))}} - (when-let [data (k (:resource-identifiers x))] - {:data data}))}) + (map (fn [[rel-key v]] + {rel-key (merge + {:links {:related (str base-url "/" rel-type "/" (get x primary-key) "/" (name rel-key))}} + (when-let [data (rel-key (:resource-identifiers x))] + {:data data}))}) rels))) +(defn build-included + "builds collection of included records from collection of included record maps" + [included] + (when (not-empty included) + (map (fn [[typ included-of-type]] + (map (fn [include] + (x-to-api (name typ) include :id)) + included-of-type)) + included))) + (defn x-to-api - [rel-type x primary-key & [rels]] + [typ x primary-key & [rels]] (when x - (merge {:type rel-type + (merge {:type typ :id (str (get x primary-key)) :attributes (-> x (dissoc (map (fn [[k v]] (:foreign-key v)) rels)) (dissoc primary-key) + (dissoc :included) (dissoc :resource-identifiers)) - :links {:self (str base-url "/" rel-type "/" (get x primary-key))}} - (when rels {:relationships (build-relationships rel-type x primary-key rels)})))) + :links {:self (str base-url "/" typ "/" (get x primary-key))}} + (when rels + {:relationships (build-relationships typ x primary-key rels)})))) (defn wrap-pagination [default-limit max-limit] @@ -235,9 +252,11 @@ included# :included} (~get-many ~req) pag# (assoc (:page ~req) :count total#) links# (gen-pagination-links ~req pag#)] - (if errors# - (bad-req errors# :status status# :exclude-source exclude-source#) - (ok (map (fn [x#] (x-to-api ~typ x# ~primary-key ~rels)) data#) :links links# :meta m# :included included#))))) + (cond + errors# (bad-req errors# :status status# :exclude-source exclude-source#) + (:include (:params ~req)) (bad-req {:?include "include resources not supported"} :status 400 :exclude-source exclude-source#) + (nil? data#) (not-found) + :else (ok (map (fn [x#] (x-to-api ~typ x# ~primary-key ~rels)) data#) :links links# :meta m# :included (build-included included#)))))) ~@(when create `(:post (let [{data# :data @@ -264,8 +283,9 @@ included# :included} (~get-one ~req)] (cond errors# (bad-req errors# :status status# :exclude-source exclude-source#) + (:include (:params ~req)) (bad-req {:?include "include resources not supported"} :status 400 :exclude-source exclude-source#) (nil? data#) (not-found) - :else (ok (x-to-api ~typ data# ~primary-key ~rels) :meta m# :included included#))))) + :else (ok (x-to-api ~typ data# ~primary-key ~rels) :meta m# :included (build-included included#)))))) ~@(when update `(:patch (let [{data# :data From e28fddb2808047078aa1b6fa822de2358b3fdf82 Mon Sep 17 00:00:00 2001 From: Christopher Plummer Date: Sun, 12 Feb 2017 12:37:41 -0500 Subject: [PATCH 09/11] updated readme and example to show included resources --- README.md | 19 ++++++++++++++++--- example/src/example/api.clj | 3 ++- src/argo/core.clj | 22 +++++++++++----------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 548f536..53614a7 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,8 @@ Argo expects as the result of these functions a map with the following keys: ```clojure { :data {...} - :included {:heroes [{:id "12345" - :name "Jason"}]} + :included {:heroes [{:id 1 :name "Jason"}] + :ally {:id 2 :name "Medea"}} } ``` @@ -281,7 +281,20 @@ This should return the following reponse. }, }, "type": "achievements" - } + }, + "included": [ + { + "type": "heroes", + "id": "1", + "attributes": { + "created": "2017-02-12T00:09:59Z", + "name": "Jason" + }, + "links": { + "self": "/v1/heroes/1" + } + } + ] } ``` diff --git a/example/src/example/api.clj b/example/src/example/api.clj index bc82293..4337e3c 100644 --- a/example/src/example/api.clj +++ b/example/src/example/api.clj @@ -48,7 +48,8 @@ {:data (db/find-achievements)}) :get (fn [req] - {:data (db/get-achievement (parse-id (-> req :params :id)))}) + {:data (db/get-achievement (parse-id (-> req :params :id))) + :included {:heroes (db/get-achievement-hero (parse-id (-> req :params :id)))}}) :create (fn [req] (if-let [errors (s/check NewAchievement (:body req))] diff --git a/src/argo/core.clj b/src/argo/core.clj index 8eb0294..5d5f47e 100644 --- a/src/argo/core.clj +++ b/src/argo/core.clj @@ -110,16 +110,6 @@ {:data data}))}) rels))) -(defn build-included - "builds collection of included records from collection of included record maps" - [included] - (when (not-empty included) - (map (fn [[typ included-of-type]] - (map (fn [include] - (x-to-api (name typ) include :id)) - included-of-type)) - included))) - (defn x-to-api [typ x primary-key & [rels]] (when x @@ -128,12 +118,22 @@ :attributes (-> x (dissoc (map (fn [[k v]] (:foreign-key v)) rels)) (dissoc primary-key) - (dissoc :included) (dissoc :resource-identifiers)) :links {:self (str base-url "/" typ "/" (get x primary-key))}} (when rels {:relationships (build-relationships typ x primary-key rels)})))) +(defn build-included + "builds collection of resources to include in a response" + [included] + (when (not-empty included) + (->> included + (map (fn [[typ included-of-type]] + (if (seq? included-of-type) + (map (fn [include] (x-to-api (name typ) include :id)) included-of-type) + [(x-to-api (name typ) included-of-type :id)]))) + (reduce (fn [acc curr] (into acc curr)))))) + (defn wrap-pagination [default-limit max-limit] (fn [handler] From afa52c9fa949285ef38f42e2556bb240080fdff0 Mon Sep 17 00:00:00 2001 From: Christopher Plummer Date: Fri, 17 Feb 2017 17:07:22 -0500 Subject: [PATCH 10/11] fix bug with including vectors. --- src/argo/core.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/argo/core.clj b/src/argo/core.clj index 5d5f47e..5fc11ea 100644 --- a/src/argo/core.clj +++ b/src/argo/core.clj @@ -129,7 +129,7 @@ (when (not-empty included) (->> included (map (fn [[typ included-of-type]] - (if (seq? included-of-type) + (if (coll? included-of-type) (map (fn [include] (x-to-api (name typ) include :id)) included-of-type) [(x-to-api (name typ) included-of-type :id)]))) (reduce (fn [acc curr] (into acc curr)))))) From cac7094836d89a8e0c4c98e152506fe382cd8f50 Mon Sep 17 00:00:00 2001 From: Christopher Plummer Date: Fri, 24 Feb 2017 16:31:48 -0500 Subject: [PATCH 11/11] Fixed error building included resources. --- example/src/example/api.clj | 2 +- example/src/example/db.clj | 2 +- src/argo/core.clj | 18 +++++++++++------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/example/src/example/api.clj b/example/src/example/api.clj index 4337e3c..3972ec3 100644 --- a/example/src/example/api.clj +++ b/example/src/example/api.clj @@ -64,7 +64,7 @@ :rels {:hero {:type :heroes :foreign-key :hero :get (fn [req] - {:data (db/get-achievement-hero (parse-id (-> req :params :id)))})}}}) + {:data (db/get-achievement-hero (parse-id (-> req :params :id)))})}}}) (defapi api {:base-url "/v1" diff --git a/example/src/example/db.clj b/example/src/example/db.clj index faa451a..b3ac119 100644 --- a/example/src/example/db.clj +++ b/example/src/example/db.clj @@ -79,7 +79,7 @@ (defn get-achievement-hero [id] (when (integer? id) - (let [q (str "SELECT heroes.id AS id, heroes.name AS name, heroes.created AS created " + (let [q (str "SELECT heroes.id AS id, heroes.name AS name, heroes.created AS created, heroes.birthplace AS birthplace " "FROM heroes, achievements " "WHERE achievements.id = ? AND heroes.id = achievements.id")] (first (jdbc/query db [q id]))))) diff --git a/src/argo/core.clj b/src/argo/core.clj index 5fc11ea..5b58ee6 100644 --- a/src/argo/core.clj +++ b/src/argo/core.clj @@ -110,15 +110,18 @@ {:data data}))}) rels))) +(defn remove-unused-keys + [x primary-key rels] + (let [rel-keys (map (fn [[k v]] (:foreign-key v)) rels) + keys-to-remove (concat rel-keys [:resource-identifiers primary-key])] + (apply dissoc x keys-to-remove))) + (defn x-to-api [typ x primary-key & [rels]] (when x (merge {:type typ :id (str (get x primary-key)) - :attributes (-> x - (dissoc (map (fn [[k v]] (:foreign-key v)) rels)) - (dissoc primary-key) - (dissoc :resource-identifiers)) + :attributes (remove-unused-keys x primary-key rels) :links {:self (str base-url "/" typ "/" (get x primary-key))}} (when rels {:relationships (build-relationships typ x primary-key rels)})))) @@ -129,9 +132,10 @@ (when (not-empty included) (->> included (map (fn [[typ included-of-type]] - (if (coll? included-of-type) - (map (fn [include] (x-to-api (name typ) include :id)) included-of-type) - [(x-to-api (name typ) included-of-type :id)]))) + (when (coll? included-of-type) + (if (map? included-of-type) + [(x-to-api (name typ) included-of-type :id)] + (map (fn [include] (x-to-api (name typ) include :id)) included-of-type))))) (reduce (fn [acc curr] (into acc curr)))))) (defn wrap-pagination