Last active
July 9, 2018 10:18
-
-
Save arifian/288fda0820813da5b3c90122925c2d2f to your computer and use it in GitHub Desktop.
pedestal - component basic building block
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns app.pedestal.component | |
"Connection between the Component framework and the Pedestal web application server. | |
inspired by: https://github.com/stuartsierra/component.pedestal/commit/9ee90960b86f84c02ec59848c6f34bd977c5e8d0" | |
(:require [com.stuartsierra.component :as component] | |
[io.pedestal.http :as http] | |
[io.pedestal.http.cors :as cors] | |
[io.pedestal.http.impl.servlet-interceptor :as servlet-interceptor] | |
[io.pedestal.interceptor :refer [interceptor interceptor-name]])) | |
(defn insert-context-interceptor | |
"Returns an interceptor which associates key with value in the | |
Pedestal context map." | |
[& kv] | |
(interceptor | |
{:name ::insert-context | |
:enter (fn [context] | |
(apply assoc context kv))})) | |
(defn add-component-interceptor | |
"Adds an interceptor to the pedestal-config map which associates the | |
pedestal-component into the Pedestal context map. Must be called | |
before io.pedestal.http/create-server." | |
[pedestal-config pedestal-component] | |
(update pedestal-config | |
::http/interceptors | |
conj | |
(apply | |
insert-context-interceptor | |
(-> pedestal-component | |
seq flatten)))) | |
(defrecord Pedestal [pedestal-config-fn pedestal-server config] | |
component/Lifecycle | |
(start [this] | |
(if pedestal-server | |
this | |
(assoc this :pedestal-server | |
(-> (pedestal-config-fn config) | |
(add-component-interceptor this) | |
(http/create-server (constantly nil)) ; no-op init-fn | |
http/start)))) | |
(stop [this] | |
(when pedestal-server | |
(http/stop pedestal-server)) | |
(assoc this :pedestal-server nil))) | |
;; (defn- get-pedestal | |
;; [context] | |
;; (let [pedestal (get context ::pedestal-component ::not-found)] | |
;; (when (nil? pedestal) | |
;; (throw (ex-info (str "Pedestal component was nil in context map; " | |
;; "component.pedestal is not configured correctly") | |
;; {:reason ::nil-pedestal | |
;; :context context}))) | |
;; (when (= ::not-found pedestal) | |
;; (throw (ex-info (str "Pedestal component was missing from context map; " | |
;; "component.pedestal is not configured correctly") | |
;; {:reason ::missing-pedestal | |
;; :context context}))) | |
;; pedestal)) | |
;; (defn context-component | |
;; "Returns the component at key from the Pedestal context map. key | |
;; must have been a declared dependency of the Pedestal server | |
;; component." | |
;; [context key] | |
;; (let [component (get (get-pedestal context) key ::not-found)] | |
;; (when (nil? component) | |
;; (throw (ex-info (str "Component " key " was nil in Pedestal dependencies; " | |
;; "maybe it returned nil from start or stop") | |
;; {:reason ::nil-component | |
;; :dependency-key key | |
;; :context context}))) | |
;; (when (= ::not-found component) | |
;; (throw (ex-info (str "Missing component " key " from Pedestal dependencies") | |
;; {:reason ::missing-dependency | |
;; :dependency-key key | |
;; :context context}))) | |
;; component)) | |
;; (defn component-handler | |
;; "Returns a Pedestal interceptor which extracts the component named | |
;; key from the context map. The key must have been declared a | |
;; dependency of the Pedestal server component. | |
;; Invokes f with two arguments, the component and the Ring-style | |
;; request map. f should return a Ring-style response map. | |
;; You can use this to replace Ring-style handler functions with | |
;; functions that take both a component and a request." | |
;; ([key f] (component-handler nil key f)) | |
;; ([name key f] | |
;; (interceptor | |
;; {:name (interceptor-name name) | |
;; :enter (fn [context] | |
;; (let [c (context-component context key)] | |
;; (assoc context :response (f c (:request context)))))}))) | |
;; (defn using-component | |
;; "Returns an interceptor which associates the component named key | |
;; into the Ring-style request map as :component. The key must have | |
;; been declared a dependency of the Pedestal server component. | |
;; You can add this interceptor to your Pedestal routes to make the | |
;; component available to your Ring-style handler functions, which can | |
;; get :component from the request map." | |
;; [key] | |
;; (interceptor | |
;; {:name ::using-component | |
;; :enter (fn [context] | |
;; (assoc-in context [:request ::component] | |
;; (context-component context key)))})) | |
;; (defn use-component | |
;; "Returns the component added to the request map by | |
;; 'using-component'." | |
;; [request] | |
;; (::component request)) | |
(defn pedestal | |
"Returns a new instance of the Pedestal server component. | |
pedestal-config-fn is a no-argument function which returns the | |
Pedestal server configuration map, which will be passed to | |
io.pedestal.http/create-server. If you want the default | |
interceptors, you must call io.pedestal.http/default-interceptors | |
in pedestal-config-fn. | |
The Pedestal component should have dependencies (as by | |
com.stuartsierra.component/using or system-using) on all components | |
needed by your web application. These dependencies will be available | |
in the Pedestal context map via 'context-component'. | |
You can make components available to your handler functions with | |
'using-component' or 'component-handler'." | |
([pedestal-config-fn config] | |
(->Pedestal pedestal-config-fn nil config))) | |
;; (defrecord DevPedestal [pedestal-config-fn pedestal-server config] | |
;; component/Lifecycle | |
;; (start [this] | |
;; (if pedestal-server | |
;; this | |
;; (assoc this :pedestal-server | |
;; (-> (pedestal-config-fn config) | |
;; (add-component-interceptor this) | |
;; ;; for development mode | |
;; (update-in [::http/interceptors] | |
;; (fn [xs] | |
;; (vec (->> xs | |
;; (cons cors/dev-allow-origin) | |
;; (cons servlet-interceptor/exception-debug))))) | |
;; (http/create-server (constantly nil)) ; no-op init-fn | |
;; http/start)))) | |
;; (stop [this] | |
;; (when pedestal-server | |
;; (http/stop pedestal-server)) | |
;; (assoc this :pedestal-server nil))) | |
;; (defn dev-pedestal | |
;; "Returns a new instance of the Pedestal server component. | |
;; pedestal-config-fn is a no-argument function which returns the | |
;; Pedestal server configuration map, which will be passed to | |
;; io.pedestal.http/create-server. If you want the default | |
;; interceptors, you must call io.pedestal.http/default-interceptors | |
;; in pedestal-config-fn. | |
;; The Pedestal component should have dependencies (as by | |
;; com.stuartsierra.component/using or system-using) on all components | |
;; needed by your web application. These dependencies will be available | |
;; in the Pedestal context map via 'context-component'. | |
;; You can make components available to your handler functions with | |
;; 'using-component' or 'component-handler'." | |
;; ([pedestal-config-fn config] | |
;; (->DevPedestal pedestal-config-fn nil config))) | |
(ns app.pedestal.config | |
"Configuration integration, injecting routes into the system" | |
(:require | |
[app.web.routes :as webrout] | |
[app.utils :as utils] | |
[app.pedestal.interceptors :as ceptors] | |
[io.pedestal.http :as http] | |
[io.pedestal.http.route.definition.table :as table] | |
[io.pedestal.http.secure-headers :as http.sh] | |
[ring.middleware.session.cookie :as cookie])) | |
(defn pedestal-config | |
"Config specs" | |
[config] | |
{::http/host (-> config :pedestal :host) | |
::http/port (-> config :pedestal :port) | |
::http/type (-> config :pedestal :http-type) | |
::http/join? (-> config :pedestal :join?) | |
::http/resource-path (-> config :pedestal :resource-path) | |
::http/enable-session {:store | |
(cookie/cookie-store | |
{:key (->> config :cookie :key)}) | |
:cookie-name (->> config :cookie :name)} | |
::http/routes (app-routes config) | |
::http/secure-headers {:content-security-policy-settings | |
(http.sh/content-security-policy-header | |
"object-src 'none';")}}) | |
(ns app.system | |
(:require | |
[com.stuartsierra.component :as component] | |
[schema.core :as s] | |
[app.utils :refer :all] | |
[app.schema :refer [ConfigSchema]] | |
[app.db.datomic :as datomic] | |
[app.pedestal.component :as pcm] | |
[app.pedestal.config :as pcf])) | |
(defn init-production-system | |
"Initialize production system" | |
[config] | |
(component/system-map | |
:config (s/validate ConfigSchema config) | |
;; :mycomponent {:my :component} | |
:datomic (component/using | |
(datomic/make (-> config :datomic-prod)) | |
[:config]) | |
:pedestal (component/using | |
(pcm/pedestal pcf/pedestal-config-fn config) | |
[:datomic]))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment