Skip to content

Bump v0.1.0 #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,4 @@ output.calva-repl
/.clj-kondo
.envrc
redis
localstack

localstack
10 changes: 4 additions & 6 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
{:paths ["resources" "src"]
:deps {org.clojure/clojure {:mvn/version "1.11.1"}
org.clojure/core.async {:mvn/version "1.6.681"}
com.moclojer/moclojer {:mvn/version "0.3.2"}
com.moclojer/rq {:mvn/version "0.1.4"}
com.moclojer/moclojer {:mvn/version "0.3.4"}
com.moclojer/rq {:mvn/version "0.2.2"}
clj-http/clj-http {:mvn/version "3.12.3"}
com.zaxxer/HikariCP {:mvn/version "5.0.1"}
com.stuartsierra/component {:mvn/version "1.1.0"}
org.clojure/core.async {:mvn/version "1.6.681"}
com.taoensso/telemere {:mvn/version "1.0.0-beta25"}
com.taoensso/slf4j-telemere {:mvn/version "1.0.0-beta21"}
org.clojure/data.json {:mvn/version "2.5.0"}
aero/aero {:mvn/version "1.1.6"}
metosin/muuntaja {:mvn/version "0.6.8"}
metosin/malli {:mvn/version "0.11.0"}
metosin/reitit {:mvn/version "0.5.18"}
metosin/reitit-pedestal {:mvn/version "0.5.18"}
metosin/reitit-swagger {:mvn/version "0.5.18"}
metosin/reitit-swagger-ui {:mvn/version "0.5.18"}
ring/ring-jetty-adapter {:mvn/version "1.12.2"}
io.sentry/sentry {:mvn/version "7.6.0"}
io.sentry/sentry-clj {:mvn/version "7.6.215"}
com.cognitect.aws/api {:mvn/version "0.8.561"}
com.cognitect.aws/endpoints {:mvn/version "1.1.12.230"}
com.cognitect.aws/s3 {:mvn/version "822.2.1145.0"}
org.postgresql/postgresql {:mvn/version "42.6.0"}
com.github.seancorfield/next.jdbc {:mvn/version "1.3.834"}
io.pedestal/pedestal.jetty {:mvn/version "0.5.10"}
io.pedestal/pedestal.service {:mvn/version "0.5.10"}
commons-io/commons-io {:mvn/version "2.16.1"}
commons-fileupload/commons-fileupload {:mvn/version "1.5"}
migratus/migratus {:mvn/version "1.5.1"}
Expand Down
1 change: 0 additions & 1 deletion docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ services:
- "6379:6379"
volumes:
- ./redis:/redis

# https://docs.localstack.cloud/user-guide/aws/s3/#s3-docker-image
localstack:
image: localstack/localstack:s3-latest
Expand Down
2 changes: 1 addition & 1 deletion src/com/moclojer/components/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

(defn new-router
[routes]
(router/map->Router {:router (router/router routes)}))
(router/map->Router {:routes routes}))

(defn new-sentry []
(sentry/map->Sentry {}))
Expand Down
5 changes: 2 additions & 3 deletions src/com/moclojer/components/moclojer.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
[com.moclojer.components.logs :as logs]
[com.moclojer.io-utils :as m.io-utils]
[com.moclojer.server :as server]
[com.stuartsierra.component :as component]
[io.pedestal.http :as http]))
[com.stuartsierra.component :as component]))

(defn moclojer-server! [{:keys [config-path join?]}]
(let [*router (m.adapters/generate-routes (m.io-utils/open-file config-path))]
Expand All @@ -28,5 +27,5 @@
(stop [this]
(let [stop-fn (-> this :moclojer :stop-future)]
(stop-fn)
(http/stop (-> this :moclojer :server))
(.stop (-> this :moclojer :server))
(assoc this :moclojer nil))))
1 change: 1 addition & 0 deletions src/com/moclojer/components/mq.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
(:require
[com.moclojer.components.logs :as logs]
[com.moclojer.components.sentry :as sentry]
[com.moclojer.rq.queue]
[com.moclojer.rq :as rq]
[com.moclojer.rq.pubsub :as rq-pubsub]
[com.stuartsierra.component :as component]))
Expand Down
147 changes: 75 additions & 72 deletions src/com/moclojer/components/router.clj
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
(ns com.moclojer.components.router
(:require
[clojure.string :as str]
[com.moclojer.components.logs :as logs]
[com.stuartsierra.component :as component]
[muuntaja.core :as m]
[reitit.coercion.malli :as reitit.malli]
[reitit.dev.pretty :as pretty]
[reitit.http :as http]
[reitit.http.coercion :as coercion]
[reitit.http.interceptors.exception :as exception]
[reitit.http.interceptors.multipart :as multipart]
[reitit.http.interceptors.muuntaja :as muuntaja]
[reitit.http.interceptors.parameters :as parameters]
[reitit.pedestal :as pedestal]
[reitit.ring :as ring]
[com.moclojer.components.sentry :as sentry]
[reitit.ring.coercion :as coercion]
[reitit.ring.middleware.multipart :as multipart]
[reitit.ring.middleware.muuntaja :as muuntaja]
[reitit.ring.middleware.parameters :as parameters]
[reitit.swagger :as swagger]
[reitit.swagger-ui :as swagger-ui]))

