Skip to content

Commit

Permalink
Use malli for defining and validating schema
Browse files Browse the repository at this point in the history
  • Loading branch information
tarun-nil committed Jan 1, 2024
1 parent 262454a commit f0c1697
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 53 deletions.
4 changes: 2 additions & 2 deletions swift-ticketing/project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
[compojure "1.7.0"]
[ring/ring-defaults "0.3.2"]
[com.github.seancorfield/next.jdbc "1.3.894"]
[org.xerial/sqlite-jdbc "3.43.2.0"]
[com.github.seancorfield/honeysql "2.4.1066"]
[com.zaxxer/HikariCP "3.3.1"]
[org.postgresql/postgresql "42.6.0"]
Expand All @@ -23,7 +22,8 @@
[com.taoensso/carmine "3.3.2"]
[ragtime "0.8.0"]
[com.stuartsierra/component "1.1.0"]
[com.taoensso/timbre "6.3.1"]]
[com.taoensso/timbre "6.3.1"]
[metosin/malli "0.13.0"]]
:main ^:skip-aot swift-ticketing.core
:target-path "target/%s"
:plugins [[lein-ring "0.12.6"]]
Expand Down
33 changes: 19 additions & 14 deletions swift-ticketing/src/swift_ticketing/handlers.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
(ns swift-ticketing.handlers
(:require
[clojure.spec.alpha :as s]
[malli.core :as m]
[malli.error :as me]
[swift-ticketing.model.event :as event]
[swift-ticketing.specs :as specs]
[clojure.walk :refer [keywordize-keys]]
Expand All @@ -22,9 +24,12 @@
(respond-json 200 body))

(defn- validate-req [req spec handler]
(if (s/valid? spec req)
(if (m/validate spec req)
(handler)
(respond-400 (s/explain-data spec req))))
(-> spec
(m/explain req)
me/humanize
respond-400)))

(defn- get-uid [cookies]
(get-in cookies ["uid" :value]))
Expand All @@ -36,21 +41,21 @@
on-success (fn []
(respond-200
(event/get-events db-spec filters)))]
(validate-req filters ::specs/get-events-params on-success)))
(validate-req filters specs/GetEventsParams on-success)))

(defn get-event-handler [db-spec request]
(let [event-id (get-in request [:route-params :event-id])
on-success (fn []
(respond-200 (event/get-event db-spec event-id)))]
(validate-req event-id ::specs/event-id on-success)))
(validate-req event-id specs/EventId on-success)))

(defn create-event-handler [db-spec request]
(let [{:keys [cookies body]} request
uid (get-uid cookies)
on-success (fn []
(respond-201
{:event-id (event/create-event db-spec uid body)}))]
(validate-req body ::specs/create-event-params on-success)))
(validate-req body specs/CreateEventParams on-success)))

(defn create-tickets-handler [db-spec request]
(let [{:keys [cookies body route-params]} request
Expand All @@ -63,8 +68,8 @@
{:ticket-type-id ticket-type-id
:tickets tickets})))]
(and
(s/valid? ::specs/event-id event-id)
(validate-req body ::specs/create-tickets-params on-success))))
(s/valid? specs/EventId event-id)
(validate-req body specs/CreateTicketsParams on-success))))

(defn reserve-ticket-handler [db-spec message-queue request]
(let [{:keys [cookies body route-params]} request
Expand All @@ -75,40 +80,40 @@
{:booking-id
(ticket/reserve-ticket db-spec message-queue uid event-id body)}))]
(and
(s/valid? ::specs/event-id event-id)
(validate-req body ::specs/reserve-tickets-params on-success))))
(s/valid? specs/EventId event-id)
(validate-req body specs/ReserveTicketsParams on-success))))

(defn post-payment-handler [message-queue request]
(let [booking-id (get-in request [:route-params :booking-id])
on-success (fn []
(booking/make-payment message-queue booking-id)
(respond-200
{:booking-id booking-id}))]
(validate-req booking-id ::specs/booking-id on-success)))
(validate-req booking-id specs/BookingId on-success)))

(defn cancel-booking-handler [message-queue request]
(let [booking-id (get-in request [:route-params :booking-id])
on-success (fn []
(booking/cancel-booking message-queue booking-id)
(respond-200 {:booking-id booking-id}))]
(validate-req booking-id ::specs/booking-id on-success)))
(validate-req booking-id specs/BookingId on-success)))

(defn get-booking-status-handler [db-spec request]
(let [booking-id (get-in request [:route-params :booking-id])
on-success (fn []
(respond-200
{:booking-status
(booking/get-booking-status db-spec booking-id)}))]
(validate-req booking-id ::specs/booking-id on-success)))
(validate-req booking-id specs/BookingId on-success)))

