diff --git a/src/codes/clj/docs/backend/adapters/db/postgres.clj b/src/codes/clj/docs/backend/adapters/db/postgres.clj index 1b95b4c..4e2c490 100644 --- a/src/codes/clj/docs/backend/adapters/db/postgres.clj +++ b/src/codes/clj/docs/backend/adapters/db/postgres.clj @@ -77,9 +77,9 @@ (filter #(= (:type %) "see-also")) (map db->see-also))) -(defn db->social +(defn db->socials {:malli/schema [:=> [:cat [:sequential schemas.db/Row]] - [:maybe schemas.model.social/Social]]} + [:maybe [:sequential schemas.model.social/Social]]]} [db-rows] (->> db-rows (group-by :definition-id) @@ -90,5 +90,10 @@ {:social/definition-id definition-id :social/notes notes :social/examples examples - :social/see-alsos see-alsos}))) - first)) + :social/see-alsos see-alsos}))))) + +(defn db->social-definition + {:malli/schema [:=> [:cat [:sequential schemas.db/Row]] + [:maybe schemas.model.social/Social]]} + [db-rows] + (first (db->socials db-rows))) diff --git a/src/codes/clj/docs/backend/adapters/social.clj b/src/codes/clj/docs/backend/adapters/social.clj index dc36e4a..255a9f0 100644 --- a/src/codes/clj/docs/backend/adapters/social.clj +++ b/src/codes/clj/docs/backend/adapters/social.clj @@ -121,3 +121,11 @@ :notes (map note->model->wire notes) :examples (map example->model->wire examples) :see-alsos (map see-also->model->wire see-alsos)}) + +(defn author+socials->model->wire + {:malli/schema [:=> [:cat schemas.model.social/Author+Socials] + schemas.wire.out.social/Author+Socials]} + [{:author/keys [socials] :as author}] + (enc/assoc-some + (author->model->wire author) + :socials (map social->model->wire socials))) diff --git a/src/codes/clj/docs/backend/controllers/social.clj b/src/codes/clj/docs/backend/controllers/social.clj index 6858c26..c8e6efe 100644 --- a/src/codes/clj/docs/backend/controllers/social.clj +++ b/src/codes/clj/docs/backend/controllers/social.clj @@ -9,11 +9,11 @@ [new-author {:keys [database]}] (db.postgres/upsert-author new-author database)) -(defn get-author +(defn get-author+socials {:malli/schema [:=> [:cat :string schemas.model.social/account-source schemas.types/Components] - [:maybe schemas.model.social/Author]]} + [:maybe schemas.model.social/Author+Socials]]} [login source {:keys [database]}] - (db.postgres/get-author login source database)) + (db.postgres/get-author+socials login source database)) (defn insert-see-also {:malli/schema [:=> [:cat schemas.model.social/NewSeeAlso schemas.types/Components] diff --git a/src/codes/clj/docs/backend/db/postgres.clj b/src/codes/clj/docs/backend/db/postgres.clj index e77d67c..2cd6ed9 100644 --- a/src/codes/clj/docs/backend/db/postgres.clj +++ b/src/codes/clj/docs/backend/db/postgres.clj @@ -5,41 +5,14 @@ [honey.sql :as sql] [honey.sql.helpers :as sql.helpers] [next.jdbc :as jdbc] - [parenthesin.components.db.jdbc-hikari :as components.database])) + [parenthesin.components.db.jdbc-hikari :as components.database] + [taoensso.encore :as enc])) (defn ^:private execute! {:malli/schema [:=> [:cat schemas.types/DatabaseComponent :any] :any]} [db sql-params] (components.database/execute db sql-params jdbc/unqualified-snake-kebab-opts)) -(defn upsert-author - {:malli/schema [:=> [:cat schemas.model.social/NewAuthor schemas.types/DatabaseComponent] - schemas.model.social/Author]} - [transaction db] - (->> (-> (sql.helpers/insert-into :author) - (sql.helpers/values [transaction]) - (sql.helpers/upsert (-> (sql.helpers/on-conflict :login :account_source) - (sql.helpers/do-update-set :avatar_url))) - (sql.helpers/returning :*) - sql/format) - (execute! db) - first - adapters/db->author)) - -(defn get-author - {:malli/schema [:=> [:cat :string schemas.model.social/account-source schemas.types/DatabaseComponent] - [:maybe schemas.model.social/Author]]} - [login source db] - (when-let [author (->> (-> (sql.helpers/select :*) - (sql.helpers/from :author) - (sql.helpers/where :and - [:= :login login] - [:= :account_source source]) - sql/format) - (execute! db) - first)] - (adapters/db->author author))) - (defn insert-see-also {:malli/schema [:=> [:cat schemas.model.social/NewSeeAlso schemas.types/DatabaseComponent] schemas.model.social/SeeAlso]} @@ -259,6 +232,51 @@ first adapters/db->note)) +(defn upsert-author + {:malli/schema [:=> [:cat schemas.model.social/NewAuthor schemas.types/DatabaseComponent] + schemas.model.social/Author]} + [transaction db] + (->> (-> (sql.helpers/insert-into :author) + (sql.helpers/values [transaction]) + (sql.helpers/upsert (-> (sql.helpers/on-conflict :login :account_source) + (sql.helpers/do-update-set :avatar_url))) + (sql.helpers/returning :*) + sql/format) + (execute! db) + first + adapters/db->author)) + +(defn get-author+socials + {:malli/schema [:=> [:cat :string schemas.model.social/account-source schemas.types/DatabaseComponent] + [:maybe schemas.model.social/Author+Socials]]} + [login source db] + (when-let [author (->> (-> (sql.helpers/select :*) + (sql.helpers/from :author) + (sql.helpers/where :and + [:= :login login] + [:= :account_source source]) + sql/format) + (execute! db) + first)] + (let [author-id (:author-id author) + socials (->> (-> (sql.helpers/union-all + (-> get-note-query + (sql.helpers/where [:= :note/author-id author-id])) + + (-> get-example-query + (sql.helpers/where [:= :example-edit/author-id author-id])) + + (-> get-see-also-query + (sql.helpers/where [:= :see-also/author-id author-id]))) + sql/format) + (execute! db) + adapters/db->socials + seq)] + + (enc/assoc-some + (adapters/db->author author) + :author/socials socials)))) + (defn get-by-definition {:malli/schema [:=> [:cat :string schemas.types/DatabaseComponent] [:maybe schemas.model.social/Social]]} @@ -274,4 +292,4 @@ (sql.helpers/where [:= :see-also/definition-id definition-id]))) sql/format) (execute! db) - adapters/db->social)) + adapters/db->social-definition)) diff --git a/src/codes/clj/docs/backend/ports/http_in/social.clj b/src/codes/clj/docs/backend/ports/http_in/social.clj index 435aaa5..4b5b296 100644 --- a/src/codes/clj/docs/backend/ports/http_in/social.clj +++ b/src/codes/clj/docs/backend/ports/http_in/social.clj @@ -16,12 +16,12 @@ :body {:author author :access-token access-token}})) -(defn get-author +(defn get-author+socials [{{{:keys [login source]} :path} :parameters components :components}] - (if-let [author (controllers.social/get-author login source components)] + (if-let [author (controllers.social/get-author+socials login source components)] {:status 200 - :body (adapters.social/author->model->wire author)} + :body (adapters.social/author+socials->model->wire author)} {:status 404 :body "not found"})) diff --git a/src/codes/clj/docs/backend/routes.clj b/src/codes/clj/docs/backend/routes.clj index 0f6e280..de3af9b 100644 --- a/src/codes/clj/docs/backend/routes.clj +++ b/src/codes/clj/docs/backend/routes.clj @@ -45,14 +45,14 @@ ["/author" ["/:login/:source" - {:get {:summary "get author by login and source" + {:get {:summary "get author and social interactions (if any) by login and source" :parameters {:path {:login :string :source :string}} - :responses {200 {:body schemas.wire.social/Author} + :responses {200 {:body schemas.wire.out.social/Author+Socials} 400 {:body :string} 404 {:body :string} 500 {:body :string}} - :handler ports.http-in.social/get-author}}]] + :handler ports.http-in.social/get-author+socials}}]] ["/example" ["/:example-id" diff --git a/src/codes/clj/docs/backend/schemas/model/social.clj b/src/codes/clj/docs/backend/schemas/model/social.clj index e230eed..9719669 100644 --- a/src/codes/clj/docs/backend/schemas/model/social.clj +++ b/src/codes/clj/docs/backend/schemas/model/social.clj @@ -110,3 +110,6 @@ [:social/notes [:sequential Note]] [:social/examples [:sequential Example]] [:social/see-alsos [:sequential SeeAlso]]]) + +(def Author+Socials + (mu/assoc Author [:author/socials {:optional true}] [:sequential Social])) diff --git a/src/codes/clj/docs/backend/schemas/wire/out/social.clj b/src/codes/clj/docs/backend/schemas/wire/out/social.clj index f2ebbb0..bfb2fce 100644 --- a/src/codes/clj/docs/backend/schemas/wire/out/social.clj +++ b/src/codes/clj/docs/backend/schemas/wire/out/social.clj @@ -1,6 +1,6 @@ (ns codes.clj.docs.backend.schemas.wire.out.social - (:require [codes.clj.docs.backend.schemas.wire.social :refer [example note - see-also]] + (:require [codes.clj.docs.backend.schemas.wire.social :refer [Author example + note see-also]] [malli.util :as mu])) (def SeeAlso @@ -32,3 +32,6 @@ [:notes [:sequential Note]] [:examples [:sequential Example]] [:see-alsos [:sequential SeeAlso]]]) + +(def Author+Socials + (mu/assoc Author [:socials {:optional true}] [:sequential Social])) diff --git a/test/integration/codes/clj/docs/backend/db/postgres_test.clj b/test/integration/codes/clj/docs/backend/db/postgres_test.clj index e1fcec3..502645f 100644 --- a/test/integration/codes/clj/docs/backend/db/postgres_test.clj +++ b/test/integration/codes/clj/docs/backend/db/postgres_test.clj @@ -26,9 +26,31 @@ :cleanup util/stop-system! :fail-fast? true} - [database (state-flow.api/get-state :database)] - - (util.db.postgres/upsert-author "delboni" "github") + [database (state-flow.api/get-state :database) + + ; prepare db authors + author-1 (util.db.postgres/upsert-author "delboni" "github") + author-2 (util.db.postgres/upsert-author "not-delboni" "github") + + ; prepare socials + see-also-1 (util.db.postgres/create-see-also {:see-also/author-id (:author/author-id author-1) + :see-also/definition-id "clojure.core/disj" + :see-also/definition-id-to "clojure.core/dissoc"}) + _see-also-2 (util.db.postgres/create-see-also {:see-also/author-id (:author/author-id author-2) + :see-also/definition-id "clojure.core/disj" + :see-also/definition-id-to "clojure.core/dissoc2"}) + note-1 (util.db.postgres/create-note {:note/author-id (:author/author-id author-1) + :note/definition-id "clojure.core/disj" + :note/body "author 1 note about this function."}) + _note-2 (util.db.postgres/create-note {:note/author-id (:author/author-id author-2) + :note/definition-id "clojure.core/disj" + :note/body "author 2 note about this function."}) + example-1 (util.db.postgres/create-example {:example/author-id (:author/author-id author-1) + :example/definition-id "clojure.core/disj" + :example/body "author 1 example about this function."}) + _example-2 (util.db.postgres/create-example {:example/author-id (:author/author-id author-2) + :example/definition-id "clojure.core/disj" + :example/body "author 2 example about this function."})] (flow "upsert author with new url" (state/invoke @@ -42,8 +64,14 @@ :author/login "delboni" :author/account-source "github" :author/avatar-url "https://my.pic.com/me2.jpg" - :author/created-at inst?} - (db/get-author "delboni" "github" database)))) + :author/created-at inst? + :author/socials [{:social/definition-id "clojure.core/disj" + :social/notes [note-1] + :social/examples [(dissoc example-1 + :example/author-id)] + :social/see-alsos [see-also-1]}]} + + (db/get-author+socials "delboni" "github" database)))) (defflow see-also-db-test {:init (util/start-system! create-and-start-components!) diff --git a/test/integration/codes/clj/docs/backend/social_test.clj b/test/integration/codes/clj/docs/backend/social_test.clj index dec1fb4..9cbb56b 100644 --- a/test/integration/codes/clj/docs/backend/social_test.clj +++ b/test/integration/codes/clj/docs/backend/social_test.clj @@ -141,6 +141,23 @@ (state-flow.server/request! {:method :get :uri (str "/api/social/note/" note-id)}))) + (flow "should return author + socials" + (match? {:status 200 + :body {:author-id string? + :login "delboni", + :account-source "github", + :avatar-url "https://my.pic/me.jpg", + :created-at string? + :socials [{:definition-id "clojure.core/disj" + :notes [{:note-id note-id + :definition-id "clojure.core/disj" + :body "my edited note about this function." + :created-at string?}] + :examples [] + :see-alsos []}]}} + (state-flow.server/request! {:method :get + :uri "/api/social/author/delboni/github"}))) + (flow "should not be able to delete if not allowed" (match? {:status 403 :body "You not allowed to delete this note."} @@ -212,6 +229,22 @@ :created-at string?}} (state-flow.server/request! {:method :get :uri (str "/api/social/see-also/" see-also-id)}))) + (flow "should return author + socials" + (match? {:status 200 + :body {:author-id string? + :login "delboni", + :account-source "github", + :avatar-url "https://my.pic/me.jpg", + :created-at string? + :socials [{:definition-id "clojure.core/disj" + :notes [] + :examples [] + :see-alsos [{:see-also-id see-also-id + :definition-id "clojure.core/disj" + :definition-id-to "clojure.core/dissoc" + :created-at string?}]}]}} + (state-flow.server/request! {:method :get + :uri "/api/social/author/delboni/github"}))) (flow "should not be able to delete if not allowed" (match? {:status 403 @@ -324,6 +357,35 @@ (state-flow.server/request! {:method :get :uri (str "/api/social/example/" example-id)}))) + (flow "should return author" + (match? {:status 200 + :body {:author-id string? + :login "delboni", + :account-source "github", + :avatar-url "https://my.pic/me.jpg", + :created-at string? + :socials [{:definition-id "clojure.core/disj" + :notes [] + :see-alsos [] + :examples [{:example-id example-id + :definition-id "clojure.core/disj" + :body "my edited example about this function." + :created-at string? + :editors [{:author-id string? + :login "delboni" + :account-source "github" + :avatar-url "https://my.pic/me.jpg" + :created-at string? + :edited-at string?} + {:author-id string? + :login "delboni" + :account-source "github" + :avatar-url "https://my.pic/me.jpg" + :created-at string? + :edited-at string?}]}]}]}} + (state-flow.server/request! {:method :get + :uri "/api/social/author/delboni/github"}))) + (flow "delete example revision part 1" (state-flow.server/request! {:method :delete :headers {"authorization" (str "Bearer " token)} diff --git a/test/unit/codes/clj/docs/backend/adapters/db/postgres_test.clj b/test/unit/codes/clj/docs/backend/adapters/db/postgres_test.clj index b06ac12..7db75ac 100644 --- a/test/unit/codes/clj/docs/backend/adapters/db/postgres_test.clj +++ b/test/unit/codes/clj/docs/backend/adapters/db/postgres_test.clj @@ -200,4 +200,59 @@ :avatar-url "https://my.pic.com/me.jpg" :created-at #inst "2020-10-23T00:00:00.000-00:00"}}]} - (adapters/db->social db-rows))))) + (adapters/db->social-definition db-rows))))) + +(deftest db->author-social-test + (testing "should map and get author social data for multiple definitions" + (is (match? [#:social{:definition-id "clojure.core/disj" + :notes [#:note{:note-id #uuid "d9564b50-98f8-4c04-a668-bd24c1241e34" + :definition-id "clojure.core/disj" + :body "my note about this function." + :created-at #inst "2020-10-23T00:00:00.000-00:00" + :author + #:author{:author-id + #uuid "387863e6-e32b-4d4b-8ec5-8cf4dab7e048" + :login "delboni" + :account-source "github" + :avatar-url "https://my.pic.com/me.jpg" + :created-at + #inst "2020-10-23T00:00:00.000-00:00"}}] + :examples [] + :see-alsos []} + #:social{:definition-id "clojure.core/disj2" + :notes [#:note{:note-id #uuid "d9564b50-98f8-4c04-a668-bd24c1241e34" + :definition-id "clojure.core/disj2" + :body "my note about this function 2." + :created-at #inst "2020-10-23T00:00:00.000-00:00" + :author + #:author{:author-id + #uuid "387863e6-e32b-4d4b-8ec5-8cf4dab7e048" + :login "delboni" + :account-source "github" + :avatar-url "https://my.pic.com/me.jpg" + :created-at + #inst "2020-10-23T00:00:00.000-00:00"}}] + :examples [] + :see-alsos []}] + (adapters/db->socials [{:account-source "github" + :avatar-url "https://my.pic.com/me.jpg" + :type "note" + :created #inst "2020-10-23T00:00:00.000-00:00" + :login "delboni" + :id #uuid "d9564b50-98f8-4c04-a668-bd24c1241e34" + :author-id #uuid "387863e6-e32b-4d4b-8ec5-8cf4dab7e048" + :body "my note about this function." + :created-at #inst "2020-10-23T00:00:00.000-00:00" + :definition-id "clojure.core/disj"} + {:account-source "github" + :avatar-url "https://my.pic.com/me.jpg" + :type "note" + :created #inst "2020-10-23T00:00:00.000-00:00" + :login "delboni" + :id #uuid "d9564b50-98f8-4c04-a668-bd24c1241e34" + :author-id #uuid "387863e6-e32b-4d4b-8ec5-8cf4dab7e048" + :body "my note about this function 2." + :created-at #inst "2020-10-23T00:00:00.000-00:00" + :definition-id "clojure.core/disj2"}]))))) + + diff --git a/test/unit/codes/clj/docs/backend/adapters/social_test.clj b/test/unit/codes/clj/docs/backend/adapters/social_test.clj index b7b9985..2e92e87 100644 --- a/test/unit/codes/clj/docs/backend/adapters/social_test.clj +++ b/test/unit/codes/clj/docs/backend/adapters/social_test.clj @@ -82,3 +82,8 @@ (properties/for-all [definition (mg/generator schemas.model.social/Social)] (m/validate schemas.wire.out.social/Social (adapters.social/social->model->wire definition)))) + +(defspec author+socials->model->wire-test 50 + (properties/for-all [author+socials (mg/generator schemas.model.social/Author+Socials)] + (m/validate schemas.wire.out.social/Author+Socials + (adapters.social/author+socials->model->wire author+socials))))