Skip to content

Commit

Permalink
Merge pull request #39 from clj-codes/feat/adds-social-dashboards-api
Browse files Browse the repository at this point in the history
Adds api for latest & top author interactions
  • Loading branch information
rafaeldelboni authored Aug 3, 2024
2 parents 9236b36 + 14fd876 commit 8875bb1
Show file tree
Hide file tree
Showing 15 changed files with 364 additions and 5 deletions.
2 changes: 0 additions & 2 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.7"

services:
db:
image: postgres:14
Expand Down
21 changes: 21 additions & 0 deletions src/codes/clj/docs/backend/adapters/db/postgres.clj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@
:author/avatar-url avatar-url
:author/created-at created-at})

(defn db->author+interaction
{:malli/schema [:=> [:cat [:sequential schemas.db/Author+InteractionsRow]]
[:sequential schemas.model.social/Author+Interactions]]}
[db-rows]
(map (fn [{:keys [interactions] :as author}]
(assoc (db->author author)
:author/interactions interactions))
db-rows))

(defn db->note
{:malli/schema [:=> [:cat [:maybe schemas.db/Row]] [:maybe schemas.model.social/Note]]}
[{:keys [id definition-id body created author-id] :as note}]
Expand Down Expand Up @@ -97,3 +106,15 @@
[:maybe schemas.model.social/Social]]}
[db-rows]
(first (db->socials db-rows)))

(defn db->any-socials
{:malli/schema [:=> [:cat [:sequential schemas.db/Row]]
[:maybe [:sequential schemas.model.social/AnySocial]]]}
[db-rows]
(let [notes (db->notes db-rows)
examples (db->examples db-rows)
see-alsos (db->see-alsos db-rows)]
(concat
(seq notes)
(seq examples)
(seq see-alsos))))
29 changes: 29 additions & 0 deletions src/codes/clj/docs/backend/adapters/social.clj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@
:avatar-url avatar-url
:created-at created-at})