(defn get-tickets-handler [db-spec request]
(let [ticket-type-id (get-in request [:query-params "ticket_type_id"])
on-success (fn []
(respond-200 (ticket/get-tickets db-spec ticket-type-id)))]
(validate-req ticket-type-id ::specs/ticket-type-id on-success)))
(validate-req ticket-type-id specs/TicketTypeId on-success)))

(defn get-tickets-by-booking-id-handler [db-spec request]
(let [booking-id (get-in request [:route-params :booking-id])
on-success (fn []
(respond-200 (ticket/get-tickets-by-booking-id db-spec booking-id)))]
(validate-req booking-id ::specs/booking-id on-success)))
(validate-req booking-id specs/BookingId on-success)))
99 changes: 62 additions & 37 deletions swift-ticketing/src/swift_ticketing/specs.clj
Original file line number Diff line number Diff line change
@@ -1,51 +1,76 @@
(ns swift-ticketing.specs
(:require [clojure.spec.alpha :as s]))
(:require
[malli.core :as m]
[malli.registry :as mr]))

(defn date? [date]
(let [date-regex #"\d{4}-\d{2}-\d{2}"]
(boolean
(and (string? date) (re-matches date-regex date)))))
(boolean
(and (string? date) (re-matches date-regex date)))))

(defn uuid? [x]
(defn string-uuid? [x]
(try
(java.util.UUID/fromString x)
(catch Exception e false)))
(catch Exception _ false)))

(defn nilable [pred]
(fn [x] (or (nil? x) (pred x))))
(def non-empty-string
(m/schema [:string {:min 1}]))

(s/def ::venue (nilable string?))
(s/def ::to (nilable date?))
(s/def ::from (nilable date?))
(mr/set-default-registry!
(mr/composite-registry
(m/default-schemas)
{:date (m/-simple-schema {:type :date
:pred date?
:type-properties {:error/message
"expected format yyyy-MM-dd"}})
:non-empty-string non-empty-string
:string-uuid (m/-simple-schema {:type :string-uuid
:pred string-uuid?
:type-properties {:error/message
"should be a uuid"}})}))

(s/def ::name string?)
(s/def ::description string?)
(s/def ::date date?)
(def GetEventsParams
[:map
[:venue {:optional true} :non-empty-string]
[:from {:optional true} :date]
[:to {:optional true} :date]])

(s/def ::event-id uuid?)
(s/def ::booking-id uuid?)
(s/def ::ticket-type-id uuid?)
(def CreateEventParams
[:map
[:name :non-empty-string
:description :non-empty-string
:date :date
:venue :non-empty-string]])

(s/def ::ticket_type string?)
(s/def ::seat_type string?)
(s/def ::ticket_type_id uuid?)
(s/def ::quantity int?)
(s/def ::price int?)
(s/def ::reservation_limit_in_seconds int?)
(s/def ::seats vector?)
(s/def ::ticket_ids vector?)
(def CreateTicketsParams
(let [seat-schema
[:map
[:name :non-empty-string]]]
[:multi {:dispatch :seat-type}
["General"
[:map
[:ticket-type :non-empty-string]
[:seat-type :non-empty-string]
[:description :string]
[:quantity pos-int?]
[:price pos?]]]
["Named"
[:map
[:ticket-type :non-empty-string]
[:seat-type :non-empty-string]
[:description :string]
[:seats seat-schema]
[:reservation-limit-in-seconds nat-int?]
[:price pos?]]]]))

(s/def ::get-events-params
(s/keys :opt-un [::venue ::to ::from]))
(def ReserveTicketsParams
[:or
[:map
[:ticket-ids string?]]
[:map
[:ticket-type-id :string-uuid]
[:quantity pos-int?]]])

(s/def ::create-event-params
(s/keys :req-un [::name ::description ::date ::venue]))

(s/def ::create-tickets-params
(s/keys :req-un [(or (and ::ticket_type ::seat_type ::description ::quantity ::price)
(and ::ticket_type ::seat_type ::description ::seats ::reservation_limit_in_seconds ::price))]))

(s/def ::reserve-tickets-params
(s/keys :req-un [(or (and ::quantity ::ticket_type_id) ::ticket_ids)]))

; (s/valid? ::create-event-params {:name "a"})
(def EventId :string-uuid)
(def BookingId :string-uuid)
(def TicketTypeId :string-uuid)

0 comments on commit f0c1697

Please sign in to comment.