Skip to content

Commit

Permalink
✨ Save do-not-calls to database
Browse files Browse the repository at this point in the history
Why:
- Adding the feature of recording do-not-calls inside the app.
- Since the do-not-calls contains PPI, they should not be stored as
  immutable events, but using a relational model.
  • Loading branch information
luontola committed Jan 3, 2024
1 parent ae4876c commit 07bf0dc
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 2 deletions.
8 changes: 8 additions & 0 deletions resources/db/flyway/master/V11__add_do_not_calls.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
create table do_not_calls
(
congregation uuid not null,
territory uuid not null,
do_not_calls text not null,
last_modified timestamptz not null,
primary key (congregation, territory)
);
23 changes: 23 additions & 0 deletions resources/db/hugsql/do-not-calls.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- Copyright © 2015-2024 Esko Luontola
-- This software is released under the Apache License 2.0.
-- The license text is at http://www.apache.org/licenses/LICENSE-2.0

-- :name get-do-not-calls :? :1
select congregation, territory, do_not_calls, last_modified
from do_not_calls
where congregation = :congregation
and territory = :territory
limit 1;

-- :name save-do-not-calls :! :n
insert into do_not_calls (congregation, territory, do_not_calls, last_modified)
values (:congregation, :territory, :do_not_calls, :last_modified)
on conflict on constraint do_not_calls_pkey
do update set do_not_calls = :do_not_calls,
last_modified = :last_modified;

-- :name delete-do-not-calls :! :n
delete
from do_not_calls
where congregation = :congregation
and territory = :territory;
12 changes: 10 additions & 2 deletions src/territory_bro/commands.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
;; Copyright © 2015-2023 Esko Luontola
;; Copyright © 2015-2024 Esko Luontola
;; This software is released under the Apache License 2.0.
;; The license text is at http://www.apache.org/licenses/LICENSE-2.0

Expand Down Expand Up @@ -129,6 +129,13 @@
:congregation/id (foreign-key/references :congregation UUID)
:territory/id (foreign-key/references :territory UUID)))

(s/defschema SaveDoNotCalls
(assoc BaseCommand
:command/type (s/eq :territory.command/save-do-not-calls)
:congregation/id (foreign-key/references :congregation UUID)
:territory/id (foreign-key/references :territory UUID)
:territory/do-not-calls s/Str))

;;; Region

(s/defschema DefineRegion
Expand Down Expand Up @@ -211,7 +218,8 @@
:share.command/create-share CreateShare
:share.command/record-share-opened RecordShareOpened
:territory.command/define-territory DefineTerritory
:territory.command/delete-territory DeleteTerritory})
:territory.command/delete-territory DeleteTerritory
:territory.command/save-do-not-calls SaveDoNotCalls})

(s/defschema Command
(apply refined/dispatch-on :command/type (flatten (seq command-schemas))))
Expand Down
31 changes: 31 additions & 0 deletions src/territory_bro/domain/do_not_calls.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
;; Copyright © 2015-2024 Esko Luontola
;; This software is released under the Apache License 2.0.
;; The license text is at http://www.apache.org/licenses/LICENSE-2.0

(ns territory-bro.domain.do-not-calls
(:require [clojure.string :as str]
[territory-bro.infra.db :as db]))

(def ^:private query! (db/compile-queries "db/hugsql/do-not-calls.sql"))

(defn- parse-db-row [row]
(when (some? row)
{:congregation/id (:congregation row)
:territory/id (:territory row)
:territory/do-not-calls (:do_not_calls row)
:do-not-calls/last-modified (:last_modified row)}))

(defn get-do-not-calls [conn cong-id territory-id]
(-> (query! conn :get-do-not-calls {:congregation cong-id
:territory territory-id})
(parse-db-row)))

(defn save-do-not-calls! [conn command]
(let [do-not-calls (:territory/do-not-calls command)]
(if (str/blank? do-not-calls)
(query! conn :delete-do-not-calls {:congregation (:congregation/id command)
:territory (:territory/id command)})
(query! conn :save-do-not-calls {:congregation (:congregation/id command)
:territory (:territory/id command)
:do_not_calls do-not-calls
:last_modified (:command/time command)}))))
75 changes: 75 additions & 0 deletions test/territory_bro/domain/do_not_calls_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
;; Copyright © 2015-2024 Esko Luontola
;; This software is released under the Apache License 2.0.
;; The license text is at http://www.apache.org/licenses/LICENSE-2.0

(ns ^:slow territory-bro.domain.do-not-calls-test
(:require [clojure.java.jdbc :as jdbc]
[clojure.test :refer :all]
[territory-bro.domain.do-not-calls :as do-not-calls]
[territory-bro.infra.db :as db]
[territory-bro.test.fixtures :refer [db-fixture]])
(:import (java.time Instant)
(java.util UUID)))

(use-fixtures :once db-fixture)

(def cong-id (UUID. 0 1))
(def territory-id (UUID. 0 2))

(deftest do-not-calls-test
(db/with-db [conn {}]
(jdbc/db-set-rollback-only! conn)
(do-not-calls/save-do-not-calls! conn {:command/time (Instant/ofEpochSecond 666)
:congregation/id (UUID. 0 0x666)
:territory/id territory-id
:territory/do-not-calls "unrelated 1"})
(do-not-calls/save-do-not-calls! conn {:command/time (Instant/ofEpochSecond 666)
:congregation/id cong-id
:territory/id (UUID. 0 0x666)
:territory/do-not-calls "unrelated 2"})

(testing "cannot read do-not-calls when there are none"
(is (nil? (do-not-calls/get-do-not-calls conn cong-id territory-id))))

(testing "create do-not-calls"
(do-not-calls/save-do-not-calls! conn {:command/time (Instant/ofEpochSecond 1)
:congregation/id cong-id
:territory/id territory-id
:territory/do-not-calls "original text"})
(is (= {:congregation/id cong-id
:territory/id territory-id
:territory/do-not-calls "original text"
:do-not-calls/last-modified (Instant/ofEpochSecond 1)}
(do-not-calls/get-do-not-calls conn cong-id territory-id))))

(testing "cannot read do-not-calls when the user lacks read permission") ; TODO

(testing "update do-not-calls"
(do-not-calls/save-do-not-calls! conn {:command/time (Instant/ofEpochSecond 2)
:congregation/id cong-id
:territory/id territory-id
:territory/do-not-calls "updated text"})
(is (= {:congregation/id cong-id
:territory/id territory-id
:territory/do-not-calls "updated text"
:do-not-calls/last-modified (Instant/ofEpochSecond 2)}
(do-not-calls/get-do-not-calls conn cong-id territory-id))))

(testing "delete do-not-calls"
(do-not-calls/save-do-not-calls! conn {:command/time (Instant/ofEpochSecond 3)
:congregation/id cong-id
:territory/id territory-id
:territory/do-not-calls ""})
(is (nil? (do-not-calls/get-do-not-calls conn cong-id territory-id))))

(testing "did not modify unrelated database rows"
(is (= {:congregation/id (UUID. 0 0x666)
:territory/id territory-id
:territory/do-not-calls "unrelated 1"
:do-not-calls/last-modified (Instant/ofEpochSecond 666)}
(do-not-calls/get-do-not-calls conn (UUID. 0 0x666) territory-id)))
(is (= {:congregation/id cong-id
:territory/id (UUID. 0 0x666)
:territory/do-not-calls "unrelated 2"
:do-not-calls/last-modified (Instant/ofEpochSecond 666)}
(do-not-calls/get-do-not-calls conn cong-id (UUID. 0 0x666)))))))

0 comments on commit 07bf0dc

Please sign in to comment.