Expand All @@ -26,74 +23,80 @@
:error "failed to send sentry event"
{:event data-ex} sentry-ex)))))

(defn- coercion-error-handler [status]
(fn [exception request]
(logs/log :error "failed to coerce req/resp"
(logs/log :error "Failed to coerce request/response"
{:uri (:uri request)
:method (:request-method request)}
nil
exception))
(send-sentry-evt-from-req! request exception)
{:status status
:body (if (= 400 status)
(str "Invalid path or request parameters, with the following errors: "
(:errors (ex-data exception)))
"Error checking path or request parameters.")}))
(defn exception-middleware
[handler-fn]
(fn [request]
(try
(handler-fn request)
(catch Exception e
(logs/log :error (.getMessage e)
:ctx {:exception e})
(send-sentry-evt-from-req! request e)
{:status 500
:body (.getMessage e)}))))

(defn log-request-middleware
[handler-fn]
(fn [request]
(let [method (str/upper-case (name (:request-method request)))]
(logs/log :info (str method " " (:uri request))
:ctx {:method method
:host (:server-name request)
:uri (:uri request)
:query-string (:query-string request)})
(handler-fn request))))

(defn- exception-info-handler [exception request]
(logs/log :error "server exception"
nil nil exception)
(send-sentry-evt-from-req! request exception)
{:status 500
:body "Internal error."})
(defn cid-middleware
"Extends incoming request with CID if not given already"
[handler-fn]
(fn [request]
(->> {:cid (get-in request [:headers "cid"]
(:cid (logs/gen-ctx-with-cid)))}
(assoc request :ctx)
(handler-fn))))

(def router-settings
{;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs
;;:validate spec/validate ;; enable spec validation for route data
;;:reitit.spec/wrap spell/closed ;; strict top-level validation
:exception pretty/exception
(defn build-components-middleware
[components]
(fn [handler-fn]
(fn [request]
(handler-fn (assoc request :components components)))))

(defn build-router-settings
[components]
{:exception pretty/exception
:data {:coercion reitit.malli/coercion
:muuntaja (m/create
(-> m/default-options
(assoc-in [:formats "application/json" :decoder-opts :bigdecimals] true)))
:interceptors [;; swagger feature
swagger/swagger-feature
;; query-params & form-params
(parameters/parameters-interceptor)
;; content-negotiation
(muuntaja/format-negotiate-interceptor)
;; encoding response body
(muuntaja/format-response-interceptor)
;; exception handling
(exception/exception-interceptor
(merge
exception/default-handlers
{:reitit.coercion/request-coercion (coercion-error-handler 400)
:reitit.coercion/response-coercion (coercion-error-handler 500)
clojure.lang.ExceptionInfo exception-info-handler}))
;; decoding request body
(muuntaja/format-request-interceptor)
;; coercing response bodys
(coercion/coerce-response-interceptor)
;; coercing request parameters
(coercion/coerce-request-interceptor)
;; multipart
(multipart/multipart-interceptor)]}})

(defn router [routes]
(pedestal/routing-interceptor
(http/router routes router-settings)
;; optional default ring handler (if no routes have matched)
(ring/routes
(swagger-ui/create-swagger-ui-handler
{:path "/"
:config {:validatorUrl nil
:operationsSorter "alpha"}})
(ring/create-resource-handler)
(ring/create-default-handler))))
(assoc-in [:formats "application/json"
:decoder-opts :bigdecimals]
true)))
:middleware [(build-components-middleware components)
log-request-middleware
cid-middleware
exception-middleware
swagger/swagger-feature
parameters/parameters-middleware
muuntaja/format-negotiate-middleware
muuntaja/format-response-middleware
muuntaja/format-request-middleware
coercion/coerce-response-middleware
coercion/coerce-request-middleware
multipart/multipart-middleware]}})

