aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.gitreview2
-rw-r--r--README.md76
m---------chameleon5
-rw-r--r--dev/dev.clj10
-rw-r--r--devops/chameleon/build-chameleon15
-rw-r--r--pom.xml305
-rw-r--r--prod/chameleon/server.clj12
-rw-r--r--project.clj42
-rw-r--r--resources/chameleon_logback.xml109
-rw-r--r--resources/log/ChameleonMsgs.properties19
-rw-r--r--src/chameleon/aai_processor.clj57
-rw-r--r--src/chameleon/config.clj8
-rw-r--r--src/chameleon/event.clj22
-rw-r--r--src/chameleon/handler.clj37
-rw-r--r--src/chameleon/logging.clj123
-rw-r--r--src/chameleon/route.clj50
-rw-r--r--src/chameleon/specs.clj71
-rw-r--r--test/chameleon/testing.clj42
19 files changed, 902 insertions, 106 deletions
diff --git a/.gitignore b/.gitignore
index 1d39b07..05ff279 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,9 @@
# Created by https://www.gitignore.io/api/clojure,emacs,osx,java
### Clojure ###
-pom.xml
pom.xml.asc
+chameleon.iml
+.idea
*jar
lib/
classes/
diff --git a/.gitreview b/.gitreview
index 8fd4a05..22efe10 100644
--- a/.gitreview
+++ b/.gitreview
@@ -1,4 +1,4 @@
[gerrit]
-host=gerrit.openecomp.org
+host=gerrit.onap.org
port=29418
project=chameleon.git
diff --git a/README.md b/README.md
index 0895c05..0a46e44 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,77 @@
-# chameleon
+# CHAMELEON
Feeds the Gallifrey time database with entity events
+
+### Building the project
+
+1. mvn clean install
+2. mvn package
+
+### Generating a code coverage report
+
+1. mvn clean install
+2. mvn exec:java -Dexec.classpathScope=test -Dexec.mainClass='clojure.main' -Dexec.args="--main cloverage.coverage -n 'chameleon.*' -t 'chameleon.testing'"
+
+ 2.1. Follow the instructions to view the report.
+
+3. To tweak the report generation, follow the instrustions here:
+ https://github.com/cloverage/cloverage
+
+### Developing
+
+1. I use Leiningen for building my clojure project and for dependency
+ management. You can read about Leiningen and follow the tutorial at
+ the link:
+ https://github.com/technomancy/leiningen
+
+2. If you make ** changes to the project.clj file, you are required to
+ re-generate the pom.xml by running `lein pom`** (requires leiningen
+ to be instaled).
+
+ You can also add your changes to the pom.xml file manually.
+
+3. I use Emacs as my editor and CIDER for interactive development in
+ Clojure.
+ - CIDER: https://github.com/clojure-emacs/cider
+ - Emacs: https://www.gnu.org/software/emacs/
+
+ - Here is a tutorial on Emacs: https://www.gnu.org/software/emacs/tour/
+ - Here is the emacs configuration I've used, it includes the CIDER
+ package:
+ https://github.com/sandhu/emacs.d
+
+4. Start nrepl `C-c M-j` (you have to be in any *.clj file in the
+ chameleon project). If the cider-repl buffer doesn't open
+ automatically, use `C-x b` and use the arrow keys to find the
+ `cider-repl chameleon` buffer and press enter.
+
+5. Open the dev.clj file by `C-x C-f`, load it `C-c C-k` and switch
+ your namespace to dev `C-c M-n`.
+
+6. In the repl buffer you should see that the namespace went from `user`
+ to `dev`.
+
+7. Make sure the configuration is correct in the dev.clj file and once
+ verified, in the repl buffer type `(go)` and press enter.
+
+8. You can verify everthing is running by generating/adding an event
+ to dmaap and seeing it flow through chameleon in the error.log file.
+
+### Running it locally (Assumming you're not using Emacs and using
+ `lein repl` from the command line)
+
+**Make you're in the directory at the root of your project. Open the
+ file `chameleon/dev.clj` and update the config to the correct
+ dmaap host, topic, and make sure you're using the correct
+ consumer group and id.**
+
+1. On the command line, execute `lein repl`
+
+2. Once in the clojure repl, load the dev namespace
+ (load "dev")
+
+3. Go into the dev namespace
+ (in-ns 'dev)
+
+4. Run the following command in the repl
+ (go)
diff --git a/chameleon b/chameleon
deleted file mode 160000
-Subproject 2610df2fc29a9d16bb869f652d4d21caeae4c15
diff --git a/dev/dev.clj b/dev/dev.clj
index 880f540..51d0fdf 100644
--- a/dev/dev.clj
+++ b/dev/dev.clj
@@ -4,19 +4,19 @@
(:require [chameleon.config :refer [config]]
[chameleon.handler :refer [handler]]
[integrant.core :as ig]
+ [chameleon.logging :as log]
+ [chameleon.specs :as sp]
[integrant.repl :refer [clear go halt init reset reset-all]]
[integrant.repl.state :refer [system]]
[clojure.tools.namespace.repl :refer [refresh refresh-all disable-reload!]]
[clojure.repl :refer [apropos dir doc find-doc pst source]]
[clojure.test :refer [run-tests run-all-tests]]
- [clojure.pprint :refer [pprint]]
- [clojure.reflect :refer [reflect]]))
+ [clojure.pprint :refer [pprint]]))
(disable-reload! (find-ns 'integrant.core))
-(integrant.repl/set-prep! (constantly (config {
- :event-config {:aai {:host "localhost:3904"
- :topic "spikeEvents"
+(integrant.repl/set-prep! (constantly (config {:event-config {:aai {:host "localhost:3904"
+ :topic "events"
:motsid ""
:pass ""
:consumer-group"chameleon1"
diff --git a/devops/chameleon/build-chameleon b/devops/chameleon/build-chameleon
index 4421407..c588e95 100644
--- a/devops/chameleon/build-chameleon
+++ b/devops/chameleon/build-chameleon
@@ -1,2 +1,13 @@
-lein uberjar
-cp -f ../../target/chameleon.jar .
+!#/bin/bash
+ls build-chameleon
+if [ $? -eq 0 ]; then
+ echo "Building Chameleon"
+else
+ echo "FAILED. Make sure you're in the same directory as the \"build-chameleon\" file."
+ exit 1
+fi
+pushd .
+cd ../../
+mvn -X clean package
+popd
+cp -f ../../target/chameleon-0.1.0-jar-with-dependencies.jar ./chameleon.jar
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..1d9ab52
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,305 @@
+<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>chameleon</groupId>
+ <artifactId>chameleon</artifactId>
+ <packaging>jar</packaging>
+ <version>0.1.0</version>
+ <name>chameleon</name>
+ <description/>
+ <scm>
+ <tag>36b5671af2c3eec5ca81663382c4ca2898f79e55
+</tag>
+ <url/>
+ </scm>
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <testSourceDirectory>test</testSourceDirectory>
+ <resources>
+ <resource>
+ <directory>resources</directory>
+ </resource>
+ </resources>
+ <testResources>
+ <testResource>
+ <directory>resources</directory>
+ </testResource>
+ </testResources>
+ <directory>target</directory>
+ <outputDirectory>target/classes</outputDirectory>
+ <plugins>
+ <plugin>
+ <groupId>com.theoryinpractise</groupId>
+ <artifactId>clojure-maven-plugin</artifactId>
+ <version>1.3.13</version>
+ <extensions>true</extensions>
+ <configuration>
+ <sourceDirectories>
+ <sourceDirectory>src</sourceDirectory>
+ <sourceDirectory>prod</sourceDirectory>
+ <sourceDirectory>test</sourceDirectory>
+ </sourceDirectories>
+ </configuration>
+ <executions>
+ <execution>
+ <id>compile</id>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ <phase>compile</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.4</version>
+ <configuration>
+ <archive>
+ <manifest>
+ <addClasspath>true</addClasspath>
+ <mainClass>chameleon.server</mainClass>
+ <classpathPrefix>dependency</classpathPrefix>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.8</version>
+ <executions>
+ <execution>
+ <id>copy-dependencies</id>
+ <goals>
+ <goal>copy-dependencies</goal>
+ </goals>
+ <phase>package</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.4.1</version>
+ <configuration>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ <archive>
+ <manifest>
+ <mainClass>chameleon.server</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.7</version>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>prod</source>
+ <source>test</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <repositories>
+ <repository>
+ <id>central</id>
+ <url>https://repo1.maven.org/maven2/</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ </repository>
+ <repository>
+ <id>clojars</id>
+ <url>https://clojars.org/repo/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ </repository>
+ <repository>
+ <id>onap-releases</id>
+ <url>https://nexus.onap.org/content/repositories/releases/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ </repository>
+ <repository>
+ <id>onap-public</id>
+ <url>https://nexus.onap.org/content/repositories/public/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ </repository>
+ <repository>
+ <id>onap-staging</id>
+ <url>https://nexus.onap.org/content/repositories/staging/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ </repository>
+ <repository>
+ <id>onap-snapshot</id>
+ <url>https://nexus.onap.org/content/repositories/snapshots/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ </repository>
+ </repositories>
+ <dependencyManagement>
+ <dependencies/>
+ </dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.clojure</groupId>
+ <artifactId>clojure</artifactId>
+ <version>1.9.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.7theta</groupId>
+ <artifactId>utilis</artifactId>
+ <version>1.0.4</version>
+ </dependency>
+ <dependency>
+ <groupId>http-kit</groupId>
+ <artifactId>http-kit</artifactId>
+ <version>2.2.0</version>
+ </dependency>
+ <dependency>
+ <groupId>ring</groupId>
+ <artifactId>ring-core</artifactId>
+ <version>1.6.3</version>
+ </dependency>
+ <dependency>
+ <groupId>ring</groupId>
+ <artifactId>ring-defaults</artifactId>
+ <version>0.3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>ring</groupId>
+ <artifactId>ring-anti-forgery</artifactId>
+ <version>1.1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>compojure</groupId>
+ <artifactId>compojure</artifactId>
+ <version>1.6.0</version>
+ </dependency>
+ <dependency>
+ <groupId>liberator</groupId>
+ <artifactId>liberator</artifactId>
+ <version>0.15.1</version>
+ </dependency>
+ <dependency>
+ <groupId>cheshire</groupId>
+ <artifactId>cheshire</artifactId>
+ <version>5.7.1</version>
+ </dependency>
+ <dependency>
+ <groupId>inflections</groupId>
+ <artifactId>inflections</artifactId>
+ <version>0.13.0</version>
+ </dependency>
+ <dependency>
+ <groupId>clj-time</groupId>
+ <artifactId>clj-time</artifactId>
+ <version>0.14.2</version>
+ </dependency>
+ <dependency>
+ <groupId>integrant</groupId>
+ <artifactId>integrant</artifactId>
+ <version>0.6.2</version>
+ </dependency>
+ <dependency>
+ <groupId>yogthos</groupId>
+ <artifactId>config</artifactId>
+ <version>0.9</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.aai.event-client</groupId>
+ <artifactId>event-client-dmaap</artifactId>
+ <version>1.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.aai.logging-service</groupId>
+ <artifactId>common-logging</artifactId>
+ <version>1.2.2</version>
+ </dependency>
+ <dependency>
+ <groupId>camel-snake-kebab</groupId>
+ <artifactId>camel-snake-kebab</artifactId>
+ <version>0.4.0</version>
+ </dependency>
+ <dependency>
+ <groupId>metosin</groupId>
+ <artifactId>ring-http-response</artifactId>
+ <version>0.9.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.clojure</groupId>
+ <artifactId>test.check</artifactId>
+ <version>0.9.0</version>
+ </dependency>
+ <dependency>
+ <groupId>cloverage</groupId>
+ <artifactId>cloverage</artifactId>
+ <version>1.0.10</version>
+ </dependency>
+ <dependency>
+ <groupId>ring</groupId>
+ <artifactId>ring-devel</artifactId>
+ <version>1.6.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>integrant</groupId>
+ <artifactId>repl</artifactId>
+ <version>0.2.0</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
+
+<!-- This file was autogenerated by Leiningen.
+ Please do not edit it directly; instead edit project.clj and regenerate it.
+ It should not be considered canonical data. For more information see
+ https://github.com/technomancy/leiningen -->
diff --git a/prod/chameleon/server.clj b/prod/chameleon/server.clj
index b9b38db..12c2c02 100644
--- a/prod/chameleon/server.clj
+++ b/prod/chameleon/server.clj
@@ -3,16 +3,18 @@
[chameleon.handler :refer [handler]]
[config.core :refer [env]]
[org.httpkit.server :refer [run-server]]
- [integrant.core :as ig])
+ [integrant.core :as ig]
+ [chameleon.specs :as cs])
(:gen-class))
(defn -main [& args]
(let [port (Integer/parseInt (or (env :http-port) "8082"))
system-config (read-string (slurp (System/getenv "CONFIG_LOCATION" )))
event-config (:event-config system-config)
- route-config (:gallifrey-host system-config)]
+ route-config (:gallifrey-host system-config)
+ log-config (:log-config system-config)]
(println "Listening on port" port)
- (ig/init (config {
- :event-config event-config
+ (ig/init (config {:event-config event-config
:gallifrey-host route-config
- :http-port port}))))
+ :http-port port
+ :log-config log-config}))))
diff --git a/project.clj b/project.clj
index 044856b..430173c 100644
--- a/project.clj
+++ b/project.clj
@@ -1,5 +1,5 @@
(defproject chameleon "0.1.0"
- :dependencies [[org.clojure/clojure "1.8.0"]
+ :dependencies [[org.clojure/clojure "1.9.0"]
[com.7theta/utilis "1.0.4"]
[http-kit "2.2.0"]
[ring/ring-core "1.6.3"]
@@ -11,15 +11,18 @@
[inflections "0.13.0"]
[clj-time "0.14.2"]
[integrant "0.6.2"]
- [clojure-future-spec "1.9.0-beta4"]
[yogthos/config "0.9"]
[org.onap.aai.event-client/event-client-dmaap "1.2.1"]
- ]
+ [org.onap.aai.logging-service/common-logging "1.2.2"]
+ [camel-snake-kebab "0.4.0"]
+ [metosin/ring-http-response "0.9.0"]
+ [org.clojure/test.check "0.9.0"]
+ [cloverage/cloverage "1.0.10"]]
+ :plugins [[lein-cloverage "1.0.10"]]
:repositories [["onap-releases" {:url "https://nexus.onap.org/content/repositories/releases/"}]
["onap-public" {:url "https://nexus.onap.org/content/repositories/public/"}]
["onap-staging" {:url "https://nexus.onap.org/content/repositories/staging/"}]
- ["onap-snapshot" {:url "https://nexus.onap.org/content/repositories/snapshots/"}]
- ]
+ ["onap-snapshot" {:url "https://nexus.onap.org/content/repositories/snapshots/"}]]
:min-lein-version "2.5.3"
:profiles {:dev {:source-paths ["dev"]
:dependencies [[ring/ring-devel "1.6.3"]
@@ -28,4 +31,31 @@
:main chameleon.server
:aot [chameleon.server]
:uberjar-name "chameleon.jar"}}
- :prep-tasks [ "compile"])
+ :prep-tasks ["compile"]
+ :source-paths ["src" "prod" "test"]
+ :resource-paths ["resources"]
+ :pom-plugins [[com.theoryinpractise/clojure-maven-plugin "1.3.13"
+ {:extensions "true"
+ :configuration [:sourceDirectories
+ [:sourceDirectory "src"]
+ [:sourceDirectory "prod"]
+ [:sourceDirectory "test"]]
+ :executions ([:execution [:id "compile"]
+ [:goals ([:goal "compile"])]
+ [:phase "compile"]])}]
+ [org.apache.maven.plugins/maven-jar-plugin "2.4"
+ {:configuration [:archive [:manifest
+ [:addClasspath true]
+ [:mainClass "chameleon.server"]
+ [:classpathPrefix "dependency"]]]}]
+ [org.apache.maven.plugins/maven-dependency-plugin "2.8"
+ {:executions ([:execution [:id "copy-dependencies"]
+ [:goals ([:goal "copy-dependencies"])]
+ [:phase "package"]])}]
+ [org.apache.maven.plugins/maven-assembly-plugin "2.4.1"
+ {:configuration ([:descriptorRefs [:descriptorRef "jar-with-dependencies"]]
+ [:archive [:manifest
+ [:mainClass "chameleon.server"]]])
+ :executions ([:execution [:id "assemble"]
+ [:phase "package"]
+ [:goals ([:goal "single"])]])}]])
diff --git a/resources/chameleon_logback.xml b/resources/chameleon_logback.xml
new file mode 100644
index 0000000..5b723d7
--- /dev/null
+++ b/resources/chameleon_logback.xml
@@ -0,0 +1,109 @@
+<configuration scan="true" scanPeriod="3 seconds" debug="false">
+ <!--<jmxConfigurator /> -->
+ <!-- directory path for all other type logs -->
+
+ <property name="logDir" value="/opt/chameleon/logs" />
+
+ <!-- specify the component name -->
+ <property name="componentName" value="AAI-CHAMELEON" />
+
+ <!-- default eelf log file names -->
+ <property name="generalLogName" value="error" />
+ <property name="metricsLogName" value="metrics" />
+ <property name="auditLogName" value="audit" />
+ <property name="debugLogName" value="debug" />
+
+ <property name="errorLogPattern" value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%mdc{RequestId}|%thread|DataRouter|%mdc{PartnerName}|%logger||%.-5level|%msg%n" />
+
+ <property name="auditMetricPattern" value="%msg%n" />
+
+ <property name="logDirectory" value="${logDir}/${componentName}" />
+
+ <!-- ============================================================================ -->
+ <!-- EELF Appenders -->
+ <!-- ============================================================================ -->
+
+ <!-- The EELFAppender is used to record events to the general application
+ log -->
+
+ <appender name="EELF"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${generalLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${logDirectory}/${generalLogName}.%d{yyyy-MM-dd}.log.zip
+ </fileNamePattern>
+ <maxHistory>60</maxHistory>
+ </rollingPolicy>
+ <encoder>
+ <pattern>${errorLogPattern}</pattern>
+ <outputPatternAsHeader>true</outputPatternAsHeader>
+ </encoder>
+ </appender>
+
+ <appender name="asyncEELF" class="ch.qos.logback.classic.AsyncAppender">
+ <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELF" />
+ </appender>
+
+
+ <!-- EELF Audit Appender. This appender is used to record audit engine
+ related logging events. The audit logger and appender are specializations
+ of the EELF application root logger and appender. This can be used to segregate
+ Policy engine events from other components, or it can be eliminated to record
+ these events as part of the application root log. -->
+
+ <appender name="EELFAudit"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${auditLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${logDirectory}/${auditLogName}.%d{yyyy-MM-dd}.log.zip
+ </fileNamePattern>
+ <maxHistory>60</maxHistory>
+ </rollingPolicy>
+ <encoder>
+ <outputPatternAsHeader>true</outputPatternAsHeader>
+ <pattern>${auditMetricPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="asyncEELFAudit" class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFAudit" />
+ </appender>
+ <!-- ============================================================================ -->
+ <!-- EELF loggers -->
+ <!-- ============================================================================ -->
+
+ <!-- CRUD Service loggers -->
+ <logger name="chameleon" level="INFO">
+ <appender-ref ref="asyncEELF" />
+ </logger>
+
+ <logger name="com.att.eelf.audit" level="info" additivity="false">
+ <appender-ref ref="asyncEELFAudit" />
+ </logger>
+
+ <!-- Other Loggers that may help troubleshoot -->
+ <logger name="net.sf" level="WARN" />
+ <logger name="org.apache" level="WARN" />
+ <logger name="org.apache.commons.httpclient" level="WARN" />
+ <logger name="org.apache.commons" level="WARN" />
+ <logger name="org.apache.coyote" level="WARN" />
+ <logger name="org.apache.jasper" level="WARN" />
+
+ <!-- logback internals logging -->
+ <logger name="ch.qos.logback.classic" level="WARN" />
+ <logger name="ch.qos.logback.core" level="WARN" />
+
+ <root>
+ <!-- <appender-ref ref="asyncEELF" /> -->
+ <!-- <appender-ref ref="asyncEELFDebug" /> -->
+ </root>
+
+</configuration>
diff --git a/resources/log/ChameleonMsgs.properties b/resources/log/ChameleonMsgs.properties
new file mode 100644
index 0000000..4e61e2a
--- /dev/null
+++ b/resources/log/ChameleonMsgs.properties
@@ -0,0 +1,19 @@
+EVENT_PROCESSOR=\
+CHM0001I|\
+Event Processor {0}\
+
+GALLIFREY_ASSERTION=\
+CHM0002I|\
+Attempt Gallifrey Assertion of operation {0} with type {1} and key {2} \
+
+GALLIFREY_ASSERTED=\
+CHM0002I|\
+Asserted type {0} and key {1} in Gallifrey\
+
+RESPONSE=\
+CHM0003I|\
+Response for operation "{0}" for endpoint "{1}" resulted in status "{2}" with body "{3}"\
+
+CHAMELEON_REQUEST=\
+CHM0004I|\
+Incoming Request of type "{0}" for endpoint "{1}" from address "{2}"\ \ No newline at end of file
diff --git a/src/chameleon/aai_processor.clj b/src/chameleon/aai_processor.clj
index d1a7d25..4243b39 100644
--- a/src/chameleon/aai_processor.clj
+++ b/src/chameleon/aai_processor.clj
@@ -1,9 +1,8 @@
(ns chameleon.aai-processor
- (:require
- [chameleon.route :refer :all]
- [cheshire.core :refer :all]
- [integrant.core :as ig]
- [clojure.set :refer :all]))
+ (:require [chameleon.route :refer :all]
+ [cheshire.core :refer :all]
+ [integrant.core :as ig]
+ [clojure.set :refer :all]))
(defonce ^:private p-attr (atom nil))
(defonce ^:private t-attr (atom nil))
@@ -33,8 +32,7 @@
{"id" id
"type" type
"source" {"id" src-id "type" src-type}
- "target" {"id" target-id "type" target-type}})
- )
+ "target" {"id" target-id "type" target-type}}))
(defn from-gallifrey
"Transforms Gallifrey response payloads into a format consumable by AAI-centric clients"
@@ -42,27 +40,21 @@
(let [resource-type (get-in body ["properties" "_type"])
id (body "_id")
type (get-in body ["properties" "type"])
- properties (body "properties")]
+ properties (body "properties")
+ entity-response {"id" id
+ "type" type
+ "properties" (dissoc properties "_type" "type")}]
(if (= resource-type "entity")
- ; Transform into an entity type
+ ;; Transform into an entity type
(let [relationships (body "relationships")]
- {
- "id" id
- "type" type
- "properties" (dissoc properties "_type" "type")
- "in" (into [] (map gen-trim-relationship (filter #(= (get-in % ["target" "id"]) id) relationships)))
- "out" (into [] (map gen-trim-relationship (filter #(= (get-in % ["source" "id"]) id) relationships)))
- })
- ; Transform into a relationship type
- {
- "id" id
- "type" type
- "properties" (dissoc properties "_type" "type")
- })))
+ (assoc entity-response
+ "in" (into [] (map gen-trim-relationship (filter #(= (get-in % ["target" "id"]) id) relationships)))
+ "out" (into [] (map gen-trim-relationship (filter #(= (get-in % ["source" "id"]) id) relationships)))))
+ entity-response)))
(defn from-spike
"Transforms Spike-based event payloads to a format accepted by Gallifrey for vertices and relationships"
- [gallifrey-host payload]
+ [gallifrey-host payload & [error-logger audit-logger]]
(let [txpayload (map-keywords (parse-string payload))
operation (:operation txpayload)
parse-type (if (contains? txpayload :vertex)
@@ -74,13 +66,14 @@
entity (map-keywords (parse-type txpayload))
key (:key entity)
properties (assoc (:properties entity) :type (:type entity))
- truth-time (if (not (nil? (get properties @t-attr))) {:t-t (get properties @t-attr)})
- assertion {:meta {:key key
- :operation operation
- :time truth-time}}
+ truth-time (if (not (nil? (get properties @t-attr))) {:t-t (get properties @t-attr)})
+ assertion {:meta {:key key
+ :operation operation
+ :time truth-time}}
provenance (get properties @p-attr "aai")]
- (assert-gallifrey gallifrey-host provenance (name entity-type) (if (= entity-type :entity)
- (assoc assertion :body (generate-string {:properties properties}))
- (assoc assertion :body (generate-string (conj {:properties properties}
- {:source (rename-keys (:source entity) {"key" "id"})}
- {:target (rename-keys (:target entity) {"key" "id"})})))))))
+ (assert-gallifrey! gallifrey-host provenance (name entity-type)
+ (if (= entity-type :entity)
+ (assoc assertion :body (generate-string {:properties properties}))
+ (assoc assertion :body (generate-string (conj {:properties properties}
+ {:source (rename-keys (:source entity) {"key" "id"})}
+ {:target (rename-keys (:target entity) {"key" "id"})})))) error-logger audit-logger)))
diff --git a/src/chameleon/config.clj b/src/chameleon/config.clj
index bd99096..10324e2 100644
--- a/src/chameleon/config.clj
+++ b/src/chameleon/config.clj
@@ -4,14 +4,16 @@
(defn config
[app-config]
- (let [conf {
+ (let [conf {:chameleon/loggers (:log-config app-config)
:chameleon/event
{:event-config (assoc-in (:event-config app-config)
[:aai :processor] from-spike)
- :gallifrey-host (:gallifrey-host app-config)}
+ :gallifrey-host (:gallifrey-host app-config)
+ :loggers (ig/ref :chameleon/loggers)}
:chameleon/handler
{:gallifrey-host (:gallifrey-host app-config)
- :gallifrey-transformer from-gallifrey}
+ :gallifrey-transformer from-gallifrey
+ :loggers (ig/ref :chameleon/loggers)}
:chameleon/aai-processor
{:provenance-attr "last-mod-source-of-truth"
:truth-attr "truth-time"}
diff --git a/src/chameleon/event.clj b/src/chameleon/event.clj
index c4bec8e..92f4211 100644
--- a/src/chameleon/event.clj
+++ b/src/chameleon/event.clj
@@ -1,19 +1,25 @@
(ns chameleon.event
(:require [integrant.core :as ig]
- [clojure.string :refer [starts-with?]])
+ [clojure.string :refer [starts-with?]]
+ [chameleon.logging :as log])
(:import [org.onap.aai.event.client DMaaPEventConsumer]))
(defmethod ig/init-key :chameleon/event
- [_ {:keys [event-config gallifrey-host]}]
+ [_ {:keys [event-config gallifrey-host loggers]}]
(let [{:keys [host topic motsid pass consumer-group consumer-id timeout batch-size type processor]} (:aai event-config)
+ [error-logger audit-logger] loggers
event-processor (DMaaPEventConsumer. host topic motsid pass consumer-group consumer-id timeout batch-size type)]
- (println "Event processor for AAI created. Starting event polling on " host topic)
+ (log/info error-logger "EVENT_PROCESSOR" [(format "AAI created. Starting event polling on %s %s" host topic) ])
(.start (Thread. (fn [] (while true
(let [it (.iterator (.consume event-processor))]
- (println "Polling...")
+ (log/info error-logger "EVENT_PROCESSOR" ["Polling ..."])
(while (.hasNext it)
+ (log/mdc-init! "SPIKE-EVENT" "CHAMELEON" "" "" gallifrey-host)
(try (let [event (.next it)]
- (if (not (starts-with? event "DMAAP")) ;Temporarily added for current version of dmaap client
- (processor gallifrey-host event)))
- (catch Exception e (println (str "Unexpected exception during processing: " (.getMessage e)))))))))))
- ))
+ ;;Temporarily added for current version of dmaap client
+ (when-not (starts-with? event "DMAAP")
+ (log/info error-logger "EVENT_PROCESSOR" [event])
+ (processor gallifrey-host event error-logger audit-logger)
+ (log/mdc-clear!)))
+ (catch Exception e
+ (println (str "Unexpected exception during processing: " (.getMessage e)))))))))))))
diff --git a/src/chameleon/handler.clj b/src/chameleon/handler.clj
index fd97bf1..675a34b 100644
--- a/src/chameleon/handler.clj
+++ b/src/chameleon/handler.clj
@@ -10,17 +10,18 @@
[ring.middleware.session :refer [wrap-session]]
[cheshire.core :as json]
[clj-time.format :as tf]
- [integrant.core :as ig]))
+ [integrant.core :as ig]
+ [chameleon.logging :as log]))
(declare handler)
(defonce ^:private g-host (atom nil))
(defonce ^:private g-transformer nil)
-(defmethod ig/init-key :chameleon/handler [_ {:keys [gallifrey-host gallifrey-transformer]}]
+(defmethod ig/init-key :chameleon/handler [_ {:keys [gallifrey-host loggers gallifrey-transformer]}]
(reset! g-host gallifrey-host)
(def g-transformer gallifrey-transformer)
- handler)
+ (handler loggers))
(defmethod ig/halt-key! :chameleon/handler [_ _]
(reset! g-host nil)
@@ -32,10 +33,10 @@
:allowed-methods [:get]
:available-media-types ["application/json"]
:exists? (fn [ctx]
- (let [resource (c-route/query @g-host id type (-> ctx
- :request
- :params
- (select-keys [:t-t :t-k])))] ; Only pass through the allowable set of keys
+ (let [resource (c-route/query @g-host id type (-> ctx
+ :request
+ :params
+ (select-keys [:t-t :t-k])))] ; Only pass through the allowable set of keys
(when (= (:status resource) 200)
{::resource (-> resource
:body
@@ -56,9 +57,27 @@
(GET "/relationship/:id" [id] (resource-endpoint "relationship" id))
(resources "/"))
-(def handler
+(defn log-reqs
+ [handler loggers]
+ (let [[error-logger audit-logger] loggers]
+ (fn [request]
+ (log/mdc-init! (get-in request [:headers "X-TransactionId"]) "CHAMELEON"
+ "CHAMELEON_SERVICE" "ONAP" (:remote-addr request))
+ (log/info error-logger "CHAMELEON_REQUEST" (mapv str ((juxt (comp name :request-method) :uri :remote-addr) request)))
+ (let [resp (handler request)
+ fields (->> ((juxt :status :body) resp)
+ (into ((juxt (comp name :request-method) :uri) request))
+ (mapv str))]
+ (log/info error-logger "RESPONSE" fields)
+ (log/info audit-logger "RESPONSE" fields)
+ (log/mdc-clear!)
+ resp))))
+
+(defn handler
+ [loggers]
(-> app-routes
- (wrap-defaults api-defaults)))
+ (wrap-defaults api-defaults)
+ (log-reqs loggers)))
;;; Implementation
diff --git a/src/chameleon/logging.clj b/src/chameleon/logging.clj
new file mode 100644
index 0000000..15e1e7b
--- /dev/null
+++ b/src/chameleon/logging.clj
@@ -0,0 +1,123 @@
+(ns chameleon.logging
+ (:require [camel-snake-kebab.core :as cs]
+ [camel-snake-kebab.extras :refer [transform-keys]]
+ [clojure.java.io :as io]
+ [integrant.core :as ig]
+ [clojure.spec.alpha :as s])
+ (:import [org.onap.aai.cl.api Logger LogFields LogLine]
+ [org.onap.aai.cl.eelf LoggerFactory LogMessageEnum AaiLoggerAdapter AuditLogLine]
+ [org.onap.aai.cl.mdc MdcContext MdcOverride]
+ [com.att.eelf.i18n EELFResourceManager]
+ [clojure.lang.IFn]
+ [org.slf4j MDC]))
+
+(declare LOGLINE_DEFINED_FIELDS error-logger audit-logger ->java string->enum logfields)
+
+(defmethod ig/init-key :chameleon/loggers
+ [_ {:keys [logback logmsgs] :or {logback (.getPath (io/file "chameleon_logback.xml"))
+ logmsgs "log/ChameleonMsgs"}}]
+ (System/setProperty "logback.configurationFile" logback)
+ (EELFResourceManager/loadMessageBundle logmsgs)
+ [(error-logger "chameleon.loggging") (audit-logger "chameleon.loggging")])
+
+(defn conform-multiple
+ [& spec-form-pair]
+ (if (s/valid? :chameleon.specs/spec-form-pair spec-form-pair)
+ (->> spec-form-pair
+ (partition 2)
+ (map (fn [[sp form]]
+ (when (s/invalid? (s/conform sp form))
+ (s/explain-data sp form))))
+ (remove nil?))
+ (s/explain-data :chameleon.specs/spec-form-pair spec-form-pair)))
+
+(defn mdc-set!
+ "Sets the global MDC context for the current thread."
+ [m]
+ (doseq [[k v] m] (MDC/put k v)))
+
+(defn mdc-init!
+ "Sets the global MDC context as required by the EELF logging library"
+ [transaction-id service instance partner client-address]
+ (MdcContext/initialize transaction-id service instance partner client-address))
+
+(defn mdc-clear! [] (MDC/clear))
+
+(defmacro with-mdc
+ "Will set the global MDC context with the options (will convert from
+ keywords to PascalCase), execute the log, and clear the MDC
+ context."
+ [opts & body]
+ `(do
+ (mdc-set! (transform-keys cs/->PascalCaseString ~opts))
+ ~@body
+ (mdc-clear)))
+
+(defn mdc-override
+ [m]
+ (->java (fn [j k v] (.addAttribute j k v)) (new MdcOverride)
+ (transform-keys cs/->PascalCaseString m)))
+
+(defn error-logger
+ [name]
+ (.getLogger (LoggerFactory/getInstance) name))
+
+(defn audit-logger
+ [name]
+ (.getAuditLogger (LoggerFactory/getInstance) name))
+
+(defn logger?
+ [logger]
+ (instance? AaiLoggerAdapter logger))
+
+(defn info
+ [^AaiLoggerAdapter logger ^String enum msgs & {:keys [fields] :or {fields {}}}]
+ (let [confirmed-specs (conform-multiple :logging/valid-fields fields :logging/msgs msgs
+ :chameleon.specs/logger logger)]
+ (if (empty? confirmed-specs)
+ (.info logger (string->enum enum) (logfields fields) (into-array java.lang.String msgs))
+ confirmed-specs)))
+
+(defn debug
+ [^AaiLoggerAdapter logger ^String enum msgs]
+ (.debug logger (string->enum enum) (into-array java.lang.String msgs)))
+
+(defn valid-logfields?
+ [m]
+ (->> (keys m)
+ (map cs/->SCREAMING_SNAKE_CASE_STRING)
+ set
+ (clojure.set/superset? (-> LOGLINE_DEFINED_FIELDS keys set))))
+
+(def ^{:private true
+ :doc "Adding these fields from \"org.onap.aai.cl.api.LogLine\"
+ class. Right now there isn't a known way to use the ENUMs of an
+ abstract JAVA class if they are not STATIC. The field order is very
+ specific and it should be maintained for the common logging library
+ version \"1.2.2\". For a better understanding, please look at the
+ \"DefinedFields\" ENUMs in the org.onap.aai.cl.api.LogLine
+ class"}
+
+ LOGLINE_DEFINED_FIELDS
+ (zipmap ["STATUS_CODE" "RESPONSE_CODE" "RESPONSE_DESCRIPTION" "INSTANCE_UUID"
+ "SEVERITY" "SERVER_IP" "CLIENT_IP" "CLASS_NAME" "PROCESS_KEY"
+ "TARGET_SVC_NAME" "TARGET_ENTITY" "ERROR_CODE" "ERROR_DESCRIPTION"
+ "CUSTOM_1" "CUSTOM_2""CUSTOM_3" "CUSTOM_4"]
+ (range)))
+
+(defn- string->enum
+ ([^String enum] (proxy [Enum LogMessageEnum] [enum (hash enum)]))
+ ([^String enum ordinal] (proxy [Enum LogMessageEnum] [enum ordinal])))
+
+(defn- ->java
+ [f jc m]
+ (reduce (fn [a [k v]] (f a k v) a)
+ jc m))
+
+(defn- logfields
+ "Generate a \"LogFields\" object with all the fields set."
+ [m]
+ (->> m
+ (transform-keys cs/->SCREAMING_SNAKE_CASE_STRING)
+ (transform-keys #(string->enum % (LOGLINE_DEFINED_FIELDS %)))
+ (->java (fn [j field v] (.setField j field v)) (new LogFields))))
diff --git a/src/chameleon/route.clj b/src/chameleon/route.clj
index e8f3a3c..0918da4 100644
--- a/src/chameleon/route.clj
+++ b/src/chameleon/route.clj
@@ -1,12 +1,7 @@
(ns chameleon.route
- (:require [org.httpkit.client :as kitclient]))
-
-(defn- interpret-response
- "Print out the response from the Gallifrey server"
- [key response]
- (let [{:keys [status body]}@response]
- (println "Response for request with key " key " resulted in status " status
- " with body " body )))
+ (:require [org.httpkit.client :as kitclient]
+ [chameleon.logging :as log]
+ [ring.util.http-status :as hs]))
(defn query
"Retrieve an entity referenced by id at the provided host. Optionally provide
@@ -21,11 +16,10 @@
:keepalive 300
:timeout 20000}))
-(defn assert-create
+(defn assert-create!
"Creates an entity in Gallifrey with an initial set of assertions coming from the provided payload"
[host actor type key payload & [time-dimensions]]
- (kitclient/request {
- :url (str "https://" host "/" type "/" key)
+ (kitclient/request {:url (str "https://" host "/" type "/" key)
:method :put
:query-params (into {"actor" actor "create" "true"} time-dimensions)
:body payload
@@ -33,11 +27,10 @@
:keepalive 300
:timeout 1000}))
-(defn assert-update
+(defn assert-update!
"Update an entity in Gallifrey with a set of assertions coming from the provided payload"
[host actor type key payload & [time-dimensions]]
- (kitclient/request {
- :url (str "https://" host "/" type "/" key)
+ (kitclient/request {:url (str "https://" host "/" type "/" key)
:method :put
:query-params (into {"actor" actor "changes-only" "true"} time-dimensions)
:body payload
@@ -45,26 +38,27 @@
:keepalive 300
:timeout 1000}))
-(defn assert-delete
+(defn assert-delete!
"Assert a deletion for an entity in Gallifrey based on the provided key."
[host actor type key & [time-dimensions]]
- (kitclient/request {
- :url (str "https://" host "/" type "/" key)
+ (kitclient/request {:url (str "https://" host "/" type "/" key)
:method :delete
- :query-params (into {"actor" actor} time-dimensions)
+ :query-params (into {"actor" actor} time-dimensions)
:insecure? true
:keepalive 300
:timeout 1000}))
-(defn assert-gallifrey [host actor type payload]
+(defn assert-gallifrey!
+ [host actor type payload & [e-logger a-logger]]
"Propagates an assertion to Gallifrey based off of an event payload coming in from the event service."
(let [{:keys [meta body]} payload
- {:keys [key operation time]} meta]
- (println operation " " type " with key " key)
- (interpret-response key (case operation
- "CREATE"
- (assert-create host actor type key body time)
- "UPDATE"
- (assert-update host actor type key body time)
- "DELETE"
- (assert-delete host actor type key time)))))
+ {:keys [key operation time]} meta
+ _ (log/info e-logger "GALLIFREY_ASSERTION" (mapv str [operation type key]))
+ g-assert (case operation
+ "CREATE" (assert-create! host actor type key body time)
+ "UPDATE" (assert-update! host actor type key body time)
+ "DELETE" (assert-delete! host actor type key time))
+ {:keys [status body]} @g-assert]
+ (log/info e-logger "GALLIFREY_ASSERTED" [(str type) (str key)])
+ (log/info a-logger "RESPONSE" (mapv str [operation key status body])
+ :fields {:response-code status :response-description (hs/get-name status)})))
diff --git a/src/chameleon/specs.clj b/src/chameleon/specs.clj
new file mode 100644
index 0000000..40d5768
--- /dev/null
+++ b/src/chameleon/specs.clj
@@ -0,0 +1,71 @@
+(ns chameleon.specs
+ (:require [clojure.spec.alpha :as s]
+ [chameleon.logging :as log]
+ [clojure.spec.gen.alpha :as gen]
+ [cheshire.core :as c]
+ [clojure.string :as str]))
+
+(s/def ::host string?)
+(s/def ::provenance string?)
+(s/def ::payload map?)
+(s/def ::string (s/spec (s/and string? (complement str/blank?)) :gen gen/string-alphanumeric))
+(s/def ::type (s/spec ::string :gen #(gen/elements ["vserver" "pserver" "generic-vnf"])))
+(s/def ::id uuid?)
+(s/def ::source (s/keys :req-un [::type ::id]))
+(s/def ::target (s/keys :req-un [::type ::id]))
+(s/def ::_id ::id)
+(s/def ::spec-form-pair (s/* (s/cat :spec #(% (s/registry)) :form any?)))
+
+;; spike event
+(s/def :spike/transaction-id uuid?)
+(s/def :spike/schema-version (s/spec #(re-matches #"v\d+" %) :gen #(->> (s/int-in 7 15)
+ s/gen
+ (gen/fmap (partial str "v")))))
+(s/def :spike/key uuid?)
+(s/def :spike/name ::string)
+(s/def :spike/last-mod-source-of-truth ::string)
+(s/def :spike/source-of-truth ::string)
+(s/def :spike/vserver-selflink ::string)
+(s/def :spike/is-closed-loop-disabled boolean?)
+(s/def :spike/in-maint boolean?)
+(s/def :spike/timestamp inst?)
+(s/def :spike/operation (s/spec ::string :gen #(gen/elements ["UPDATE" "CREATE" "DELETE"])))
+(s/def :spike/properties (s/keys :req-un [:spike/name ::id :spike/last-mod-source-of-truth
+ :spike/source-of-truth :spike/vserver-selflink
+ :spike/is-closed-loop-disabled :spike/in-maint]))
+(s/def :spike/vertex (s/keys :req-un [:spike/schema-version ::type :spike/key :spike/properties]))
+(s/def :spike/event (s/keys :req-un [:spike/transaction-id :spike/vertex
+ :spike/operation :spike/timestamp]))
+(s/def :spike/payload (s/spec string? :gen #(gen/fmap (partial c/generate-string)
+ (s/gen :spike/event))))
+(s/def :gallifrey/k-end-actor ::string)
+
+;; gallifrey response
+(s/def :gallifrey/k-start-actor ::string)
+(s/def :gallifrey/k-end inst?)
+(s/def :gallifrey/k-start inst?)
+(s/def :gallifrey/history (s/keys :req-un [:gallifrey/k-end-actor :gallifrey/k-end
+ :gallifrey/k-start-actor :gallifrey/k-start]))
+(s/def :relationship/_meta (s/map-of ::string :gallifrey/history))
+(s/def :relationship/_type (s/spec string? :gen #(gen/return "relationship")))
+(s/def :gallifrey/_type (s/spec ::string :gen #(gen/return "entity")))
+(s/def :gallifrey/properties (s/keys :req-un [:gallifrey/_type ::type]))
+(s/def :relationship/type (s/spec string? :gen #(->> (gen/string-alphanumeric)
+ (gen/such-that (complement str/blank?))
+ (gen/fmap (partial str "tosca.relationship.")))))
+(s/def :relationship/properties (s/keys :req-un [:relationship/_type :relationship/type]))
+(s/def ::relationship (s/keys :req-un [:relationship/properties ::source
+ ::target :relationship/_meta ::_id]))
+(s/def ::relationships (s/coll-of ::relationship :gen-max 8))
+(s/def :gallifrey/payload (s/spec map?
+ :gen #(->> [::_id :gallifrey/properties
+ :gallifrey/properties ::relationships]
+ (s/keys :req-un)
+ s/gen
+ (gen/fmap (partial clojure.walk/stringify-keys)))))
+
+;; Logger specs
+(s/def ::logger (s/spec log/logger? :gen #(gen/return (log/error-logger "chameleon.specs"))))
+(s/def ::loggers (s/cat :e :chameleon.specs/logger :a :chameleon.specs/logger))
+(s/def :logging/msgs (s/* ::string))
+(s/def :logging/valid-fields log/valid-logfields?)
diff --git a/test/chameleon/testing.clj b/test/chameleon/testing.clj
new file mode 100644
index 0000000..13acb1a
--- /dev/null
+++ b/test/chameleon/testing.clj
@@ -0,0 +1,42 @@
+(ns chameleon.testing
+ (:require [clojure.test :as t]
+ [clojure.spec.alpha :as s]
+ [clojure.spec.gen.alpha :as gen]
+ [clojure.spec.test.alpha :as st]
+ [chameleon.logging :as log]
+ [chameleon.specs :as cs]
+ [chameleon.route :as cr]
+ [chameleon.aai-processor :as cai]))
+
+(s/fdef chameleon.route/assert-gallifrey!
+ :args (s/cat :host :chameleon.specs/host
+ :provenance :chameleon.specs/provenance
+ :type :chameleon.specs/type
+ :payload :chameleon.specs/payload
+ :loggers :chameleon.specs/loggers)
+ :ret nil?)
+
+(s/fdef chameleon.aai-processor/from-spike
+ :args (s/cat :gallifrey-host :chameleon.specs/host
+ :payload :spike/payload
+ :loggers :chameleon.specs/loggers)
+ :ret nil?)
+
+(s/fdef chameleon.aai-processor/from-gallifrey
+ :args (s/cat :body :gallifrey/payload)
+ :ret map?)
+
+(s/fdef chameleon.aai-processor/gen-trim-relationship
+ :args (s/cat :relationship :chameleon.specs/relationship)
+ :ret map?)
+
+(st/instrument 'chameleon.route/assert-gallifrey! {:stub '(chameleon.route/assert-gallifrey!)})
+
+;; Testing instrumentation
+(chameleon.route/assert-gallifrey! "host" "aai" "type" {} (log/error-logger "chameleon.testing") (log/audit-logger "chameleon.testing"))
+
+(->> '(chameleon.aai-processor/from-spike
+ chameleon.aai-processor/from-gallifrey
+ chameleon.aai-processor/gen-trim-relationship)
+ st/check
+ st/summarize-results)