CMR integration with external services1.5.0A CMR connector service that provides an inter-service API dependencies
| (this space intentionally left almost blank) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
namespaces
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This namespace defines the REST API handlers for collection resources. | (ns cmr.opendap.app.handler.collection (:require [cheshire.core :as json] [clojure.core.async :as async] [clojure.java.io :as io] [cmr.authz.token :as token] [cmr.exchange.query.core :as base-query] [cmr.exchange.query.core :as query] [cmr.http.kit.request :as request] [cmr.http.kit.response :as response] [cmr.opendap.components.config :as config] [cmr.ous.util.http.request :as ous-reqeust] [org.httpkit.server :as server] [org.httpkit.timer :as timer] [taoensso.timbre :as log])) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Service Bridge Handlers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-service-endpoint
[component destination]
(case destination
:cmr (config/get-cmr-search-endpoint component)
:giovanni (config/get-giovanni-endpoint component)
:edsc (config/get-edsc-endpoint component))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn params->service-url
[component destination data]
(let [service-endpoint (get-service-endpoint component destination)]
(format "%s?%s" service-endpoint
(-> data
(query/parse {:destination destination})
base-query/->query-string)))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Here's what we expect a request might look like: https://this-service/service-bridge/collection/c123? destination=giovanni& bounding-box=[...]& temporal=[...]& variables=V123,V234,V345& granules=G123 | (defn bridge-services
[component]
(fn [request]
(let [user-token (token/extract request)
;; TODO: Destination will not be found in path-params.
;; We need to retrieve this from the raw-params.
{:keys [concept-id destination]} (:path-params request)
api-version (ous-reqeust/accept-api-version component request)
data (-> request
:params
(merge {:collection-id concept-id}))]
(log/warnf "Handling bridge request from %s ..." (:referer request))
(log/warnf "Bridging service to %s ..." destination)
(response/json {
:service {
:url (params->service-url component destination data)}})))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This namespace defines the handlers for general resources. Simple handlers will only need to make a call to a library and then have that data prepared for the client by standard response function. More complex handlers will need to perform additional tasks. For example, in order of increasing complexity: * utilize non-default, non-trivial response functions * operate on the obtained data with various transformations, including extracting form data, query strings, etc. * take advantage of middleware functions that encapsulate complicated business logic | (ns cmr.opendap.app.handler.core (:require [clojure.java.io :as io] [cmr.opendap.health :as health] [cmr.http.kit.response :as response] [taoensso.timbre :as log])) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Admin Handlers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn health
[component]
(fn [request]
(->> component
health/components-ok?
(response/json request)))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def ping
(fn [request]
(response/json request {:result :pong}))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Utility Handlers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn text-file
[filepath]
(fn [request]
(if-let [file-resource (io/resource filepath)]
(response/text request (slurp file-resource))))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn html-file
[filepath]
(fn [request]
(if-let [file-resource (io/resource filepath)]
(response/html request (slurp file-resource))))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Custom ring middleware for CMR OPeNDAP. | (ns cmr.opendap.app.middleware (:require [clojusc.twig :refer [pprint]] [cmr.ous.util.http.request :as request] [cmr.http.kit.response :as response] [cmr.metadata.proxy.components.auth :as auth] [reitit.ring :as ring] [taoensso.timbre :as log])) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ring-based middleware for supporting the protection of routes using the CMR Access Control service and CMR Legacy ECHO support. In particular, this wrapper allows for the protection of routes by both roles as well as concept-specific permissions. This is done by annotating the routes per the means described in the reitit library's documentation. | (defn wrap-auth
[handler system]
(fn [req]
(log/debug "Running perms middleware ...")
(auth/check-route-access system handler req))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn reitit-auth
[system]
"This auth middleware is specific to reitit, providing the data structure
necessary that will allow for the extraction of roles and permissions
settings from the request.
For more details, see the docstring above for `wrap-auth`."
{:data
{:middleware [#(wrap-auth % system)]}}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn wrap-api-version-dispatch
([site-routes route-data system]
(wrap-api-version-dispatch
site-routes route-data system (reitit-auth system)))
([site-routes {:keys [main-api-routes-fn plugins-api-routes-fns]} system opts]
(fn [req]
(let [api-version (request/accept-api-version system req)
plugins-api-routes (vec (mapcat #(% system api-version) plugins-api-routes-fns))
api-routes (vec (main-api-routes-fn system api-version))
routes (concat (vec site-routes) plugins-api-routes api-routes)
handler (ring/ring-handler (ring/router routes opts))]
(log/debug "API version:" api-version)
(log/debug "Made routes:" (pprint routes))
(response/version-media-type
(handler req)
(request/accept-media-type-format system req)))))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.opendap.app.core (:require [cmr.http.kit.app.core :as base-app] [cmr.http.kit.app.middleware :as base-middleware] [cmr.opendap.app.middleware :as middleware] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CMR Service Bridge is a framework that supports the addition of new features via plugins. As a framework, it offers a limited number of routes and handlers, but it is the individual plugins that provide the functionality. As you can see below, site-routes are extracted from the route-data; the reason we do not extract api-routes (i.e. REST resources), is because api-routes are versioned. As such, a different version of the route may be required at request time. To satisfy that condition, we build the routes dynamically inside the middleware that handles the API versioning. Lastly, a whole series of middleware (both community-provided and CMR-created) are applied after we perform versioning dispatch. These may be viewed in the cmr-http-kit project. | (defn main
[httpd-component]
(log/trace "httpd-component keys:" (keys httpd-component))
(let [{site-routes :site-routes :as route-data}
(base-app/collected-routes httpd-component)]
(-> site-routes
;; initial routes and middleware are reitit-based
(middleware/wrap-api-version-dispatch route-data httpd-component)
;; the wrap-api-version-dispatch makes a call which converts the
;; reitit-based middleware/routes to ring; from here on out, all
;; middleware is ring-based
(base-middleware/wrap-ring-middleware httpd-component)))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This namespace defines the REST routes provided by this service. Upon idnetifying a particular request as matching a given route, work is then handed off to the relevant request handler function. | (ns cmr.opendap.app.routes.site (:require [cmr.http.kit.app.handler :as base-handler] [cmr.http.kit.site.pages :as base-pages] [cmr.opendap.app.handler.core :as core-handler] [cmr.opendap.components.config :as config] [cmr.opendap.health :as health] [cmr.opendap.site.pages :as pages] [reitit.ring :as ring] [taoensso.timbre :as log])) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CMR OPeNDAP Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn main
[httpd-component]
[["/service-bridge" {
:get (base-handler/dynamic-page
httpd-component
pages/home
{:base-url (config/opendap-url httpd-component)})
:head base-handler/ok}]]) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Note that these routes only cover part of the docs; the rest are supplied via static content from specific directories (done in middleware). | (defn docs
[httpd-component]
[["/service-bridge/docs" {
:get (base-handler/dynamic-page
httpd-component
pages/opendap-docs
{:base-url (config/opendap-url httpd-component)})}]]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Static & Redirect Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn redirects
[httpd-component]
[["/service-bridge/robots.txt" {
:get (base-handler/permanent-redirect
(str (config/get-search-url httpd-component)
"/robots.txt"))}]]) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn static
[httpd-component]
[;; Google verification files
["/service-bridge/googled099d52314962514.html" {
:get (core-handler/text-file
"public/verifications/googled099d52314962514.html")}]]) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Assembled Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn all [httpd-component] (concat (main httpd-component) (docs httpd-component) (redirects httpd-component) (static httpd-component))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This namespace defines the Version 2.1 REST routes provided by this service. Upon idnetifying a particular request as matching a given route, work is then handed off to the relevant request handler function. | (ns cmr.opendap.app.routes.rest.v2-1 (:require [cmr.opendap.app.handler.collection :as collection-handler] [cmr.opendap.app.routes.rest.v1 :as routes-v1] [cmr.opendap.app.routes.rest.v2 :as routes-v2] [taoensso.timbre :as log])) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
REST API Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn service-bridge-api
[httpd-component]
[["/service-bridge/crossover/collection/:concept-id" {
:get {:handler (collection-handler/bridge-services httpd-component)
:permissions #{:read}}}]]) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Assembled Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Note that, since this uses the JAR-file plugin, routes are also pulled in implicitly. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn all [httpd-component] (concat (service-bridge-api httpd-component) (routes-v2/all httpd-component))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This namespace defines the Version 1 REST routes provided by this service. Upon idnetifying a particular request as matching a given route, work is then handed off to the relevant request handler function. | (ns cmr.opendap.app.routes.rest.v1 (:require [cmr.opendap.app.handler.collection :as collection-handler] [cmr.opendap.app.handler.core :as core-handler] [cmr.http.kit.app.handler :as base-handler] [cmr.opendap.health :as health] [taoensso.timbre :as log])) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
REST API Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn admin-api
[httpd-component]
[["/service-bridge/health" {
:get (core-handler/health httpd-component)
:options base-handler/ok}]
["/service-bridge/ping" {
:get {:handler core-handler/ping
:roles #{:admin}}
:post {:handler core-handler/ping
:roles #{:admin}}
:options base-handler/ok}]]) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Testing Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def testing
[["/testing/401" {:get (base-handler/status :unauthorized)}]
["/testing/403" {:get (base-handler/status :forbidden)}]
["/testing/404" {:get (base-handler/status :not-found)}]
["/testing/405" {:get (base-handler/status :method-not-allowed)}]
["/testing/500" {:get (base-handler/status :internal-server-error)}]
["/testing/503" {:get (base-handler/status :service-unavailable)}]]) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Assembled Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn all [httpd-component] (concat (admin-api httpd-component) testing)) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This namespace defines the Version 2 REST routes provided by this service. Upon idnetifying a particular request as matching a given route, work is then handed off to the relevant request handler function. | (ns cmr.opendap.app.routes.rest.v2 (:require [cmr.opendap.app.routes.rest.v1 :as routes-v1] [taoensso.timbre :as log])) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
REST API Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
These routes are now provided by the OUS service-bridge plugin. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Assembled Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn all
[httpd-component]
(concat
(routes-v1/admin-api httpd-component)
routes-v1/testing)) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.opendap.app.routes.rest.core (:require [cmr.opendap.app.routes.rest.v1 :as v1] [cmr.opendap.app.routes.rest.v2 :as v2] [cmr.opendap.app.routes.rest.v2-1 :as v2-1] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
REST API version summary: * v1 - the first implementation of the REST API * v2 - changes to the admin API (in particular, how caching is managed) * v2.1 - changes in the behaviour of how URLs are generated, with non-gridded granules having any spatial subsetting requests stripped. | (defn all
[httpd-component version]
(case (keyword version)
:v1 (v1/all httpd-component)
:v2 (v2/all httpd-component)
:v2.1 (v2-1/all httpd-component)
(v2-1/all httpd-component))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.opendap.core (:require [clojusc.twig :as logger] [cmr.opendap.components.core :as components] [com.stuartsierra.component :as component] [trifl.java :as trifl]) (:gen-class)) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(logger/set-level! '[cmr.opendap] :info logger/no-color-log-formatter) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn -main
[& args]
(let [system (components/init)]
(component/start system)
(trifl/add-shutdown-handler #(component/stop system)))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.opendap.testing.util (:require [cheshire.core :as json] [clojure.java.io :as io] [clojure.string :as string] [cmr.http.kit.request :as request] [cmr.ous.util.http.request :as ous-request]) (:import (clojure.lang Keyword))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def vendor "cmr-service-bridge.") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn override-api-version-header
([version]
(override-api-version-header {} version))
([req version]
(request/add-header req
"Accept"
(format ous-request/version-format
vendor
version
"json")))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn parse-response
[response]
(try
(let [data (json/parse-string (:body response) true)]
(cond
(not (nil? (:items data)))
(:items data)
:else data))
(catch Exception e
{:error {:msg "Couldn't parse body."
:body (:body response)}}))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn create-json-payload
[data]
{:body (json/generate-string data)}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn create-json-stream-payload
[data]
{:body (io/input-stream
(byte-array
(map (comp byte int)
(json/generate-string data))))}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-env-token
[^Keyword deployment]
(System/getenv (format "CMR_%s_TOKEN"
(string/upper-case (name deployment))))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def get-sit-token #(get-env-token :sit)) (def get-uat-token #(get-env-token :uat)) (def get-prod-token #(get-env-token :prod)) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.opendap.testing.system
(:require
[clojusc.system-manager.core :as system-api]
[clojusc.twig :as logger]
[cmr.opendap.components.config :as config]
[cmr.opendap.components.core])) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Setup and Constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def setup-options {
:init 'cmr.opendap.components.core/testing
:throw-errors true}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This is used to set the options and any other global data. This is defined in a function for re-use. For instance, when a REPL is reloaded, the options will be lost and need to be re-applied. | (defn init [] (logger/set-level! '[] :fatal) (system-api/setup-manager setup-options)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Convenience Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def manager #'system-api/manager) (def system #'system-api/system) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn http-port [] (config/http-port (system))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Test Fixtures ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Testing fixture for system and integration tests. | (defn with-system [test-fn] (init) (system-api/startup) (test-fn) (system-api/shutdown)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.opendap.testing.config
(:require
[clojusc.system-manager.core :as system-api]
[clojusc.twig :as logger]
[cmr.opendap.components.config :as config]
[cmr.opendap.components.core])) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Setup and Constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def setup-options {
:init 'cmr.opendap.components.core/testing-config-only
:throw-errors true}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn init [] "This is used to set the options and any other global data. This is defined in a function for re-use. For instance, when a REPL is reloaded, the options will be lost and need to be re-applied." (logger/set-level! '[] :fatal) (system-api/setup-manager setup-options)) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Convenience Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def system #'system-api/system) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Test Fixtures ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Testing fixture for simple system tests that only require access to the configuration component. | (defn with-system [test-fn] (init) (system-api/startup) (test-fn) (system-api/shutdown)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.opendap.components.core
(:require
[cmr.authz.components.caching :as auth-caching]
[cmr.exchange.common.components.config :as base-config]
[cmr.exchange.common.components.logging :as logging]
[cmr.http.kit.components.server :as httpd]
[cmr.metadata.proxy.components.auth :as auth]
[cmr.metadata.proxy.components.caching :as concept-caching]
[cmr.metadata.proxy.components.concept :as concept]
[cmr.mission-control.components.pubsub :as pubsub]
[cmr.opendap.config :as config-lib]
[cmr.ous.components.config :as config]
[cmr.plugin.jar.components.registry :as plugin-registry]
[com.stuartsierra.component :as component])) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Common Configuration Components ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn cfg
[]
{:config (base-config/create-component (config-lib/data))}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def log
{:logging (component/using
(logging/create-component)
[:config])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def reg
{:plugin (component/using
(plugin-registry/create-component)
[:config :logging])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def pubsub
{:pubsub (component/using
(pubsub/create-component)
[:config :logging])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def auth-cache
{:auth-caching (component/using
(auth-caching/create-component)
[:config :logging])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def authz
{:auth (component/using
(auth/create-component)
[:auth-caching :pubsub])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def concept-cache
{:concept-caching (component/using
(concept-caching/create-component)
[:config :logging])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def concepts
{:concepts (component/using
(concept/create-component)
[:concept-caching :pubsub])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn httpd
[cfg-data]
{:httpd (component/using
(httpd/create-component (config/http-port cfg-data))
[:config :logging :plugin
:pubsub :auth-caching :auth
:concept-caching :concepts])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Additional components for systems that want to supress logging (e.g., systems created for testing). | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def reg-without-logging
{:plugin (component/using
(plugin-registry/create-component)
[:config])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def pubsub-without-logging
{:pubsub (component/using
(pubsub/create-component)
[:config])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def auth-cache-without-logging
{:auth-caching (component/using
(auth-caching/create-component)
[:config])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def concept-cache-without-logging
{:concept-caching (component/using
(concept-caching/create-component)
[:config])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn httpd-without-logging
[cfg-data]
{:httpd (component/using
(httpd/create-component (config/http-port cfg-data))
[:config :plugin :pubsub
:auth-caching :auth
:concept-caching :concepts])}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Component Initializations ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn initialize-config-only [] (component/map->SystemMap (cfg))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn initialize-bare-bones
[]
(component/map->SystemMap
(merge (cfg)
log))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn initialize
[]
(let [cfg-data (initialize-bare-bones)]
(component/map->SystemMap
(merge cfg-data
reg
pubsub
auth-cache
authz
concept-cache
concepts
(httpd cfg-data))))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn initialize-without-logging
[]
(let [cfg-data (cfg)]
(component/map->SystemMap
(merge cfg-data
reg-without-logging
pubsub-without-logging
auth-cache-without-logging
authz
concept-cache-without-logging
concepts
(httpd-without-logging cfg-data))))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def init-lookup
{:basic #'initialize-bare-bones
:testing-config-only #'initialize-config-only
:testing #'initialize-without-logging
:main #'initialize}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn init
([]
(init :main))
([mode]
((mode init-lookup)))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def testing #(init :testing)) (def testing-config-only #(init :testing-config-only)) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.opendap.components.config (:require [cmr.authz.components.config :as authz-config] [cmr.exchange.common.components.config :as config] [cmr.http.kit.components.config :as httpd-config] [cmr.metadata.proxy.components.config :as metadata-config] [cmr.opendap.config :as config-lib] [cmr.ous.components.config :as ous-config] [com.stuartsierra.component :as component] [taoensso.timbre :as log]) (:import (clojure.lang Keyword))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Utility Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def get-cfg config/get-cfg) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Config Component API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
From the common config component | (def log-color? config/log-color?) (def log-level config/log-level) (def log-nss config/log-nss) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
From the authz config component | (def authz-cache-dumpfile #'authz-config/cache-dumpfile) (def authz-cache-init #'authz-config/cache-init) (def authz-cache-lru-threshold #'authz-config/cache-lru-threshold) (def authz-cache-ttl-ms #'authz-config/cache-ttl-ms) (def authz-cache-type #'authz-config/cache-type) (def get-access-control-url #'authz-config/get-access-control-url) (def get-echo-rest-url #'authz-config/get-echo-rest-url) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
From the metadata proxy config component | (def concept-cache-dumpfile #'metadata-config/concept-cache-dumpfile) (def concept-cache-init #'metadata-config/concept-cache-init) (def concept-cache-ttl-ms #'metadata-config/concept-cache-ttl-ms) (def cache-type #'metadata-config/cache-type) (def cmr-max-pagesize #'metadata-config/cmr-max-pagesize) (def concept-variable-version #'metadata-config/concept-variable-version) (def get-service #'metadata-config/get-service) (def cmr-base-url #'metadata-config/cmr-base-url) (def get-service-url #'metadata-config/get-service-url) (def get-cmr-search-endpoint metadata-config/get-search-url) (def get-ingest-url metadata-config/get-ingest-url) (def get-search-url metadata-config/get-search-url) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
From the HTTPD config component | (def http-entry-point-fn httpd-config/http-entry-point-fn) (def http-assets httpd-config/http-assets) (def http-docs httpd-config/http-docs) (def http-index-dirs httpd-config/http-index-dirs) (def http-replace-base-url httpd-config/http-replace-base-url) (def http-rest-docs-base-url-template httpd-config/http-rest-docs-base-url-template) (def http-rest-docs-outdir httpd-config/http-rest-docs-outdir) (def http-rest-docs-source httpd-config/http-rest-docs-source) (def http-skip-static httpd-config/http-skip-static) (def streaming-heartbeat httpd-config/streaming-heartbeat) (def streaming-timeout httpd-config/streaming-timeout) (def api-routes httpd-config/api-routes) (def site-routes httpd-config/site-routes) (def default-page-title httpd-config/default-page-title) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
From the OUS plugin config component | (def opendap-base-url ous-config/opendap-base-url) (def opendap-url ous-config/opendap-url) (def get-edsc-endpoint ous-config/get-edsc-endpoint) (def get-giovanni-endpoint ous-config/get-giovanni-endpoint) (def get-opendap-url ous-config/get-opendap-url) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Overrides of the HTTPD config component | (defn http-port
[system]
(or (get-in (get-cfg system) [:cmr :opendap :port])
(httpd-config/http-port system))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn http-base-url
[system]
(or (get-in (get-cfg system) [:cmr :opendap :relative :root :url])
(httpd-config/http-base-url system))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn vendor [system] (:vendor (get-cfg system))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn api-version [system] (:api-version (get-cfg system))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn api-version-dotted [system] (str "." (api-version system))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn default-content-type [system] (:default-content-type (get-cfg system))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Component Lifecycle Implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Implemented in cmr.exchange.common.components.config | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Component Constructor ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Implemented in cmr.exchange.common.components.config | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.opendap.const) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
XXX We should move these to configuration; this would mean that anything that requires these values would need access to the 'config' component thus also requiring that the calling function has access to the system component ... | (def client-id "cmr-service-bridge") (def user-agent "CMR Service-Bridge Service/1.0 (+https://github.com/cmr-exchange/cmr-service-bridge)") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The functions of this namespace are specifically responsible for returning ready-to-serve pages. | (ns cmr.opendap.site.pages (:require [cmr.http.kit.site.data :as data] [cmr.http.kit.site.pages :as pages] [taoensso.timbre :as log])) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HTML page-genereating functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Prepare the home page template. | (defn home [system request data] (pages/render-html "templates/opendap-home.html" (data/base-dynamic system data))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Prepare the top-level search docs page. | (defn opendap-docs [system request data] (log/debug "Calling opendap-docs page ...") (pages/render-html "templates/opendap-docs.html" (data/base-dynamic system data))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The functions of this namespace are specifically responsible for generating the static resources of the top-level and site pages and sitemaps. | (ns cmr.opendap.site.static (:require [clojure.java.io :as io] [clojusc.twig :as logger] [cmr.opendap.components.config :as config] [cmr.opendap.components.core :as components] [com.stuartsierra.component :as component] [markdown.core :as markdown] [selmer.parser :as selmer] [taoensso.timbre :as log] [trifl.java :as trifl]) (:gen-class)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(logger/set-level! '[cmr.opendap] :info) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Utility Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This is the function used by default to render templates, given data that the template needs to render. | (defn generate
[target template-file data]
(log/debug "Rendering data from template to:" target)
(log/debug "Template:" template-file)
(log/debug "Data:" data)
(io/make-parents target)
(->> data
(selmer/render-file template-file)
(spit target))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Content Generators ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Generate the HTML for the CMR OPeNDAP REST API docs page. | (defn generate-rest-api-docs
[docs-source docs-dir base-url]
(generate
(format "%s/index.html" docs-dir)
"templates/opendap-docs-static.html"
{:base-url base-url
:page-content (markdown/md-to-html-string (slurp docs-source))})) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
A convenience function that pulls together all the static content generators in this namespace. This is the function that should be called in the parent static generator namespace. | (defn generate-all [docs-source docs-dir base-url] (log/debug "Generating static site files ...")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn -main
[& args]
(let [system-init (components/init :basic)
system (component/start system-init)]
(trifl/add-shutdown-handler #(component/stop system))
(generate-all
(config/http-rest-docs-source system)
(config/http-rest-docs-outdir system)
(config/http-rest-docs-base-url-template system)))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.opendap.health) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn has-data?
[x]
(if (nil? x)
false
true)) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn config-ok? [component] (has-data? (:config component))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn logging-ok? [component] (has-data? (:logging component))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn components-ok?
[component]
{:config {:ok? (config-ok? component)}
:httpd {:ok? true}
:logging {:ok? (logging-ok? component)}}) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.opendap.config
(:require
[clojure.string :as string]
[cmr.exchange.common.file :as file]
[cmr.exchange.common.util :as util]
[cmr.metadata.proxy.config :as config]
[environ.core :as environ]
[taoensso.timbre :as log])
(:import
(clojure.lang Keyword))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(def config-file "config/cmr-opendap/config.edn") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn base-data
([]
(base-data config-file))
([filename]
(file/read-edn-resource filename))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(defn data
[]
(util/deep-merge (base-data)
(config/props-data)
(config/env-data))) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||