(defrecord Router [router]
(defrecord Router [routes]
component/Lifecycle
(start [this] this)
(stop [this] this))
(start [this]
(assoc this :router
(ring/ring-handler
(ring/router
routes
(build-router-settings this))
(ring/routes
(swagger-ui/create-swagger-ui-handler
{:path "/"
:config {:validatorUrl nil
:operationsSorter "alpha"}})
(ring/create-resource-handler)
(ring/create-default-handler)))))
(stop [this]
(dissoc this :router)))
98 changes: 11 additions & 87 deletions src/com/moclojer/components/webserver.clj
Original file line number Diff line number Diff line change
@@ -1,99 +1,23 @@
(ns com.moclojer.components.webserver
(:require
[clojure.pprint :refer [pprint]]
[clojure.string :as str]
[ring.adapter.jetty :as jetty]
[com.moclojer.components.logs :as logs]
[com.stuartsierra.component :as component]
[io.pedestal.http :as server]
[io.pedestal.interceptor.helpers :refer [before on-request]]
[reitit.pedestal :as pedestal]))

(defn- add-system [service]
(before (fn [context] (assoc-in context [:request :components] service))))

(defn system-interceptors
"Extend to service's interceptors to include one to inject the components
into the request object"
[service-map service]
(update-in service-map
[::server/interceptors]
#(vec (->> % (cons (add-system service))))))

(defn cid-interceptor
"Extends incoming request with CID if not given already"
[service-map]
(update-in service-map
[::server/interceptors]
#(conj % (before
(fn [ctx]
(assoc-in ctx [:request :ctx]
{:cid (get-in ctx [:request :headers "cid"]
(:cid (logs/gen-ctx-with-cid)))}))))))

(def req-logger
(on-request
::log-request
(fn [req]
(logs/log
:info (str (str/lower-case (name (:request-method req))) " "
(:uri req))
(apply dissoc req [:body :components :servlet :servlet-request
:servlet-response]))
req)))

(defn base-service [port]
{::server/port port
::server/type :jetty
::server/host "0.0.0.0"
;; dev false to not lock repl/thread
::server/join? true
;; no pedestal routes
::server/request-logger req-logger
::server/routes []
;; allow serving the swagger-ui styles & scripts from self
::server/secure-headers {:content-security-policy-settings
{:default-src "'self'"
:style-src "'self' 'unsafe-inline'"
:script-src "'self' 'unsafe-inline'"
:img-src "'self' 'unsafe-inline' data: https://validator.swagger.io"}}})

(defn dev-init [service-map router]
(-> service-map
(merge {:env :dev
;; do not block thread that starts web server
::server/join? false
::server/allowed-origins {:creds true :allowed-origins (constantly true)}
;; Content Security Policy (CSP) is mostly turned off in dev mode
::server/secure-headers {:content-security-policy-settings {:object-src "none"}}})
;; Wire up interceptor chains
(server/default-interceptors)
(pedestal/replace-last-interceptor router)
(server/dev-interceptors)))

(defn prod-init [service-map router]
(-> service-map
(merge {:env :prod})
(server/default-interceptors)
(pedestal/replace-last-interceptor router)))
[com.stuartsierra.component :as component]))

(defrecord WebServer [config router]
component/Lifecycle
(start [this]
(let [{:webserver/keys [port]
:keys [env]} (:config config)
init-fn (if (= env :dev) dev-init prod-init)]
(logs/log :info "starting webserver config"
{:env env :port port})
(assoc this :webserver
(-> (base-service port)
(init-fn (:router router))
(cid-interceptor)
(system-interceptors this)
(server/create-server)
(server/start)))))
cfg {:port port
:host "0.0.0.0"
:join? true
:env env}]
(logs/log :info "starting webserver config" {:env env :port port})
(assoc this :webserver (jetty/run-jetty (:router router) cfg))))

(stop [this]
(logs/log :info "stopping webserver")
(server/stop (:webserver this))
(dissoc this :webserver)
this))
(when-let [srv (:webserver this)]
(.stop srv))
(dissoc this :webserver)))