Using the REPL

The common way of launching a REPL is from within an IDE. However, you can also launch a REPL directly from the terminal using the following command:

clj -M:dev

The advantage of running a REPL from the IDE is typically that you can send code from the IDE directly to the REPL to be evaluated.

Once started, it will look like this:

Clojure 1.11.3
user=>

To launch the server with the default configuration, do

user=> (start)

which will start a server and once started will display something like this:

2024-05-03T08:31:39.402Z jonas INFO [jobtech-taxonomy.api.core:33] - Started http://jonas:8080
{:started ["#'dev/http-server"]}

The user namespace is initialised from env/dev/clj/user, in particular the comment clauses towards the end describes how the functionality in the namespace is intended to be used.

(ns user
  {:clj-kondo/config '{:linters {:clojure-lsp/unused-public-var {:level :off}}}}
  (:require [clj-http.client :as client]
            [clojure.data.json :as cdj]
            [clojure.pprint :refer [pprint]]
            [clojure.spec.alpha :as s]
            [dev]
            [expound.alpha :as expound]
            [jobtech-taxonomy.api.config :refer [get-config transduce-backends]]
            [jobtech-taxonomy.api.core]
            [mount.core :as mount]
            [taoensso.timbre :as log]))

(alter-var-root #'s/*explain-out* (constantly expound/printer))

(defn add-taps []
  (add-tap (bound-fn* pprint)))

(def dev-cfg (atom (get-config [])))

(defn show-configs []
  (let [cfg @dev-cfg]
    {:available (mapv :id (:backends cfg))
     :active (:database-backend cfg)
     :multi (:compare-backends cfg)}))

(defn activate-config [cfg-id & cfg-ids]
  (swap! dev-cfg
         #(-> %
              (assoc :database-backend cfg-id)
              (dissoc  :compare-backends)
              (merge (if cfg-ids {:compare-backends (into [cfg-id] cfg-ids)} {}))))
  (show-configs))

(comment
  (load-config)
  (show-configs)
  (activate-config ':datahike-v.dev)
  @dev-cfg
  'bye)

(def api-root
  (str "http://localhost"
       (if-let [port (get-in @dev-cfg [:options :port])]
         (str ":" port "/")
         "/")))

(defn ^:export ppt []
  (pprint *1))

(defn ^:export get-raw-body [path]
  (-> (str api-root path)
      client/get
      :body))

(defn get-json-body [path]
  (-> (get-raw-body path)
      (cdj/read-str :key-fn keyword)))

(defn ^:export api-json []
  (get-json-body "v1/taxonomy/swagger.json"))

(defn ^:export tax-get [path]
  (get-json-body path))

(defn ^:export load-config
  ([] (reset! dev-cfg (get-config [])))
  ([cfg] (reset! dev-cfg cfg)))

(defn start []
  (reset! @#'mount/-args @dev-cfg)
  (mount/start-without #'dev/repl-server))

(defn stop []
  (mount/stop-except #'dev/repl-server))

(defn restart []
  (stop)
  (start))

(defn query-url [& re-parts]
  (->> (api-json)
       :paths
       (keep (fn [[k m]]
               (let [url (name k)]
                 (when (every? #(re-find % url) re-parts)
                   {:url (name k) :methods (keys m) :info m}))))
       vec))

(defn reduce-param [params]
  (->> (group-by :in params)
       (map (fn [[n g]]
              [n (mapv #(let [dissoc-falsy ((remove %) dissoc)]
                          (-> (dissoc % :in)
                              (dissoc-falsy :required)
                              (dissoc-falsy :deprecated))) g)]))))

(defn get-query [api-entry method]
  (when-let [method-info (get-in api-entry [:info method])]
    {:url (:url api-entry)
     :method method
     :summary (:summary method-info)
     :parameters (reduce-param (:parameters method-info))}))

(comment
  ;; This example demonstrates how you can start a short-lived server
  ;; to try things out. In this particular case, we attempt to reproduce
  ;; the datomic timeout reported in
  ;;
  ;; https://gitlab.com/arbetsformedlingen/taxonomy-dev/backend/jobtech-taxonomy-api/-/issues/652
  ;;
  #_{:clj-kondo/ignore [:unresolved-symbol]}
  (dev/with-http-server
    [{:keys [url]} (->> []
                        get-config
                        (transduce-backends (filter (comp #{:datomic} :type))))]
    (client/get
     (str url "/v1/taxonomy/suggesters/autocomplete?query-string=mjaoelimjaoelimjao&related-ids=i46j_HmG_v64&relation=narrower")
     {:accept :json}))

  )

(comment
  ;; Show what will be logged and how.
  (pprint log/*config*)
  ;; Possibly add taps.
  (add-taps)
  ;; Start by loading a config, any config, for the mount-system.
  ;; This is optional, but allows any config to be loaded.
  (load-config (get-config []))
  ;; Proceed by starting the mountable things, except for the REPL, since you are in it.
  (start)
  ;; Let's look at the Swagger API!
  ;; query-url takes some reg-exps that has to match the url
  (->> (query-url #"relation" #"changes" #"main")
       (keep #(get-query % :get))
       pprint)
  ;; (log/set-min-level! :debug)
  ;; Change the config. This particular one will break the system.
  (reset! @#'mount/-args {:options {:port 3000}})
  ;; Just to clear the air a bit.
  (restart)
  ;; Stop the system!
  (stop)
  'bye!
  )