Clojure Protocol Buffer Library0.1.0-SNAPSHOTA service and API for querying CMR metadata relationships dependencies
| (this space intentionally left almost blank) | ||||||||||||||||||||||||||||||||||||||||||||||||
namespaces
| |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.demo.movie (:require [clojure.string :as string] [clojurewerkz.neocons.rest :as nr] [clojurewerkz.neocons.rest.cypher :as cy] [cmr.graph.queries.neo4j.demo.movie :as query] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-graph ([conn] (get-graph conn 100)) ([conn limit] (when (string? limit) (get-graph conn (Integer/parseInt limit))) (let [result (cy/tquery conn query/graph {:limit limit}) nodes (map (fn [{:strs [cast movie]}] (concat [{:title movie :label :movie}] (map (fn [x] {:title x :label :actor}) cast))) result) nodes (distinct (apply concat nodes)) nodes-index (into {} (map-indexed #(vector %2 %1) nodes)) links (map (fn [{:strs [cast movie]}] (let [target (nodes-index {:title movie :label :movie})] (map (fn [x] {:target target :source (nodes-index {:title x :label :actor})}) cast))) result)] {:nodes nodes :links (flatten links)}))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn search [conn q] (if (string/blank? q) [] (let [result (cy/tquery conn query/search {:title (str "(?i).*" q ".*")})] (map (fn [x] {:movie (:data (x "movie"))}) result)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-movie [conn title] (log/trace "Got connection:" conn) (log/trace "Got title:" title) (let [[result] (cy/tquery conn query/title {:title title})] result)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.queries.neo4j.demo.movie) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def graph "MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) RETURN m.title as movie, collect(a.name) as cast LIMIT {limit};") | |||||||||||||||||||||||||||||||||||||||||||||||||
(def search "MATCH (movie:Movie) WHERE movie.title =~ {title} RETURN movie;") | |||||||||||||||||||||||||||||||||||||||||||||||||
(def title "MATCH (movie:Movie {title:{title}}) OPTIONAL MATCH (movie)<-[r]-(person:Person) RETURN movie.title as title, collect({name:person.name, job:head(split(lower(type(r)),'_')), role:r.roles}) as cast LIMIT 1;") | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.queries.neo4j.collections (:require [clojure.string :as string])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def reset "MATCH (n) DETACH DELETE n;") | |||||||||||||||||||||||||||||||||||||||||||||||||
(def get-all "MATCH (collection:Collection) RETURN collection;") (def delete-all "MATCH (collection:Collection) DELETE collection;") (def delete-all-cascade "MATCH (collection:Collection) DETACH DELETE collection;") | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-urls-by-concept-id [concept-id] (format "match (c:Collection)-[:LINKS_TO]->(u:Url) where c.conceptId='%s' return u.name;" concept-id)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-concept-ids-by-urls [urls] (format "match (c:Collection)-[:LINKS_TO]->(u:Url) where u.name in [%s] return c.conceptId;" (string/join "," (map #(format "'%s'" %) urls)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.core (:require [clojusc.twig :as logger] [cmr.graph.components.core :as componemts] [com.stuartsierra.component :as component] [taoensso.timbre :as log] [trifl.java :as java]) (:gen-class)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def startup-message (str "Component startup complete." \newline \newline "The CMR Graph system is now ready to use. " "To get started," \newline "you can visit the following:" \newline "* Neo4j: http://localhost:7474/browser/" \newline "* Kibana: http://localhost:5601/" \newline \newline "Additionally, the CMR Graph REST API is available at" \newline "http://localhost:3012. To try it out, call the following:" \newline "* curl --silent \"http://localhost:3012/ping\ \newline "* curl --silent \"http://localhost:3012/health\ \newline)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn shutdown [system] (component/stop system)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn -main [& args] (logger/set-level! ['cmr.graph] :info) (log/info "Starting the CMR Graph components ...") (let [system (componemts/init)] (component/start system) (java/add-shutdown-handler #(shutdown system))) (log/info startup-message)) | |||||||||||||||||||||||||||||||||||||||||||||||||
CMR Graph system management. | (ns cmr.graph.system.impl.state (:require [com.stuartsierra.component :as component] [taoensso.timbre :as log])) | ||||||||||||||||||||||||||||||||||||||||||||||||
State Atom ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(def ^:dynamic *state* (atom {:status :stopped :system nil :ns })) | |||||||||||||||||||||||||||||||||||||||||||||||||
System State Implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defrecord StateTracker []) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-state [_this] @*state*) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn set-state [_this new-state] (reset! *state* new-state)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-status [this] (:status (get-state this))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn set-status [this value] (set-state this (assoc (get-state this) :status value))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-system [this] (:system (get-state this))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn set-system [this value] (set-state this (assoc (get-state this) :system value))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-system-ns [this] (:ns (get-state this))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn set-system-ns [this an-ns] (set-state this (assoc (get-state this) :ns an-ns))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def behaviour {:get-state get-state :set-state set-state :get-status get-status :set-status set-status :get-system get-system :set-system set-system :get-system-ns get-system-ns :set-system-ns set-system-ns}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn create-state-tracker [] (->StateTracker)) | |||||||||||||||||||||||||||||||||||||||||||||||||
CMR Graph system management. | (ns cmr.graph.system.impl.management (:require [cmr.graph.system.impl.state :as state] [com.stuartsierra.component :as component] [taoensso.timbre :as log])) | ||||||||||||||||||||||||||||||||||||||||||||||||
Transition Vars ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(def valid-stop-transitions #{:started :running}) (def invalid-init-transitions #{:initialized :started :running}) (def invalid-deinit-transitions #{:started :running}) (def invalid-start-transitions #{:started :running}) (def invalid-stop-transitions #{:stopped}) (def invalid-startup-transitions #{:running}) (def invalid-shutdown-transitions #{:uninitialized :shutdown}) | |||||||||||||||||||||||||||||||||||||||||||||||||
Utility Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn- resolve-by-name [an-ns a-fun] (resolve (symbol (str an-ns "/" a-fun)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn- call-by-name [an-ns a-fun & args] (apply (resolve-by-name an-ns a-fun) args)) | |||||||||||||||||||||||||||||||||||||||||||||||||
State Management Implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defrecord StateManager [state]) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn init ([this] (init this :default)) ([this mode] (if (contains? invalid-init-transitions (state/get-status (:state this))) (log/warn "System has aready been initialized.") (do (state/set-system (:state this) (call-by-name (state/get-system-ns (:state this)) "init")) (state/set-status (:state this) :initialized))) (state/get-status (:state this)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn deinit [this] (if (contains? invalid-deinit-transitions (state/get-status (:state this))) (log/error "System is not stopped; please stop before deinitializing.") (do (state/set-system (:state this) nil) (state/set-status (:state this) :uninitialized))) (state/get-status (:state this))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn start ([this] (start this :default)) ([this mode] (when (nil? (state/get-status (:state this))) (init mode)) (if (contains? invalid-start-transitions (state/get-status (:state this))) (log/warn "System has already been started.") (do (state/set-system (:state this) (component/start (state/get-system (:state this)))) (state/set-status (:state this) :started))) (state/get-status (:state this)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn stop [this] (if (contains? invalid-stop-transitions (state/get-status (:state this))) (log/warn "System already stopped.") (do (state/set-system (:state this) (component/stop (state/get-system (:state this)))) (state/set-status (:state this) :stopped))) (state/get-status (:state this))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn restart ([this] (restart this :default)) ([this mode] (stop this) (start this mode))) | |||||||||||||||||||||||||||||||||||||||||||||||||
Initialize a system and start all of its components. This is essentially a convenience wrapper for | (defn startup ([this] (startup this :default)) ([this mode] (if (contains? invalid-startup-transitions (state/get-status (:state this))) (log/warn "System is already running.") (do (when-not (contains? invalid-init-transitions (state/get-status (:state this))) (init this mode)) (when-not (contains? invalid-start-transitions (state/get-status (:state this))) (start this mode)) (state/set-status (:state this) :running) (state/get-status (:state this)))))) | ||||||||||||||||||||||||||||||||||||||||||||||||
(defn shutdown [this] "Stop a running system and de-initialize it. This is essentially a convenience wrapper for `stop` + `deinit`." (if (contains? invalid-shutdown-transitions (state/get-status (:state this))) (log/warn "System is already shutdown.") (do (when-not (contains? invalid-stop-transitions (state/get-status (:state this))) (stop this)) (when-not (contains? invalid-deinit-transitions (state/get-status (:state this))) (deinit this)) (state/set-status (:state this) :shutdown) (state/get-status (:state this))))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def behaviour {:init init :deinit deinit :start start :stop stop :restart restart :startup startup :shutdown shutdown}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn create-state-manager [] (->StateManager (state/create-state-tracker))) | |||||||||||||||||||||||||||||||||||||||||||||||||
CMR Graph system management API. | (ns cmr.graph.system.core (:require [cmr.graph.system.impl.management :as management] [cmr.graph.system.impl.state :as state]) (:import (cmr.graph.system.impl.management StateManager) (cmr.graph.system.impl.state StateTracker))) | ||||||||||||||||||||||||||||||||||||||||||||||||
System State API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defprotocol StateTrackerAPI (get-state [this]) (set-state [this new-state]) (get-status [this]) (set-status [this value]) (get-system [this]) (set-system [this value]) (get-system-ns [this]) (set-system-ns [this value])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(extend StateTracker StateTrackerAPI state/behaviour) | |||||||||||||||||||||||||||||||||||||||||||||||||
State Management API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defprotocol StateManagementAPI (init [this] [this mode]) (deinit [this]) (start [this] [this mode]) (stop [this]) (restart [this] [this mode]) (startup [this] [this mode]) (shutdown [this])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(extend StateManager StateManagementAPI management/behaviour) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def create-state-manager #'management/create-state-manager) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.components.neo4j (:require [clojurewerkz.neocons.rest :as nr] [cmr.graph.components.config :as config] [com.stuartsierra.component :as component] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Config Component API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-conn [system] (get-in system [:neo4j :conn])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Component Lifecycle Implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defrecord Neo4j [conn]) | |||||||||||||||||||||||||||||||||||||||||||||||||
(nr/connect "http://localhost:7474/db/data/") | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn start [this] (log/info "Starting Neo4j component ...") (let [conn (nr/connect (format "http://%s:%s%s" (config/neo4j-host this) (config/neo4j-port this) (config/neo4j-db-path this)))] (log/debug "Started Neo4j component.") (assoc this :conn conn))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn stop [this] (log/info "Stopping Neo4j component ...") (log/debug "Stopped Neo4j component.") (assoc this :conn nil)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def lifecycle-behaviour {:start start :stop stop}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(extend Neo4j component/Lifecycle lifecycle-behaviour) | |||||||||||||||||||||||||||||||||||||||||||||||||
Component Constructor ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn create-component [] (map->Neo4j {})) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.components.core (:require [cmr.graph.components.config :as config] [cmr.graph.components.elastic :as elastic] [cmr.graph.components.httpd :as httpd] [cmr.graph.components.logging :as logging] [cmr.graph.components.neo4j :as neo4j] [com.stuartsierra.component :as component])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Common Configuration Components ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(def cfg {:config (config/create-component)}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def log {:logging (component/using (logging/create-component) [:config])}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def neo4j {:neo4j (component/using (neo4j/create-component) [:config :logging])}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def elastic {:elastic (component/using (elastic/create-component) [:config :logging])}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def httpd {:httpd (component/using (httpd/create-component) [:config :logging :neo4j :elastic])}) | |||||||||||||||||||||||||||||||||||||||||||||||||
Component Initializations ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn initialize-bare-bones [] (component/map->SystemMap (merge cfg log neo4j))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn initialize-with-web [] (component/map->SystemMap (merge cfg log neo4j elastic httpd))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def init-lookup {:basic #'initialize-bare-bones :web #'initialize-with-web}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn init ([] (init :web)) ([mode] ((mode init-lookup)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.components.logging (:require [clojusc.twig :as logger] [com.stuartsierra.component :as component] [cmr.graph.components.config :as config] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Logging Component API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
TBD | |||||||||||||||||||||||||||||||||||||||||||||||||
Component Lifecycle Implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defrecord Logging []) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn start [this] (log/info "Starting logging component ...") (let [log-level (config/log-level this) log-nss (config/log-nss this)] (log/debug "Setting up logging with level" log-level) (log/debug "Logging namespaces:" log-nss) (logger/set-level! log-nss log-level) (log/debug "Started logging component.") this)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn stop [this] (log/info "Stopping logging component ...") (log/debug "Stopped logging component.") this) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def lifecycle-behaviour {:start start :stop stop}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(extend Logging component/Lifecycle lifecycle-behaviour) | |||||||||||||||||||||||||||||||||||||||||||||||||
Component Constructor ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn create-component [] (map->Logging {})) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.components.elastic (:require [clojurewerkz.elastisch.rest :as esr] [cmr.graph.components.config :as config] [com.stuartsierra.component :as component] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Config Component API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
Component Lifecycle Implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defrecord Elastic [conn]) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn start [this] (log/info "Starting Elasticsearch component ...") (let [conn-mgr (clj-http.conn-mgr/make-reusable-conn-manager {:timeout (config/elastic-timeout this)}) conn (esr/connect (format "http://%s:%s" (config/elastic-host this) (config/elastic-port this)) {:connection-manager conn-mgr})] (log/debug "Started Elasticsearch component.") (assoc this :conn conn))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn stop [this] (log/info "Stopping Elasticsearch component ...") (log/debug "Stopped Elasticsearch component.") (assoc this :conn nil)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def lifecycle-behaviour {:start start :stop stop}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(extend Elastic component/Lifecycle lifecycle-behaviour) | |||||||||||||||||||||||||||||||||||||||||||||||||
Component Constructor ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn create-component [] (map->Elastic {})) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.components.config (:require [cmr.graph.config :as config] [com.stuartsierra.component :as component] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Utility Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn- get-cfg [system] (get-in system [:config :data])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Config Component API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn elastic-host [system] (get-in (get-cfg system) [:elastic :host])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn elastic-port [system] (get-in (get-cfg system) [:elastic :port])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn elastic-timeout [system] (get-in (get-cfg system) [:elastic :timeout])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn http-port [system] (get-in (get-cfg system) [:httpd :port])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn http-docroot [system] (get-in (get-cfg system) [:httpd :docroot])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn log-level [system] (get-in (get-cfg system) [:logging :level])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn log-nss [system] (get-in (get-cfg system) [:logging :nss])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn neo4j-host [system] (get-in (get-cfg system) [:neo4j :host])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn neo4j-port [system] (get-in (get-cfg system) [:neo4j :port])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn neo4j-db-path [system] (get-in (get-cfg system) [:neo4j :db-path])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Component Lifecycle Implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defrecord Config [data]) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn start [this] (log/info "Starting config component ...") (log/debug "Started config component.") (let [cfg (config/data)] (log/trace "Built configuration:" cfg) (assoc this :data cfg))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn stop [this] (log/info "Stopping config component ...") (log/debug "Stopped config component.") (assoc this :data nil)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def lifecycle-behaviour {:start start :stop stop}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(extend Config component/Lifecycle lifecycle-behaviour) | |||||||||||||||||||||||||||||||||||||||||||||||||
Component Constructor ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn create-component [] (map->Config {})) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.components.httpd (:require [com.stuartsierra.component :as component] [cmr.graph.components.config :as config] [cmr.graph.rest.app :as rest-api] [org.httpkit.server :as server] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||
HTTP Server Component API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
TBD | |||||||||||||||||||||||||||||||||||||||||||||||||
Component Lifecycle Implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defrecord HTTPD []) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn start [this] (log/info "Starting httpd component ...") (let [port (config/http-port this) server (server/run-server (rest-api/app this) {:port port})] (log/debugf "HTTPD is listening on port %s" port) (log/debug "Started httpd component.") (assoc this :server server))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn stop [this] (log/info "Stopping httpd component ...") (if-let [server (:server this)] (server)) (assoc this :server nil)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def lifecycle-behaviour {:start start :stop stop}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(extend HTTPD component/Lifecycle lifecycle-behaviour) | |||||||||||||||||||||||||||||||||||||||||||||||||
Component Constructor ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn create-component [] (map->HTTPD {})) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.health (:require [clj-http.client :as httpc])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn http-ok? [url] (if (= 200 (:status (httpc/head url))) true false)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn has-data? [x] (if (nil? x) false true)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn config-ok? [component] (has-data? (:config component))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn elastic-ok? [component] (http-ok? (get-in component [:elastic :conn :uri]))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn logging-ok? [component] (has-data? (:logging component))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn neo4j-ok? [component] (http-ok? (get-in component [:neo4j :conn :endpoint :uri]))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn components-ok? [component] {:config {:ok? (config-ok? component)} :httpd {:ok? true} :elastic {:ok? (elastic-ok? component)} :logging {:ok? (logging-ok? component)} :neo4j {:ok? (neo4j-ok? component)}}) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.collections.core (:require [cheshire.core :as json] [clojurewerkz.neocons.rest.cypher :as cypher] [clojurewerkz.neocons.rest.nodes :as nodes] [clojurewerkz.neocons.rest.relationships :as relations] [cmr.graph.queries.neo4j.collections :as query])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn reset [conn] (cypher/tquery conn query/reset)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-all [conn] (cypher/tquery conn query/get-all)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn delete-all [conn] (cypher/tquery conn query/delete-all)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn delete-all-cascade [conn] (cypher/tquery conn query/delete-all-cascade)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn batch-add [conn ^String json]) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn add-collection [conn ^String json]) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-collection [conn ^String concept-id]) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn delete-collection [conn ^String concept-id]) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn update-collection [conn ^String concept-id]) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-collections-via-related-urls [conn ^String concept-id] (cypher/tquery conn (query/get-urls-by-concept-id concept-id))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-concept-ids-by-urls [conn urls] (cypher/tquery conn (query/get-concept-ids-by-urls urls))) | |||||||||||||||||||||||||||||||||||||||||||||||||
Functions for converting a collection into neo4j create statements. | (ns cmr.graph.data.statement (:require [clojure.string :as string] [digest :as digest])) | ||||||||||||||||||||||||||||||||||||||||||||||||
(defn- coll-concept-id-key [coll] (string/replace (first (:concept-id coll)) "-" )) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn- coll->provider-node [coll] (let [provider-id (first (:provider-id coll))] [(format "CREATE (%s:Provider {ShortName:'%s'})" provider-id provider-id)])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn- coll->coll-stmts [coll] (let [{:keys [provider-id concept-id entry-id version-id]} coll provider-id (first provider-id) concept-id (first concept-id) entry-id (first entry-id) version-id (first version-id)] [(format "CREATE (%s:Collection {conceptId: '%s', entryId:'%s', version:'%s'})" (coll-concept-id-key coll) concept-id entry-id version-id) (format "CREATE (%s)-[:PROVIDES {type:['collection']}]->(%s)" provider-id (coll-concept-id-key coll))])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn- url-type->stmt [url-type coll-key] (let [url-type-key (str "A" (digest/md5 url-type))] [(format "CREATE (%s:UrlType {Value:'%s'})" url-type-key url-type) (format "CREATE (%s)-[:LINKS {type:['collection']}]->(%s)" url-type-key coll-key)])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn- url->stmt [url coll-key] (let [url-key (str "A" (digest/md5 url))] [(format "CREATE (%s:Url {name:'%s'})" url-key url) (format "CREATE (%s)-[:LINKS_TO]->(%s)" coll-key url-key) (format "CREATE (%s)-[:IS_IN]->(%s)" url-key coll-key)])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn- coll->related-urls-stmts [coll] (let [{:keys [related-urls]} coll url-types (distinct (mapv :type related-urls)) urls (distinct (mapv :url related-urls)) coll-key (coll-concept-id-key coll)] (concat (mapcat #(url-type->stmt % coll-key) url-types) (mapcat #(url->stmt % coll-key) urls)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
Returns the neo4j create statements for the given collection | (defn coll->create-stmts [coll] (concat (coll->provider-node coll) (coll->coll-stmts coll) (coll->related-urls-stmts coll))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Returns the neo4j statements for building the graph of the given collections. | (defn neo4j-statements [colls] (string/join "\n" (distinct (mapcat coll->create-stmts colls)))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Functions for working with tags data. | (ns cmr.graph.data.tags (:require [clojure.data.codec.base64 :as b64] [clojure.edn :as edn]) (:import (java.util.zip GZIPInputStream) (java.io ByteArrayInputStream))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Converts a base64 encoded gzipped string to EDN. | (defn gzip-base64-tag->edn [^String input] (-> input .getBytes b64/decode ByteArrayInputStream. GZIPInputStream. slurp edn/read-string)) | ||||||||||||||||||||||||||||||||||||||||||||||||
(comment (gzip-base64-tag->edn "H4sIAAAAAAAAAIWPMQvCMBCF/8qRScEGxM3tiAELtSltFXEpoQkhII0ktR1K/7spio4Od8O7997HTcR5Q1vtAh2NDYG2o22p8bJ73nWgD+8UgWnewF/fYJX20btXspdAcqyQ/M11sreDbqz6BRme+DXZAcvE+QCYx+GlqEQGBZZ1yjIO7IglspqX6Q3rVOSwYlggW8Nl+0WaiAzv3SzFSeu8/rxi3BDJQdJ4VTYs6vwC7Me2uQkBAAA=")) | |||||||||||||||||||||||||||||||||||||||||||||||||
Functions for importing data into neo4j. | (ns cmr.graph.data.import (:require [cheshire.core :as json] [clojure.data.csv :as csv] [clojure.java.io :as io] [clojurewerkz.neocons.rest.cypher :as cypher] [cmr.graph.data.statement :as statement] [cmr.graph.data.tags :as tags] [digest :as digest])) | ||||||||||||||||||||||||||||||||||||||||||||||||
(def json-collections-filename "data/all_public_collections_from_es.json") | |||||||||||||||||||||||||||||||||||||||||||||||||
(def test-file "data/testfile.json") | |||||||||||||||||||||||||||||||||||||||||||||||||
(def collection-csv-file "data/collections.csv") | |||||||||||||||||||||||||||||||||||||||||||||||||
(def collection-url-csv-file "data/collection_and_urls.csv") | |||||||||||||||||||||||||||||||||||||||||||||||||
(def collection-data-center-csv-file "data/collection_and_data_centers.csv") | |||||||||||||||||||||||||||||||||||||||||||||||||
(def collection-tag-csv-file "data/collection_and_tags.csv") | |||||||||||||||||||||||||||||||||||||||||||||||||
List of fields we are interested in parsing from a given URL. | (def url-fields [:type :url]) | ||||||||||||||||||||||||||||||||||||||||||||||||
List of fields to parse from a collection record. | (def relevant-fields [:concept-id :provider-id :entry-id :related-urls :data-center :version-id :metadata-format :tags-gzip-b64]) | ||||||||||||||||||||||||||||||||||||||||||||||||
Parses a single URL field into all the nodes we want to create for the URL. | (defn parse-url-into-nodes [url] (select-keys (json/parse-string url true) url-fields)) | ||||||||||||||||||||||||||||||||||||||||||||||||
Returns each of the tags and associated data from the provided Elasticsearch tags-gzip-b64 field. | (defn- parse-tags [tags-gzip-b64] (when tags-gzip-b64 (tags/gzip-base64-tag->edn tags-gzip-b64))) | ||||||||||||||||||||||||||||||||||||||||||||||||
When a hash just isn't good enough. | (defn md5-leo [value] (str "A" (digest/md5 value))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Returns only the relevant JSON fields from the provided collection record for import into neo4j. | (defn prepare-collection-for-import [collection] (update (select-keys (:fields collection) relevant-fields) :related-urls (fn [urls] (mapv parse-url-into-nodes urls)))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Reads a JSON file into memory | (defn read-json-file [filename] (json/parse-string (slurp (io/resource filename)) true)) | ||||||||||||||||||||||||||||||||||||||||||||||||
Columns in the collections CSV file. | (def collection-columns ["MD5Leo" "ConceptId" "ProviderId" "VersionId" "MetadataFormat"]) | ||||||||||||||||||||||||||||||||||||||||||||||||
Returns a row to write to the collections CSV file for a given collection. | (defn collection->row [collection] (let [{:keys [provider-id concept-id version-id metadata-format]} collection] [(md5-leo (first concept-id)) (first concept-id) (first provider-id) (first version-id) (first metadata-format)])) | ||||||||||||||||||||||||||||||||||||||||||||||||
Creates the collection csv file | (defn write-collection-csv [collections output-filename] (with-open [csv-file (io/writer output-filename)] (csv/write-csv csv-file [collection-columns]) (csv/write-csv csv-file (mapv collection->row collections)))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Creates a collection URL row for a relationship CSV file. | (defn construct-collection-url-row [collection url] [(md5-leo (first (:concept-id collection))) (:url url) (:type url)]) | ||||||||||||||||||||||||||||||||||||||||||||||||
Creates a collection data center row for a relationship CSV file. | (defn construct-collection-data-center-row [collection data-center] [(md5-leo (first (:concept-id collection))) data-center]) | ||||||||||||||||||||||||||||||||||||||||||||||||
Creates the collection<->url relationship csv file. | (defn write-collection-url-relationship-csv [collections output-filename] (let [rows (doall (for [collection collections url (:related-urls collection)] (construct-collection-url-row collection url)))] (with-open [csv-file (io/writer output-filename)] (csv/write-csv csv-file [["CollectionMD5Leo" "URL" "URLType"]]) (csv/write-csv csv-file rows)))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Creates the collection<->data centers relationship csv file. | (defn write-collection-data-center-relationship-csv [collections output-filename] (let [rows (doall (for [collection collections data-center (:data-center collection)] (construct-collection-data-center-row collection data-center)))] (with-open [csv-file (io/writer output-filename)] (csv/write-csv csv-file [["CollectionMD5Leo" "DataCenter"]]) (csv/write-csv csv-file rows)))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Creates a collection data center row for a relationship CSV file. | (defn- construct-collection-tag-row [collection tag] (let [[tag-key tag-association-data] tag] [(md5-leo (first (:concept-id collection))) tag-key])) | ||||||||||||||||||||||||||||||||||||||||||||||||
Creates the collection<->tag relationship csv file. | (defn write-collection-tags-relationship-csv [collections output-filename] (let [rows (doall (for [collection collections tag (parse-tags (first (:tags-gzip-b64 collection)))] (construct-collection-tag-row collection tag)))] (with-open [csv-file (io/writer output-filename)] (csv/write-csv csv-file [["CollectionMD5Leo" "TagKey"]]) (csv/write-csv csv-file rows)))) | ||||||||||||||||||||||||||||||||||||||||||||||||
All of the import statements to run to populate a completely empty database. Make sure to delete everything before running. | (def import-statements ["CREATE CONSTRAINT ON (url:Url) ASSERT url.name IS UNIQUE" "CREATE CONSTRAINT ON (urlType:UrlType) ASSERT urlType.name IS UNIQUE" "CREATE CONSTRAINT ON (coll:Collection) ASSERT coll.md5Leo IS UNIQUE" "CREATE CONSTRAINT ON (dataCenter:DataCenter) ASSERT dataCenter.name IS UNIQUE" "CREATE CONSTRAINT ON (tag:Tag) ASSERT tag.name IS UNIQUE" "LOAD CSV WITH HEADERS FROM \"https://raw.githubusercontent.com/cmr-exchange/cmr-graph/master/resources/data/collections.csv\" AS csvLine MERGE (format:MetadataFormat {name: csvLine.MetadataFormat}) MERGE (version:Version {name: csvLine.VersionId}) MERGE (provider:Provider {name: csvLine.ProviderId}) CREATE (coll:Collection {md5Leo: csvLine.MD5Leo, conceptId: csvLine.ConceptId}) CREATE (coll)-[:OWNED_BY]->(provider) CREATE (coll)-[:FORMATTED_IN]->(format) CREATE (coll)-[:VERSION_IS]->(version)" "USING PERIODIC COMMIT 500 LOAD CSV WITH HEADERS FROM \"https://raw.githubusercontent.com/cmr-exchange/cmr-graph/master/resources/data/collection_and_urls.csv\" AS csvLine MATCH (coll:Collection { md5Leo: csvLine.CollectionMD5Leo}) MERGE (url:Url { name: csvLine.URL}) MERGE (urlType:UrlType { name: csvLine.URLType}) CREATE (coll)-[:LINKS_TO]->(url) CREATE (url)-[:HAS_TYPE]->(urlType)" "USING PERIODIC COMMIT 500 LOAD CSV WITH HEADERS FROM \"https://raw.githubusercontent.com/cmr-exchange/cmr-graph/master/resources/data/collection_and_data_centers.csv\" AS csvLine MATCH (coll:Collection { md5Leo: csvLine.CollectionMD5Leo}) MERGE (dataCenter:DataCenter { name: csvLine.DataCenter}) CREATE (coll)-[:AFFILIATED_WITH]->(dataCenter)" "USING PERIODIC COMMIT 500 LOAD CSV WITH HEADERS FROM \"https://raw.githubusercontent.com/cmr-exchange/cmr-graph/master/resources/data/collection_and_tags.csv\" AS csvLine MATCH (coll:Collection { md5Leo: csvLine.CollectionMD5Leo}) MERGE (tag:Tag { name: csvLine.TagKey}) CREATE (coll)-[:TAGGED_WITH]->(tag)"]) | ||||||||||||||||||||||||||||||||||||||||||||||||
Imports all of the collection data. | (defn import-all-data [conn] (doseq [statement import-statements] (cypher/tquery conn statement))) | ||||||||||||||||||||||||||||||||||||||||||||||||
(comment (prepare-collection-for-import (first (:hits (:hits (read-json-file json-collections-filename))))) (mapv prepare-collection-for-import (:hits (:hits (read-json-file test-file)))) (prepare-collection-for-import (first (:hits (:hits (read-json-file json-collections-filename))))) (write-collection-csv (mapv prepare-collection-for-import (:hits (:hits (read-json-file json-collections-filename)))) (str "resources/" collection-csv-file)) (write-collection-url-relationship-csv (mapv prepare-collection-for-import (:hits (:hits (read-json-file json-collections-filename)))) (str "resources/" collection-url-csv-file)) (write-collection-data-center-relationship-csv (mapv prepare-collection-for-import (:hits (:hits (read-json-file json-collections-filename)))) (str "resources/" collection-data-center-csv-file)) (write-collection-tags-relationship-csv (mapv prepare-collection-for-import (:hits (:hits (read-json-file json-collections-filename)))) (str "resources/" collection-tag-csv-file)) (mapv prepare-collection-for-import (:hits (:hits (read-json-file test-file)))) (println (statement/neo4j-statements (mapv prepare-collection-for-import (:hits (:hits (read-json-file test-file))))))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.rest.route (:require [cmr.graph.components.config :as config] [cmr.graph.components.neo4j :as neo4j] [cmr.graph.health :as health] [cmr.graph.rest.handler :as handler] [reitit.ring :as ring] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||
CMR Graph Database Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn collections [httpd-component] (let [conn (neo4j/get-conn httpd-component)] [["/collections" {:get (handler/get-collections conn) :delete (handler/delete-collections conn) :post (handler/add-collections conn) :options handler/ok}] ["/collections/import" {:post (handler/import-collection-data conn) :options handler/ok}] ["/collection" {:post (handler/add-collection conn) :options handler/ok}] ["/collection/:concept-id" {:get (handler/get-collection conn) :delete (handler/delete-collection conn) :put (handler/update-collection conn) :options handler/ok}]])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn relationships [httpd-component] (let [conn (neo4j/get-conn httpd-component)] [["/relationships/related-urls/collections/:concept-id" {:get (handler/get-collections-via-related-urls conn) :options handler/ok}]])) | |||||||||||||||||||||||||||||||||||||||||||||||||
CMR Elasticsearch Graph Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
Demo Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn movie-demo [httpd-component] (let [conn (neo4j/get-conn httpd-component)] [["/demo/movie/graph/:limit" { :get (handler/movie-demo-graph conn) :options handler/ok}] ["/demo/movie/search" { :get (handler/movie-demo-search conn) :options handler/ok}] ["/demo/movie/title/:title" { :get (handler/movie-demo-title conn) :options handler/ok}]])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Static Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn static [httpd-component] (let [docroot (config/http-docroot httpd-component)] [["/static/*" { :get (handler/static-files docroot)}]])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Admin Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn admin [httpd-component] (let [conn (neo4j/get-conn httpd-component)] [["/health" { :get (handler/health httpd-component) :options handler/ok}] ["/reset" { :delete (handler/reset conn) :options handler/ok}] ["/reload" {:post (handler/reload conn) :options handler/ok}] ["/ping" { :get handler/ping :post handler/ping :options handler/ok}]])) | |||||||||||||||||||||||||||||||||||||||||||||||||
DANGEROUS!!! REMOVE ME!!! *Injection Routes ;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn dangerous [httpd-component] (let [conn (neo4j/get-conn httpd-component)] [["/queries/cypher" {:get (handler/cypher-injection-get conn) :post (handler/cypher-injection-post conn) :options handler/ok}]])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Utility Routes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
Custom ring middleware for CMR Graph. | (ns cmr.graph.rest.middleware (:require [cmr.graph.rest.response :as response] [taoensso.timbre :as log])) | ||||||||||||||||||||||||||||||||||||||||||||||||
(defn wrap-cors [handler] (fn [request] (response/cors request (handler request)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.rest.app (:require [clojure.java.io :as io] [cmr.graph.rest.handler :as handler] [cmr.graph.rest.middleware :as middleware] [cmr.graph.rest.route :as route] [ring.middleware.defaults :as ring-defaults] [reitit.ring :as ring] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn rest-api-routes [httpd-component] (concat (route/collections httpd-component) (route/relationships httpd-component) (route/static httpd-component) (route/movie-demo httpd-component) (route/admin httpd-component) (route/dangerous httpd-component))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn app [httpd-component] (-> httpd-component rest-api-routes ring/router (ring/ring-handler handler/fallback) (ring-defaults/wrap-defaults ring-defaults/api-defaults) (middleware/wrap-cors))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.rest.handler (:require [clojure.java.io :as io] [clojurewerkz.neocons.rest.cypher :as cypher] [cmr.graph.data.import :as data-import] [clojusc.twig :as twig] [cmr.graph.collections.core :as collections] [cmr.graph.demo.movie :as movie] [cmr.graph.health :as health] [cmr.graph.rest.response :as response] [ring.middleware.file :as file-middleware] [ring.util.codec :as codec] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||
Graph Handlers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-collections [conn] (fn [request] (->> conn (collections/get-all) (response/json request)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn delete-collections [conn] (fn [request] (let [cascade? (get-in request [:params :cascade])] (if (= "true" cascade?) (->> conn (collections/delete-all-cascade) (response/json request)) (->> conn (collections/delete-all) (response/json request)))))) | |||||||||||||||||||||||||||||||||||||||||||||||||
Expects the body to be a JSON payload of an array of node objects. | (defn add-collections [conn] (fn [request] (->> request :body slurp ;(collections/batch-add conn) ((fn [_] {:error :not-implemented})) (response/json request)))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Expects the body to be a JSON payload of a node object. | (defn add-collection [conn] (fn [request] (->> request :body slurp ;(collections/add-collection conn) ((fn [_] {:error :not-implemented})) (response/json request)))) | ||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-collection [conn] (fn [request] (->> [:path-params :concept-id] (get-in request) ;(collections/get-collection conn) ((fn [_] {:error :not-implemented})) (response/json request)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn delete-collection [conn] (fn [request] (->> [:path-params :concept-id] (get-in request) ;(collections/delete-collection conn) ((fn [_] {:error :not-implemented})) (response/json request)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn update-collection [conn] (fn [request] (->> [:path-params :concept-id] (get-in request) ;(collections/update-collection conn) ((fn [_] {:error :not-implemented})) (response/json request)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn- get-related-urls [conn concept-id] (let [result (collections/get-collections-via-related-urls conn concept-id)] (map #(get % "u.name") result))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn get-collections-via-related-urls [conn] (fn [request] (let [related-urls (get-related-urls conn (get-in request [:path-params :concept-id])) result (collections/get-concept-ids-by-urls conn related-urls) concept-ids (distinct (map #(get % "c.conceptId") result))] (response/json request concept-ids)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
Imports all of our collection data. | (defn import-collection-data [conn] (fn [request] (data-import/import-all-data conn) (response/ok request))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Demo Handlers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn movie-demo-graph [conn] (fn [request] (->> [:path-params :limit] (get-in request) Integer. (movie/get-graph conn) (response/json request)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn movie-demo-search [conn] (fn [request] (->> [:params :q] (get-in request) (movie/search conn) (response/json request)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn movie-demo-title [conn] (fn [request] (->> [:path-params :title] (get-in request) (codec/percent-decode) (movie/get-movie conn) (response/json request)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
Admin Handlers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn health [component] (fn [request] (->> component health/components-ok? (response/json request)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn reset [conn] (fn [request] ;; delete things in small increments to avoid hanging the system (collections/delete-all-cascade conn) (collections/reset conn) (response/ok request))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn reload [conn] (fn [request] ;; delete things in small increments to avoid hanging the system (collections/delete-all-cascade conn) (collections/reset conn) (data-import/import-all-data conn) (response/ok request))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def ping (fn [request] (response/json request {:result :pong}))) | |||||||||||||||||||||||||||||||||||||||||||||||||
DANGEROUS!!! REMOVE ME!!! *Injection Handlers ;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
Call with something like this: $ curl http://localhost:3012/queries/cypher?q='MATCH%20(people:Person)%20RETURN%20people.name%20LIMIT%2010;' But don't, really. Since we're going to delete this :-) | (defn cypher-injection-get [conn] (fn [request] (->> [:params :q] (get-in request) (codec/percent-decode) (cypher/tquery conn) (response/json request)))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Call with something like this: $ curl -XPOST -H 'Content-Type: text/plain' http://localhost:3012/queries/cypher -d 'MATCH (people:Person) RETURN people.name LIMIT 10;' But don't, really. Since we're going to delete this :-) | (defn cypher-injection-post [conn] (fn [request] (->> request :body slurp (cypher/tquery conn) (response/json request)))) | ||||||||||||||||||||||||||||||||||||||||||||||||
Utility Handlers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |||||||||||||||||||||||||||||||||||||||||||||||||
(def ok (fn [request] (response/ok request))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def fallback (fn [request] (response/not-found request))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn static-files [docroot] (fn [request] (if-let [doc-resource (.getPath (io/resource docroot))] (file-middleware/file-request request doc-resource)))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.rest.response (:require [cheshire.core :as json] [ring.util.http-response :as response] [taoensso.timbre :as log])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn ok [_request & args] (response/ok args)) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn json [_request data] (-> data json/generate-string response/ok (response/content-type "application/json"))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn text [_request data] (-> data response/ok (response/content-type "text/plain"))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn not-found [_request] (response/content-type (response/not-found "Not Found") "plain/text")) | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn cors [request response] (case (:request-method request) :options (-> response (response/content-type "text/plain; charset=utf-8") (response/header "Access-Control-Allow-Origin" "*") (response/header "Access-Control-Allow-Methods" "POST, PUT, GET, DELETE, OPTIONS") (response/header "Access-Control-Allow-Headers" "Content-Type") (response/header "Access-Control-Max-Age" "2592000")) (response/header response "Access-Control-Allow-Origin" "*"))) | |||||||||||||||||||||||||||||||||||||||||||||||||
(ns cmr.graph.config (:require [clojure.edn :as edn] [clojure.java.io :as io])) | |||||||||||||||||||||||||||||||||||||||||||||||||
(def config-file "config/cmr-graph/config.edn") | |||||||||||||||||||||||||||||||||||||||||||||||||
(defn data ([] (data config-file)) ([filename] (with-open [rdr (io/reader (io/resource filename))] (edn/read (new java.io.PushbackReader rdr))))) | |||||||||||||||||||||||||||||||||||||||||||||||||