I am using swagger to provide an API for a db access program. During development I normally have 2 versions running, the dev version and the prod version that I automatically start on login. I want to have a different title visible on the swagger front page so I don't accidentally trash my live database. So far I have been hand editing the title field in the swagger setup, but this is error prone, I often forget to change it before I run lein uberjar
to build the prod version.
The env setup seems like an ideal way to do this. The luminus lein template already uses an env map that is built from dev and prod config files which works fine, allowing me to automatically specify different ports for the 2 builds. I added an entry to these that gives me a title that is different in prod and dev versions. I can see it from the repl, but including it in the swagger specification just gives null
.
This is the :swagger definition from the start of my photo-api.routes.services.clj file:
(ns photo-api.routes.services
(:require [cheshire.core :as json]
[compojure.api.sweet :refer :all]
[image-lib.images :as ilim]
[image-lib.preferences :as ilpf]
[image-lib.projects :as ilpr]
[image-lib.write :as ilwr]
[photo-api.db.core :as db]
[photo-api.config :refer [env]]
[photo-api.routes.helpers.build :as build]
[photo-api.routes.helpers.keywords :as keywords]
[photo-api.routes.helpers.open :as open]
[photo-api.routes.helpers.photos :as photos]
[photo-api.routes.helpers.projects :as projects]
[ring.util.codec :refer [url-decode]]
[ring.util.http-response :refer [ok]]
[schema.core :as s]
[clojure.string :as str]))
(defapi service-routes
{:swagger {:ui "/swagger-ui"
:spec "/swagger.json"
:data
{:info
{:version "1.0.1"
;; Switch to correct title before lein uberjar
;; TODO Automate this so swagger page always shows dev or prod version
;;:title "Photo API"
:title (:title env)
:description "Access a mongo database containing details of photos"}}}}
The commented out :title specification works fine, but the (:title env)
call doesn't although it is the exact same call I can successfully use from the repl. I believe the env map is built as part of photo-api.config and from the startup messages when I start the server it looks like this is being successfully started before the http-server:
{:started
["#'photo-api.config/env"
"#'photo-api.db.core/db*"
"#'photo-api.db.core/db"
"#'photo-api.handler/init-app"
"#'photo-api.handler/app"
"#'photo-api.core/http-server"]}
user>
This is photo-api.config, unchanged from the luminus default:
(ns photo-api.config
(:require [cprop.core :refer [load-config]]
[cprop.source :as source]
[mount.core :refer [args defstate]]))
(defstate env :start (load-config
:merge
[(args)
(source/from-system-props)
(source/from-env)]))
and the dev config.edn file:
{:title "**** Photos Development API ****"
:dev true
:port 31999
;; when :nrepl-port is set the application starts the nREPL server on load
:nrepl-port 57251}
Am I missing something obvious here? Is there another step necessary to make the env map visible to the swagger setup?
EDIT:
Changing the call from (:title env)
to (env :title) causes cider-jack-in to fail with a long error message/stack trace that includes:
Caused by: java.lang.ClassCastException: mount.core.DerefableState cannot be cast to clojure.lang.IFn
Changing it again to (@env :title)
then gives a similarly long error message/stack trace that contains:
Caused by: java.lang.ClassCastException: mount.core.NotStartedState cannot be cast to clojure.lang.IFn, compiling:(services.clj:29:23)
So it looks like env is not being started till after the call to it from the swagger setup. I still have no idea why as the when cider-jack-in did work it clearly showed the config.env state starting before the http-server. (see above)
It looks like
(defstate env)
is an atom thatneeds to bederef
ed. Mount's README points to the tests to for some examples .You might try
(:title @env)
inservice-routes
EDIT --
Not an atom. See this issue for the same deref error, which, presumably means that no deref is needed.