(defn author-interaction->model->wire
{:malli/schema [:=> [:cat [:sequential schemas.model.social/Author+Interactions]]
[:sequential schemas.wire.out.social/Author+Interactions]]}
[author+interactions]
(->> author+interactions
(map (fn [{:author/keys [interactions] :as author}]
(assoc (author->model->wire author)
:interactions interactions)))
(sort-by :interactions #(compare %2 %1))))

(defn editor->model->wire
{:malli/schema [:=> [:cat schemas.model.social/Editor] schemas.wire.social/Editor]}
[{:editor/keys [edited-at] :as author}]
Expand Down Expand Up @@ -129,3 +139,22 @@
(enc/assoc-some
(author->model->wire author)
:socials (map social->model->wire socials)))

(defn any-social->model->wire
{:malli/schema [:=> [:cat [:sequential schemas.model.social/AnySocial]]
[:maybe [:sequential schemas.wire.out.social/AnySocial]]]}
[any-socials]
(let [notes (->> any-socials
(filter #(:note/note-id %))
(map note->model->wire))
examples (->> any-socials
(filter #(:example/example-id %))
(map example->model->wire))
see-alsos (->> any-socials
(filter #(:see-also/see-also-id %))
(map see-also->model->wire))]
(->> (concat
(seq notes)
(seq examples)
(seq see-alsos))
(sort-by :created-at #(compare %2 %1)))))
12 changes: 12 additions & 0 deletions src/codes/clj/docs/backend/controllers/social.clj
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,15 @@
[:maybe schemas.model.social/Social]]}
[definition-id {:keys [database]}]
(db.postgres/get-by-definition definition-id database))

(defn get-top-authors
{:malli/schema [:=> [:cat :int schemas.types/Components]
[:maybe [:sequential schemas.model.social/Author+Interactions]]]}
[limit {:keys [database]}]
(db.postgres/get-top-authors limit database))

(defn get-latest-interactions
{:malli/schema [:=> [:cat :int schemas.types/Components]
[:maybe [:sequential schemas.model.social/AnySocial]]]}
[limit {:keys [database]}]
(db.postgres/get-latest-interactions limit database))
43 changes: 43 additions & 0 deletions src/codes/clj/docs/backend/db/postgres.clj
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,46 @@
sql/format)
(execute! db)
adapters/db->social-definition))

(defn get-top-authors
{:malli/schema [:=> [:cat :int schemas.types/DatabaseComponent]
[:maybe [:sequential schemas.model.social/Author+Interactions]]]}
[limit db]
(->> (-> (sql.helpers/select
:author/*
[[:count :social/id] :interactions])
(sql.helpers/from [(sql.helpers/union-all
(-> (sql.helpers/select
[:note/note-id :id]
[:note/author-id :author-id])
(sql.helpers/from :note))
(-> (sql.helpers/select
[:example-edit/example-edit-id :id]
[:example-edit/author-id :author-id])
(sql.helpers/from :example-edit))
(-> (sql.helpers/select
[:see-also/see-also-id :id]
[:see-also/author-id :author-id])
(sql.helpers/from :see-also))) :social])
(sql.helpers/join :author
[:= :social/author-id :author/author-id])
(sql.helpers/group-by :author/author-id)
(sql.helpers/order-by [:interactions :desc])
(sql.helpers/limit limit)
sql/format)
(execute! db)
adapters/db->author+interaction))

(defn get-latest-interactions
{:malli/schema [:=> [:cat :int schemas.types/DatabaseComponent]
[:maybe [:sequential schemas.model.social/AnySocial]]]}
[limit db]
(->> (-> (sql.helpers/union-all
get-note-query
get-example-query
get-see-also-query)
(sql.helpers/order-by [:created :desc])
(sql.helpers/limit limit)
sql/format)
(execute! db)
adapters/db->any-socials))
16 changes: 16 additions & 0 deletions src/codes/clj/docs/backend/ports/http_in/social.clj
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,19 @@
:notes []
:examples []
:see-alsos []})})

(defn get-top-authors
[{{{:keys [limit]} :query} :parameters
components :components}]
{:status 200
:body (-> (or limit 10)
(controllers.social/get-top-authors components)
adapters.social/author-interaction->model->wire)})

(defn get-latest-interactions
[{{{:keys [limit]} :query} :parameters
components :components}]
{:status 200
:body (-> (or limit 10)
(controllers.social/get-latest-interactions components)
adapters.social/any-social->model->wire)})
19 changes: 18 additions & 1 deletion src/codes/clj/docs/backend/routes.clj
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,24 @@
:responses {200 {:body schemas.wire.out.social/Social}
404 {:body :string}
500 {:body :string}}
:handler ports.http-in.social/get-by-definition}}]]]
:handler ports.http-in.social/get-by-definition}}]]

["/query"
["/top-authors"
{:get {:summary "get top contributing author list"
:parameters {:query [:map [:limit {:optional true} :int]]}
:responses {200 {:body [:sequential schemas.wire.out.social/Author+Interactions]}
404 {:body :string}
500 {:body :string}}
:handler ports.http-in.social/get-top-authors}}]

["/latest-interactions"
{:get {:summary "get latest social interactions list"
:parameters {:query [:map [:limit {:optional true} :int]]}
:responses {200 {:body [:sequential schemas.wire.out.social/AnySocial]}
404 {:body :string}
500 {:body :string}}
:handler ports.http-in.social/get-latest-interactions}}]]]

["/document"
{:swagger {:tags ["document"]}}
Expand Down
3 changes: 3 additions & 0 deletions src/codes/clj/docs/backend/schemas/db/postgres.clj
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@
:account-source
:avatar-url
:created-at]))

(def Author+InteractionsRow
(mu/assoc AuthorRow :interactions :int))
6 changes: 6 additions & 0 deletions src/codes/clj/docs/backend/schemas/model/social.clj
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,11 @@
[:social/examples [:sequential Example]]
[:social/see-alsos [:sequential SeeAlso]]])

(def AnySocial
[:or Example Note SeeAlso])

(def Author+Socials
(mu/assoc Author [:author/socials {:optional true}] [:sequential Social]))

(def Author+Interactions
(mu/assoc Author :author/interactions :int))
6 changes: 6 additions & 0 deletions src/codes/clj/docs/backend/schemas/wire/out/social.clj
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,11 @@
[:examples [:sequential Example]]
[:see-alsos [:sequential SeeAlso]]])

(def AnySocial
[:or Example Note SeeAlso])

(def Author+Socials
(mu/assoc Author [:socials {:optional true}] [:sequential Social]))

(def Author+Interactions
(mu/assoc Author :interactions :int))
29 changes: 28 additions & 1 deletion test/integration/codes/clj/docs/backend/db/postgres_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[com.stuartsierra.component :as component]
[integration.codes.clj.docs.backend.util :as util]
[integration.codes.clj.docs.backend.util.db.postgres :as util.db.postgres]
[matcher-combinators.matchers :as m]
[parenthesin.components.config.aero :as components.config]
[parenthesin.components.db.jdbc-hikari :as components.database]
[parenthesin.helpers.malli :as helpers.malli]
Expand Down Expand Up @@ -71,7 +72,33 @@
:example/author-id)]
:social/see-alsos [see-also-1]}]}

(db/get-author+socials "delboni" "github" database))))
(db/get-author+socials "delboni" "github" database)))

(flow "check latest top authors in db"
(match? (m/in-any-order
[#:author{:author-id uuid?
:login "not-delboni"
:account-source "github"
:avatar-url "https://my.pic.com/me.jpg"
:created-at inst?
:interactions 3}
#:author{:author-id uuid?
:login "delboni"
:account-source "github"
:avatar-url "https://my.pic.com/me2.jpg"
:created-at inst?
:interactions 3}])
(util.db.postgres/get-top-authors 10)))

(flow "check latest social interactions in db"
(match? (m/in-any-order
[#:note{:note-id uuid?}
#:note{:note-id uuid?}
#:example{:example-id uuid?}
#:example{:example-id uuid?}
#:see-also{:see-also-id uuid?}
#:see-also{:see-also-id uuid?}])
(util.db.postgres/get-latest-interactions 10))))

(defflow see-also-db-test
{:init (util/start-system! create-and-start-components!)
Expand Down
77 changes: 77 additions & 0 deletions test/integration/codes/clj/docs/backend/social_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,31 @@
(state-flow.server/request! {:method :get
:uri "/api/social/author/delboni/github"})))

(flow "should return top authors"
(match? {:status 200
:body [{:author-id string?
:login "delboni"
:account-source "github"
:avatar-url "https://my.pic/me.jpg"
:created-at string?
:interactions 1}]}
(state-flow.server/request! {:method :get
:uri "/api/social/query/top-authors"})))

(flow "should return latest interactions"
(match? {:status 200
:body [{:note-id string?
:definition-id "clojure.core/disj"
:body "my edited note about this function."
:created-at string?
:author {:author-id string?
:login "delboni"
:account-source "github"
:avatar-url "https://my.pic/me.jpg"
:created-at string?}}]}
(state-flow.server/request! {:method :get
:uri "/api/social/query/latest-interactions"})))

(flow "should not be able to delete if not allowed"
(match? {:status 403
:body "You not allowed to delete this note."}
Expand Down Expand Up @@ -246,6 +271,26 @@
(state-flow.server/request! {:method :get
:uri "/api/social/author/delboni/github"})))

(flow "should return top authors"
(match? {:status 200
:body [{:author-id string?
:login "delboni"
:account-source "github"
:avatar-url "https://my.pic/me.jpg"
:created-at string?
:interactions 1}]}
(state-flow.server/request! {:method :get
:uri "/api/social/query/top-authors"})))

(flow "should return latest interactions"
(match? {:status 200
:body [{: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/query/latest-interactions"})))

(flow "should not be able to delete if not allowed"
(match? {:status 403
:body "You not allowed to delete this see also."}
Expand Down Expand Up @@ -386,6 +431,38 @@
(state-flow.server/request! {:method :get
:uri "/api/social/author/delboni/github"})))

(flow "should return top authors"
(match? {:status 200
:body [{:author-id string?
:login "delboni"
:account-source "github"
:avatar-url "https://my.pic/me.jpg"
:created-at string?
:interactions 2}]}
(state-flow.server/request! {:method :get
:uri "/api/social/query/top-authors"})))

(flow "should return latest interactions"
(match? {:status 200
:body [{: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/query/latest-interactions"})))

(flow "delete example revision part 1"
(state-flow.server/request! {:method :delete
:headers {"authorization" (str "Bearer " token)}
Expand Down
16 changes: 16 additions & 0 deletions test/integration/codes/clj/docs/backend/util/db/postgres.clj
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,19 @@
(->> database
(db/get-by-definition definition-id)
state-flow.api/return)))

(defn get-top-authors
[limit]
(flow "get top authors with their interactions sum"
[database (state-flow.api/get-state :database)]
(->> database
(db/get-top-authors limit)
state-flow.api/return)))

(defn get-latest-interactions
[limit]
(flow "get latest social interactions"
[database (state-flow.api/get-state :database)]
(->> database
(db/get-latest-interactions limit)
state-flow.api/return)))
Loading

0 comments on commit 8875bb1

Please sign in to comment.