mirror of
https://github.com/status-im/open-bounty.git
synced 2025-02-02 04:35:44 +00:00
Luminus skeleton + useless github button
This commit is contained in:
commit
c4bab5a6ed
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
/target
|
||||
/lib
|
||||
/classes
|
||||
/checkouts
|
||||
pom.xml
|
||||
*.jar
|
||||
*.class
|
||||
/.lein-*
|
||||
profiles.clj
|
||||
/.env
|
||||
.nrepl-port
|
||||
/log
|
||||
.idea
|
||||
*.iml
|
||||
*.ipr
|
||||
/resources/public/css/screen.css
|
||||
*.log
|
28
Capstanfile
Normal file
28
Capstanfile
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
#
|
||||
# Name of the base image. Capstan will download this automatically from
|
||||
# Cloudius S3 repository.
|
||||
#
|
||||
#base: cloudius/osv
|
||||
base: cloudius/osv-openjdk8
|
||||
|
||||
#
|
||||
# The command line passed to OSv to start up the application.
|
||||
#
|
||||
cmdline: /java.so -jar /commiteth/app.jar
|
||||
|
||||
#
|
||||
# The command to use to build the application.
|
||||
# You can use any build tool/command (make/rake/lein/boot) - this runs locally on your machine
|
||||
#
|
||||
# For Leiningen, you can use:
|
||||
#build: lein uberjar
|
||||
# For Boot, you can use:
|
||||
#build: boot build
|
||||
|
||||
#
|
||||
# List of files that are included in the generated image.
|
||||
#
|
||||
files:
|
||||
/commiteth/app.jar: ./target/uberjar/commiteth.jar
|
||||
|
8
Dockerfile
Normal file
8
Dockerfile
Normal file
@ -0,0 +1,8 @@
|
||||
FROM java:8-alpine
|
||||
MAINTAINER Your Name <you@example.com>
|
||||
|
||||
ADD target/uberjar/commiteth.jar /commiteth/app.jar
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["java", "-jar", "/commiteth/app.jar"]
|
1
Procfile
Normal file
1
Procfile
Normal file
@ -0,0 +1 @@
|
||||
web: java $JVM_OPTS -cp target/uberjar/commiteth.jar clojure.main -m commiteth.core
|
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# commiteth
|
||||
|
||||
generated using Luminus version "2.9.10.94"
|
||||
|
||||
FIXME
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* You will need [Leiningen][1] 2.0 or above installed.
|
||||
|
||||
* [SassC][2] libsass command-line compiler is required
|
||||
|
||||
[1]: https://github.com/technomancy/leiningen
|
||||
[2]: http://github.com/sass/sassc
|
||||
|
||||
## Running
|
||||
|
||||
lein run
|
||||
lein figwheel
|
||||
lein auto sassc once
|
||||
|
||||
## License
|
||||
|
||||
Copyright © 2016 FIXME
|
10
env/dev/clj/commiteth/dev_middleware.clj
vendored
Normal file
10
env/dev/clj/commiteth/dev_middleware.clj
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
(ns commiteth.dev-middleware
|
||||
(:require [ring.middleware.reload :refer [wrap-reload]]
|
||||
[selmer.middleware :refer [wrap-error-page]]
|
||||
[prone.middleware :refer [wrap-exceptions]]))
|
||||
|
||||
(defn wrap-dev [handler]
|
||||
(-> handler
|
||||
wrap-reload
|
||||
wrap-error-page
|
||||
wrap-exceptions))
|
14
env/dev/clj/commiteth/env.clj
vendored
Normal file
14
env/dev/clj/commiteth/env.clj
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
(ns commiteth.env
|
||||
(:require [selmer.parser :as parser]
|
||||
[clojure.tools.logging :as log]
|
||||
[commiteth.dev-middleware :refer [wrap-dev]]))
|
||||
|
||||
(def defaults
|
||||
{:init
|
||||
(fn []
|
||||
(parser/cache-off!)
|
||||
(log/info "\n-=[commiteth started successfully using the development profile]=-"))
|
||||
:stop
|
||||
(fn []
|
||||
(log/info "\n-=[commiteth has shut down successfully]=-"))
|
||||
:middleware wrap-dev})
|
12
env/dev/clj/commiteth/figwheel.clj
vendored
Normal file
12
env/dev/clj/commiteth/figwheel.clj
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
(ns commiteth.figwheel
|
||||
(:require [figwheel-sidecar.repl-api :as ra]))
|
||||
|
||||
(defn start-fw []
|
||||
(ra/start-figwheel!))
|
||||
|
||||
(defn stop-fw []
|
||||
(ra/stop-figwheel!))
|
||||
|
||||
(defn cljs []
|
||||
(ra/cljs-repl))
|
||||
|
16
env/dev/clj/user.clj
vendored
Normal file
16
env/dev/clj/user.clj
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
(ns user
|
||||
(:require [mount.core :as mount]
|
||||
[commiteth.figwheel :refer [start-fw stop-fw cljs]]
|
||||
commiteth.core))
|
||||
|
||||
(defn start []
|
||||
(mount/start-without #'commiteth.core/repl-server))
|
||||
|
||||
(defn stop []
|
||||
(mount/stop-except #'commiteth.core/repl-server))
|
||||
|
||||
(defn restart []
|
||||
(stop)
|
||||
(start))
|
||||
|
||||
|
14
env/dev/cljs/commiteth/dev.cljs
vendored
Normal file
14
env/dev/cljs/commiteth/dev.cljs
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
(ns ^:figwheel-no-load commiteth.app
|
||||
(:require [commiteth.core :as core]
|
||||
[devtools.core :as devtools]
|
||||
[figwheel.client :as figwheel :include-macros true]))
|
||||
|
||||
(enable-console-print!)
|
||||
|
||||
(figwheel/watch-and-reload
|
||||
:websocket-url "ws://localhost:3449/figwheel-ws"
|
||||
:on-jsload core/mount-components)
|
||||
|
||||
(devtools/install!)
|
||||
|
||||
(core/init!)
|
6
env/dev/resources/config.edn
vendored
Normal file
6
env/dev/resources/config.edn
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{:dev true
|
||||
:port 3000
|
||||
;; when :nrepl-port is set the application starts the nREPL server on load
|
||||
:nrepl-port 7000
|
||||
:github-access-token "c9323a357c2beeebe4e8d618e0fda47dc9e15f62"
|
||||
:github-user "kagel"}
|
39
env/dev/resources/logback.xml
vendored
Normal file
39
env/dev/resources/logback.xml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<!-- encoders are assigned the type
|
||||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg %n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>log/commiteth.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>log/commiteth.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<!-- keep 30 days of history -->
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg %n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<logger name="org.xnio.nio" level="warn">
|
||||
<AppenderRef ref="STDOUT"/>
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<logger name="com.zaxxer.hikari" level="warn">
|
||||
<AppenderRef ref="STDOUT"/>
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
</configuration>
|
11
env/prod/clj/commiteth/env.clj
vendored
Normal file
11
env/prod/clj/commiteth/env.clj
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
(ns commiteth.env
|
||||
(:require [clojure.tools.logging :as log]))
|
||||
|
||||
(def defaults
|
||||
{:init
|
||||
(fn []
|
||||
(log/info "\n-=[commiteth started successfully]=-"))
|
||||
:stop
|
||||
(fn []
|
||||
(log/info "\n-=[commiteth has shut down successfully]=-"))
|
||||
:middleware identity})
|
7
env/prod/cljs/commiteth/prod.cljs
vendored
Normal file
7
env/prod/cljs/commiteth/prod.cljs
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
(ns commiteth.app
|
||||
(:require [commiteth.core :as core]))
|
||||
|
||||
;;ignore println statements in prod
|
||||
(set! *print-fn* (fn [& _]))
|
||||
|
||||
(core/init!)
|
4
env/prod/resources/config.edn
vendored
Normal file
4
env/prod/resources/config.edn
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{:production true
|
||||
:port 3000
|
||||
:github-access-token "c9323a357c2beeebe4e8d618e0fda47dc9e15f62"
|
||||
:github-user "kagel"}
|
28
env/prod/resources/logback.xml
vendored
Normal file
28
env/prod/resources/logback.xml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>log/commiteth.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>log/commiteth.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<!-- keep 30 days of history -->
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg %n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<logger name="org.xnio.nio" level="warn">
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<logger name="com.zaxxer.hikari" level="warn">
|
||||
<AppenderRef ref="FILE"/>
|
||||
</logger>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
</configuration>
|
6
env/test/resources/config.edn
vendored
Normal file
6
env/test/resources/config.edn
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{:test true
|
||||
:port 3001
|
||||
;; when :nrepl-port is set the application starts the nREPL server on load
|
||||
:nrepl-port 7001
|
||||
:github-access-token "c9323a357c2beeebe4e8d618e0fda47dc9e15f62"
|
||||
:github-user "kagel"}
|
143
project.clj
Normal file
143
project.clj
Normal file
@ -0,0 +1,143 @@
|
||||
(defproject commiteth "0.1.0-SNAPSHOT"
|
||||
|
||||
:description "FIXME: write description"
|
||||
:url "http://example.com/FIXME"
|
||||
|
||||
:dependencies [[metosin/compojure-api "1.1.6"]
|
||||
[re-frame "0.8.0"]
|
||||
[cljs-ajax "0.5.8"]
|
||||
[secretary "1.2.3"]
|
||||
[reagent-utils "0.2.0"]
|
||||
[reagent "0.6.0-rc"]
|
||||
[org.clojure/clojurescript "1.9.225" :scope "provided"]
|
||||
[org.clojure/clojure "1.8.0"]
|
||||
[selmer "1.0.7"]
|
||||
[markdown-clj "0.9.89"]
|
||||
[ring-middleware-format "0.7.0"]
|
||||
[metosin/ring-http-response "0.8.0"]
|
||||
[bouncer "1.0.0"]
|
||||
[org.webjars/bootstrap "4.0.0-alpha.3"]
|
||||
[org.webjars/font-awesome "4.6.3"]
|
||||
[org.webjars/bootstrap-social "5.0.0"]
|
||||
[org.webjars.bower/tether "1.3.3"]
|
||||
[org.clojure/tools.logging "0.3.1"]
|
||||
[compojure "1.5.1"]
|
||||
[ring-webjars "0.1.1"]
|
||||
[ring/ring-defaults "0.2.1"]
|
||||
[mount "0.1.10"]
|
||||
[cprop "0.1.9"]
|
||||
[org.clojure/tools.cli "0.3.5"]
|
||||
[luminus-nrepl "0.1.4"]
|
||||
[buddy "1.0.0"]
|
||||
[luminus-migrations "0.2.6"]
|
||||
[conman "0.6.0"]
|
||||
[org.postgresql/postgresql "9.4.1209"]
|
||||
[org.webjars/webjars-locator-jboss-vfs "0.1.0"]
|
||||
[luminus-immutant "0.2.2"]
|
||||
[tentacles "0.5.1"]]
|
||||
|
||||
:min-lein-version "2.0.0"
|
||||
|
||||
:jvm-opts ["-server" "-Dconf=.lein-env"]
|
||||
:source-paths ["src/clj" "src/cljc"]
|
||||
:resource-paths ["resources" "target/cljsbuild"]
|
||||
:target-path "target/%s/"
|
||||
:main commiteth.core
|
||||
:migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")}
|
||||
|
||||
:plugins [[lein-cprop "1.0.1"]
|
||||
[migratus-lein "0.4.1"]
|
||||
[lein-cljsbuild "1.1.3"]
|
||||
[lein-immutant "2.1.0"]
|
||||
[lein-sassc "0.10.4"]
|
||||
[lein-auto "0.1.2"]]
|
||||
:sassc
|
||||
[{:src "resources/scss/screen.scss"
|
||||
:output-to "resources/public/css/screen.css"
|
||||
:style "nested"
|
||||
:import-path "resources/scss"}]
|
||||
|
||||
:auto
|
||||
{"sassc" {:file-pattern #"\.(scss|sass)$" :paths ["resources/scss"]}}
|
||||
|
||||
:hooks [leiningen.sassc]
|
||||
:clean-targets ^{:protect false}
|
||||
[:target-path [:cljsbuild :builds :app :compiler :output-dir] [:cljsbuild :builds :app :compiler :output-to]]
|
||||
:figwheel
|
||||
{:http-server-root "public"
|
||||
:nrepl-port 7002
|
||||
:css-dirs ["resources/public/css"]
|
||||
:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
|
||||
|
||||
|
||||
:profiles
|
||||
{:uberjar {:omit-source true
|
||||
:prep-tasks ["compile" ["cljsbuild" "once" "min"]]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:min
|
||||
{:source-paths ["src/cljc" "src/cljs" "env/prod/cljs"]
|
||||
:compiler
|
||||
{:output-to "target/cljsbuild/public/js/app.js"
|
||||
:externs ["react/externs/react.js"]
|
||||
:optimizations :advanced
|
||||
:pretty-print false
|
||||
:closure-warnings
|
||||
{:externs-validation :off :non-standard-jsdoc :off}}}}}
|
||||
|
||||
|
||||
:aot :all
|
||||
:uberjar-name "commiteth.jar"
|
||||
:source-paths ["env/prod/clj"]
|
||||
:resource-paths ["env/prod/resources"]}
|
||||
|
||||
:dev [:project/dev :profiles/dev]
|
||||
:test [:project/test :profiles/test]
|
||||
|
||||
:project/dev {:dependencies [[prone "1.1.1"]
|
||||
[ring/ring-mock "0.3.0"]
|
||||
[ring/ring-devel "1.5.0"]
|
||||
[pjstadig/humane-test-output "0.8.1"]
|
||||
[doo "0.1.7"]
|
||||
[binaryage/devtools "0.8.1"]
|
||||
[figwheel-sidecar "0.5.4-7"]
|
||||
[com.cemerick/piggieback "0.2.2-SNAPSHOT"]]
|
||||
:plugins [[com.jakemccrary/lein-test-refresh "0.14.0"]
|
||||
[lein-doo "0.1.7"]
|
||||
[lein-figwheel "0.5.4-7"]
|
||||
[org.clojure/clojurescript "1.9.225"]]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:app
|
||||
{:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"]
|
||||
:compiler
|
||||
{:main "commiteth.app"
|
||||
:asset-path "/js/out"
|
||||
:output-to "target/cljsbuild/public/js/app.js"
|
||||
:output-dir "target/cljsbuild/public/js/out"
|
||||
:source-map true
|
||||
:optimizations :none
|
||||
:pretty-print true}}}}
|
||||
|
||||
|
||||
|
||||
:doo {:build "test"}
|
||||
:source-paths ["env/dev/clj" "test/clj"]
|
||||
:resource-paths ["env/dev/resources"]
|
||||
:repl-options {:init-ns user}
|
||||
:injections [(require 'pjstadig.humane-test-output)
|
||||
(pjstadig.humane-test-output/activate!)]}
|
||||
:project/test {:resource-paths ["env/dev/resources" "env/test/resources"]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:test
|
||||
{:source-paths ["src/cljc" "src/cljs" "test/cljs"]
|
||||
:compiler
|
||||
{:output-to "target/test.js"
|
||||
:main "commiteth.doo-runner"
|
||||
:optimizations :whitespace
|
||||
:pretty-print true}}}}
|
||||
|
||||
}
|
||||
:profiles/dev {}
|
||||
:profiles/test {}})
|
0
resources/public/favicon.ico
Normal file
0
resources/public/favicon.ico
Normal file
5
resources/scss/screen.scss
Normal file
5
resources/scss/screen.scss
Normal file
@ -0,0 +1,5 @@
|
||||
html, body {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
height: 100%;
|
||||
padding-top: 0px;
|
||||
}
|
21
resources/sql/queries.sql
Normal file
21
resources/sql/queries.sql
Normal file
@ -0,0 +1,21 @@
|
||||
-- :name create-user! :! :n
|
||||
-- :doc creates a new user record
|
||||
INSERT INTO users
|
||||
(id, first_name, last_name, email, pass)
|
||||
VALUES (:id, :first_name, :last_name, :email, :pass)
|
||||
|
||||
-- :name update-user! :! :n
|
||||
-- :doc update an existing user record
|
||||
UPDATE users
|
||||
SET first_name = :first_name, last_name = :last_name, email = :email
|
||||
WHERE id = :id
|
||||
|
||||
-- :name get-user :? :1
|
||||
-- :doc retrieve a user given the id.
|
||||
SELECT * FROM users
|
||||
WHERE id = :id
|
||||
|
||||
-- :name delete-user! :! :n
|
||||
-- :doc delete a user given the id
|
||||
DELETE FROM users
|
||||
WHERE id = :id
|
59
resources/templates/error.html
Normal file
59
resources/templates/error.html
Normal file
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Something bad happened</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{% style "/assets/bootstrap/css/bootstrap.min.css" %}
|
||||
{% style "/assets/bootstrap/css/bootstrap-theme.min.css" %}
|
||||
<style type="text/css">
|
||||
html {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
html body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
html .container-fluid {
|
||||
display: table;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
html .row-fluid {
|
||||
display: table-cell;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="col-lg-12">
|
||||
<div class="centering text-center">
|
||||
<div class="text-center">
|
||||
<h1><span class="text-danger">Error: {{status}}</span></h1>
|
||||
<hr>
|
||||
{% if title %}
|
||||
<h2 class="without-margin">{{title}}</h2>
|
||||
{% endif %}
|
||||
{% if message %}
|
||||
<h4 class="text-danger">{{message}}</h4>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
27
resources/templates/home.html
Normal file
27
resources/templates/home.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Welcome to commiteth</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<div class="container-fluid">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- scripts and styles -->
|
||||
{% style "/assets/bootstrap/css/bootstrap.min.css" %}
|
||||
{% style "/assets/bootstrap-social/bootstrap-social.css" %}
|
||||
{% style "/assets/font-awesome/css/font-awesome.min.css" %}
|
||||
{% style "/css/screen.css" %}
|
||||
|
||||
<script type="text/javascript">
|
||||
var context = "{{servlet-context}}";
|
||||
var csrfToken = "{{csrf-token}}";
|
||||
</script>
|
||||
{% script "/js/app.js" %}
|
||||
</body>
|
||||
</html>
|
10
src/clj/commiteth/config.clj
Normal file
10
src/clj/commiteth/config.clj
Normal file
@ -0,0 +1,10 @@
|
||||
(ns commiteth.config
|
||||
(:require [cprop.core :refer [load-config]]
|
||||
[cprop.source :as source]
|
||||
[mount.core :refer [args defstate]]))
|
||||
|
||||
(defstate env :start (load-config
|
||||
:merge
|
||||
[(args)
|
||||
(source/from-system-props)
|
||||
(source/from-env)]))
|
58
src/clj/commiteth/core.clj
Normal file
58
src/clj/commiteth/core.clj
Normal file
@ -0,0 +1,58 @@
|
||||
(ns commiteth.core
|
||||
(:require [commiteth.handler :as handler]
|
||||
[luminus.repl-server :as repl]
|
||||
[luminus.http-server :as http]
|
||||
[luminus-migrations.core :as migrations]
|
||||
[commiteth.config :refer [env]]
|
||||
[clojure.tools.cli :refer [parse-opts]]
|
||||
[clojure.tools.logging :as log]
|
||||
[mount.core :as mount])
|
||||
(:gen-class))
|
||||
|
||||
(def cli-options
|
||||
[["-p" "--port PORT" "Port number"
|
||||
:parse-fn #(Integer/parseInt %)]])
|
||||
|
||||
(mount/defstate ^{:on-reload :noop}
|
||||
http-server
|
||||
:start
|
||||
(http/start
|
||||
(-> env
|
||||
(assoc :handler (handler/app))
|
||||
(update :port #(or (-> env :options :port) %))))
|
||||
:stop
|
||||
(http/stop http-server))
|
||||
|
||||
(mount/defstate ^{:on-reload :noop}
|
||||
repl-server
|
||||
:start
|
||||
(when-let [nrepl-port (env :nrepl-port)]
|
||||
(repl/start {:port nrepl-port}))
|
||||
:stop
|
||||
(when repl-server
|
||||
(repl/stop repl-server)))
|
||||
|
||||
|
||||
(defn stop-app []
|
||||
(doseq [component (:stopped (mount/stop))]
|
||||
(log/info component "stopped"))
|
||||
(shutdown-agents))
|
||||
|
||||
(defn start-app [args]
|
||||
(doseq [component (-> args
|
||||
(parse-opts cli-options)
|
||||
mount/start-with-args
|
||||
:started)]
|
||||
(log/info component "started"))
|
||||
(.addShutdownHook (Runtime/getRuntime) (Thread. stop-app)))
|
||||
|
||||
(defn -main [& args]
|
||||
(cond
|
||||
(some #{"migrate" "rollback"} args)
|
||||
(do
|
||||
(mount/start #'commiteth.config/env)
|
||||
(migrations/migrate args (select-keys env [:database-url]))
|
||||
(System/exit 0))
|
||||
:else
|
||||
(start-app args)))
|
||||
|
71
src/clj/commiteth/db/core.clj
Normal file
71
src/clj/commiteth/db/core.clj
Normal file
@ -0,0 +1,71 @@
|
||||
(ns commiteth.db.core
|
||||
(:require
|
||||
[cheshire.core :refer [generate-string parse-string]]
|
||||
[clojure.java.jdbc :as jdbc]
|
||||
[conman.core :as conman]
|
||||
[commiteth.config :refer [env]]
|
||||
[mount.core :refer [defstate]])
|
||||
(:import org.postgresql.util.PGobject
|
||||
java.sql.Array
|
||||
clojure.lang.IPersistentMap
|
||||
clojure.lang.IPersistentVector
|
||||
[java.sql
|
||||
BatchUpdateException
|
||||
Date
|
||||
Timestamp
|
||||
PreparedStatement]))
|
||||
|
||||
(defstate ^:dynamic *db*
|
||||
:start (conman/connect! {:jdbc-url (env :database-url)})
|
||||
:stop (conman/disconnect! *db*))
|
||||
|
||||
(conman/bind-connection *db* "sql/queries.sql")
|
||||
|
||||
(defn to-date [^java.sql.Date sql-date]
|
||||
(-> sql-date (.getTime) (java.util.Date.)))
|
||||
|
||||
(extend-protocol jdbc/IResultSetReadColumn
|
||||
Date
|
||||
(result-set-read-column [v _ _] (to-date v))
|
||||
|
||||
Timestamp
|
||||
(result-set-read-column [v _ _] (to-date v))
|
||||
|
||||
Array
|
||||
(result-set-read-column [v _ _] (vec (.getArray v)))
|
||||
|
||||
PGobject
|
||||
(result-set-read-column [pgobj _metadata _index]
|
||||
(let [type (.getType pgobj)
|
||||
value (.getValue pgobj)]
|
||||
(case type
|
||||
"json" (parse-string value true)
|
||||
"jsonb" (parse-string value true)
|
||||
"citext" (str value)
|
||||
value))))
|
||||
|
||||
(extend-type java.util.Date
|
||||
jdbc/ISQLParameter
|
||||
(set-parameter [v ^PreparedStatement stmt ^long idx]
|
||||
(.setTimestamp stmt idx (Timestamp. (.getTime v)))))
|
||||
|
||||
(defn to-pg-json [value]
|
||||
(doto (PGobject.)
|
||||
(.setType "jsonb")
|
||||
(.setValue (generate-string value))))
|
||||
|
||||
(extend-type clojure.lang.IPersistentVector
|
||||
jdbc/ISQLParameter
|
||||
(set-parameter [v ^java.sql.PreparedStatement stmt ^long idx]
|
||||
(let [conn (.getConnection stmt)
|
||||
meta (.getParameterMetaData stmt)
|
||||
type-name (.getParameterTypeName meta idx)]
|
||||
(if-let [elem-type (when (= (first type-name) \_) (apply str (rest type-name)))]
|
||||
(.setObject stmt idx (.createArrayOf conn elem-type (to-array v)))
|
||||
(.setObject stmt idx (to-pg-json v))))))
|
||||
|
||||
(extend-protocol jdbc/ISQLValue
|
||||
IPersistentMap
|
||||
(sql-value [value] (to-pg-json value))
|
||||
IPersistentVector
|
||||
(sql-value [value] (to-pg-json value)))
|
27
src/clj/commiteth/handler.clj
Normal file
27
src/clj/commiteth/handler.clj
Normal file
@ -0,0 +1,27 @@
|
||||
(ns commiteth.handler
|
||||
(:require [compojure.core :refer [routes wrap-routes]]
|
||||
[commiteth.layout :refer [error-page]]
|
||||
[commiteth.routes.home :refer [home-routes]]
|
||||
[commiteth.routes.services :refer [service-routes]]
|
||||
[compojure.route :as route]
|
||||
[commiteth.env :refer [defaults]]
|
||||
[mount.core :as mount]
|
||||
[commiteth.middleware :as middleware]))
|
||||
|
||||
(mount/defstate init-app
|
||||
:start ((or (:init defaults) identity))
|
||||
:stop ((or (:stop defaults) identity)))
|
||||
|
||||
(def app-routes
|
||||
(routes
|
||||
(-> #'home-routes
|
||||
(wrap-routes middleware/wrap-csrf)
|
||||
(wrap-routes middleware/wrap-formats))
|
||||
#'service-routes
|
||||
(route/not-found
|
||||
(:body
|
||||
(error-page {:status 404
|
||||
:title "page not found"})))))
|
||||
|
||||
|
||||
(defn app [] (middleware/wrap-base #'app-routes))
|
39
src/clj/commiteth/layout.clj
Normal file
39
src/clj/commiteth/layout.clj
Normal file
@ -0,0 +1,39 @@
|
||||
(ns commiteth.layout
|
||||
(:require [selmer.parser :as parser]
|
||||
[selmer.filters :as filters]
|
||||
[markdown.core :refer [md-to-html-string]]
|
||||
[ring.util.http-response :refer [content-type ok]]
|
||||
[ring.util.anti-forgery :refer [anti-forgery-field]]
|
||||
[ring.middleware.anti-forgery :refer [*anti-forgery-token*]]))
|
||||
|
||||
(declare ^:dynamic *identity*)
|
||||
(declare ^:dynamic *app-context*)
|
||||
(parser/set-resource-path! (clojure.java.io/resource "templates"))
|
||||
(parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field)))
|
||||
(filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)]))
|
||||
|
||||
(defn render
|
||||
"renders the HTML template located relative to resources/templates"
|
||||
[template & [params]]
|
||||
(content-type
|
||||
(ok
|
||||
(parser/render-file
|
||||
template
|
||||
(assoc params
|
||||
:page template
|
||||
:csrf-token *anti-forgery-token*
|
||||
:servlet-context *app-context*)))
|
||||
"text/html; charset=utf-8"))
|
||||
|
||||
(defn error-page
|
||||
"error-details should be a map containing the following keys:
|
||||
:status - error status
|
||||
:title - error title (optional)
|
||||
:message - detailed error message (optional)
|
||||
|
||||
returns a response map with the error page as the body
|
||||
and the status specified by the status key"
|
||||
[error-details]
|
||||
{:status (:status error-details)
|
||||
:headers {"Content-Type" "text/html; charset=utf-8"}
|
||||
:body (parser/render-file "error.html" error-details)})
|
93
src/clj/commiteth/middleware.clj
Normal file
93
src/clj/commiteth/middleware.clj
Normal file
@ -0,0 +1,93 @@
|
||||
(ns commiteth.middleware
|
||||
(:require [commiteth.env :refer [defaults]]
|
||||
[clojure.tools.logging :as log]
|
||||
[commiteth.layout :refer [*app-context* error-page]]
|
||||
[ring.middleware.anti-forgery :refer [wrap-anti-forgery]]
|
||||
[ring.middleware.webjars :refer [wrap-webjars]]
|
||||
[ring.middleware.format :refer [wrap-restful-format]]
|
||||
[commiteth.config :refer [env]]
|
||||
[ring.middleware.flash :refer [wrap-flash]]
|
||||
[immutant.web.middleware :refer [wrap-session]]
|
||||
[ring.middleware.defaults :refer [site-defaults wrap-defaults]]
|
||||
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]
|
||||
[buddy.auth.backends.session :refer [session-backend]]
|
||||
[buddy.auth.accessrules :refer [restrict]]
|
||||
[buddy.auth :refer [authenticated?]]
|
||||
[commiteth.layout :refer [*identity*]])
|
||||
(:import [javax.servlet ServletContext]))
|
||||
|
||||
(defn wrap-context [handler]
|
||||
(fn [request]
|
||||
(binding [*app-context*
|
||||
(if-let [context (:servlet-context request)]
|
||||
;; If we're not inside a servlet environment
|
||||
;; (for example when using mock requests), then
|
||||
;; .getContextPath might not exist
|
||||
(try (.getContextPath ^ServletContext context)
|
||||
(catch IllegalArgumentException _ context))
|
||||
;; if the context is not specified in the request
|
||||
;; we check if one has been specified in the environment
|
||||
;; instead
|
||||
(:app-context env))]
|
||||
(handler request))))
|
||||
|
||||
(defn wrap-internal-error [handler]
|
||||
(fn [req]
|
||||
(try
|
||||
(handler req)
|
||||
(catch Throwable t
|
||||
(log/error t)
|
||||
(error-page {:status 500
|
||||
:title "Something very bad has happened!"
|
||||
:message "We've dispatched a team of highly trained gnomes to take care of the problem."})))))
|
||||
|
||||
(defn wrap-csrf [handler]
|
||||
(wrap-anti-forgery
|
||||
handler
|
||||
{:error-response
|
||||
(error-page
|
||||
{:status 403
|
||||
:title "Invalid anti-forgery token"})}))
|
||||
|
||||
(defn wrap-formats [handler]
|
||||
(let [wrapped (wrap-restful-format
|
||||
handler
|
||||
{:formats [:json-kw :transit-json :transit-msgpack]})]
|
||||
(fn [request]
|
||||
;; disable wrap-formats for websockets
|
||||
;; since they're not compatible with this middleware
|
||||
((if (:websocket? request) handler wrapped) request))))
|
||||
|
||||
(defn on-error [request response]
|
||||
(error-page
|
||||
{:status 403
|
||||
:title (str "Access to " (:uri request) " is not authorized")}))
|
||||
|
||||
(defn wrap-restricted [handler]
|
||||
(restrict handler {:handler authenticated?
|
||||
:on-error on-error}))
|
||||
|
||||
(defn wrap-identity [handler]
|
||||
(fn [request]
|
||||
(binding [*identity* (get-in request [:session :identity])]
|
||||
(handler request))))
|
||||
|
||||
(defn wrap-auth [handler]
|
||||
(let [backend (session-backend)]
|
||||
(-> handler
|
||||
wrap-identity
|
||||
(wrap-authentication backend)
|
||||
(wrap-authorization backend))))
|
||||
|
||||
(defn wrap-base [handler]
|
||||
(-> ((:middleware defaults) handler)
|
||||
wrap-auth
|
||||
wrap-webjars
|
||||
wrap-flash
|
||||
(wrap-session {:cookie-attrs {:http-only true}})
|
||||
(wrap-defaults
|
||||
(-> site-defaults
|
||||
(assoc-in [:security :anti-forgery] false)
|
||||
(dissoc :session)))
|
||||
wrap-context
|
||||
wrap-internal-error))
|
14
src/clj/commiteth/routes/home.clj
Normal file
14
src/clj/commiteth/routes/home.clj
Normal file
@ -0,0 +1,14 @@
|
||||
(ns commiteth.routes.home
|
||||
(:require [commiteth.layout :as layout]
|
||||
[compojure.core :refer [defroutes GET]]
|
||||
[ring.util.http-response :as response]
|
||||
[clojure.java.io :as io]))
|
||||
|
||||
(defn home-page []
|
||||
(layout/render "home.html"))
|
||||
|
||||
(defroutes home-routes
|
||||
(GET "/" [] (home-page))
|
||||
(GET "/docs" [] (-> (response/ok (-> "docs/docs.md" io/resource slurp))
|
||||
(response/header "Content-Type" "text/plain; charset=utf-8"))))
|
||||
|
66
src/clj/commiteth/routes/services.clj
Normal file
66
src/clj/commiteth/routes/services.clj
Normal file
@ -0,0 +1,66 @@
|
||||
(ns commiteth.routes.services
|
||||
(:require [ring.util.http-response :refer :all]
|
||||
[compojure.api.sweet :refer :all]
|
||||
[schema.core :as s]
|
||||
[compojure.api.meta :refer [restructure-param]]
|
||||
[buddy.auth.accessrules :refer [restrict]]
|
||||
[buddy.auth :refer [authenticated?]]))
|
||||
|
||||
(defn access-error [_ _]
|
||||
(unauthorized {:error "unauthorized"}))
|
||||
|
||||
(defn wrap-restricted [handler rule]
|
||||
(restrict handler {:handler rule
|
||||
:on-error access-error}))
|
||||
|
||||
(defmethod restructure-param :auth-rules
|
||||
[_ rule acc]
|
||||
(update-in acc [:middleware] conj [wrap-restricted rule]))
|
||||
|
||||
(defmethod restructure-param :current-user
|
||||
[_ binding acc]
|
||||
(update-in acc [:letks] into [binding `(:identity ~'+compojure-api-request+)]))
|
||||
|
||||
(defapi service-routes
|
||||
{:swagger {:ui "/swagger-ui"
|
||||
:spec "/swagger.json"
|
||||
:data {:info {:version "1.0.0"
|
||||
:title "Sample API"
|
||||
:description "Sample Services"}}}}
|
||||
|
||||
(GET "/authenticated" []
|
||||
:auth-rules authenticated?
|
||||
:current-user user
|
||||
(ok {:user user}))
|
||||
(context "/api" []
|
||||
:tags ["thingie"]
|
||||
|
||||
(GET "/plus" []
|
||||
:return Long
|
||||
:query-params [x :- Long, {y :- Long 1}]
|
||||
:summary "x+y with query-parameters. y defaults to 1."
|
||||
(ok (+ x y)))
|
||||
|
||||
(POST "/minus" []
|
||||
:return Long
|
||||
:body-params [x :- Long, y :- Long]
|
||||
:summary "x-y with body-parameters."
|
||||
(ok (- x y)))
|
||||
|
||||
(GET "/times/:x/:y" []
|
||||
:return Long
|
||||
:path-params [x :- Long, y :- Long]
|
||||
:summary "x*y with path-parameters"
|
||||
(ok (* x y)))
|
||||
|
||||
(POST "/divide" []
|
||||
:return Double
|
||||
:form-params [x :- Long, y :- Long]
|
||||
:summary "x/y with form-parameters"
|
||||
(ok (/ x y)))
|
||||
|
||||
(GET "/power" []
|
||||
:return Long
|
||||
:header-params [x :- Long, y :- Long]
|
||||
:summary "x^y with header-parameters"
|
||||
(ok (long (Math/pow x y))))))
|
3
src/cljc/commiteth/validation.cljc
Normal file
3
src/cljc/commiteth/validation.cljc
Normal file
@ -0,0 +1,3 @@
|
||||
(ns commiteth.validation
|
||||
(:require [bouncer.core :as b]
|
||||
[bouncer.validators :as v]))
|
18
src/cljs/commiteth/ajax.cljs
Normal file
18
src/cljs/commiteth/ajax.cljs
Normal file
@ -0,0 +1,18 @@
|
||||
(ns commiteth.ajax
|
||||
(:require [ajax.core :as ajax]))
|
||||
|
||||
(defn default-headers [request]
|
||||
(-> request
|
||||
(update :uri #(str js/context %))
|
||||
(update
|
||||
:headers
|
||||
#(merge
|
||||
%
|
||||
{"Accept" "application/transit+json"
|
||||
"x-csrf-token" js/csrfToken}))))
|
||||
|
||||
(defn load-interceptors! []
|
||||
(swap! ajax/default-interceptors
|
||||
conj
|
||||
(ajax/to-interceptor {:name "default headers"
|
||||
:request default-headers})))
|
76
src/cljs/commiteth/core.cljs
Normal file
76
src/cljs/commiteth/core.cljs
Normal file
@ -0,0 +1,76 @@
|
||||
(ns commiteth.core
|
||||
(:require [reagent.core :as r]
|
||||
[re-frame.core :as rf]
|
||||
[secretary.core :as secretary]
|
||||
[goog.events :as events]
|
||||
[goog.history.EventType :as HistoryEventType]
|
||||
[markdown.core :refer [md->html]]
|
||||
[ajax.core :refer [GET POST]]
|
||||
[commiteth.ajax :refer [load-interceptors!]]
|
||||
[commiteth.handlers]
|
||||
[commiteth.subscriptions])
|
||||
(:import goog.History))
|
||||
|
||||
(defn nav-link [uri title page collapsed?]
|
||||
(let [selected-page (rf/subscribe [:page])] [:li.nav-item
|
||||
{:class (when (= page @selected-page) "active")}
|
||||
[:a.nav-link
|
||||
{:href uri
|
||||
:on-click #(reset! collapsed? true)} title]]))
|
||||
|
||||
(defn navbar []
|
||||
(r/with-let [collapsed? (r/atom true)]
|
||||
[:nav.navbar.navbar-light.bg-faded
|
||||
[:button.navbar-toggler.hidden-sm-up
|
||||
{:on-click #(swap! collapsed? not)} "☰"]
|
||||
[:div.collapse.navbar-toggleable-xs
|
||||
(when-not @collapsed? {:class "in"})
|
||||
[:a.navbar-brand {:href "#/"} "commiteth"]
|
||||
[:ul.nav.navbar-nav
|
||||
[nav-link "#/" "Home" :home collapsed?]]]]))
|
||||
|
||||
(defn home-page []
|
||||
[:div.container
|
||||
[:div.jumbotron
|
||||
[:h1 "Welcome to commitETH"]
|
||||
[:p [:a.btn.btn-block.btn-social.btn-github
|
||||
{:href "http://github.com"}
|
||||
[:i.fa.fa-github]
|
||||
"Sign in with GitHub"]]]])
|
||||
|
||||
(def pages
|
||||
{:home #'home-page})
|
||||
|
||||
(defn page []
|
||||
[:div
|
||||
[navbar]
|
||||
[(pages @(rf/subscribe [:page]))]])
|
||||
|
||||
;; -------------------------
|
||||
;; Routes
|
||||
(secretary/set-config! :prefix "#")
|
||||
|
||||
(secretary/defroute "/" []
|
||||
(rf/dispatch [:set-active-page :home]))
|
||||
|
||||
;; -------------------------
|
||||
;; History
|
||||
;; must be called after routes have been defined
|
||||
(defn hook-browser-navigation! []
|
||||
(doto (History.)
|
||||
(events/listen
|
||||
HistoryEventType/NAVIGATE
|
||||
(fn [event]
|
||||
(secretary/dispatch! (.-token event))))
|
||||
(.setEnabled true)))
|
||||
|
||||
;; -------------------------
|
||||
;; Initialize app
|
||||
(defn mount-components []
|
||||
(r/render [#'page] (.getElementById js/document "app")))
|
||||
|
||||
(defn init! []
|
||||
(rf/dispatch-sync [:initialize-db])
|
||||
(load-interceptors!)
|
||||
(hook-browser-navigation!)
|
||||
(mount-components))
|
4
src/cljs/commiteth/db.cljs
Normal file
4
src/cljs/commiteth/db.cljs
Normal file
@ -0,0 +1,4 @@
|
||||
(ns commiteth.db)
|
||||
|
||||
(def default-db
|
||||
{:page :home})
|
13
src/cljs/commiteth/handlers.cljs
Normal file
13
src/cljs/commiteth/handlers.cljs
Normal file
@ -0,0 +1,13 @@
|
||||
(ns commiteth.handlers
|
||||
(:require [commiteth.db :as db]
|
||||
[re-frame.core :refer [dispatch reg-event-db]]))
|
||||
|
||||
(reg-event-db
|
||||
:initialize-db
|
||||
(fn [_ _]
|
||||
db/default-db))
|
||||
|
||||
(reg-event-db
|
||||
:set-active-page
|
||||
(fn [db [_ page]]
|
||||
(assoc db :page page)))
|
12
src/cljs/commiteth/subscriptions.cljs
Normal file
12
src/cljs/commiteth/subscriptions.cljs
Normal file
@ -0,0 +1,12 @@
|
||||
(ns commiteth.subscriptions
|
||||
(:require [re-frame.core :refer [reg-sub]]))
|
||||
|
||||
(reg-sub
|
||||
:page
|
||||
(fn [db _]
|
||||
(:page db)))
|
||||
|
||||
(reg-sub
|
||||
:docs
|
||||
(fn [db _]
|
||||
(:docs db)))
|
36
test/clj/commiteth/test/db/core.clj
Normal file
36
test/clj/commiteth/test/db/core.clj
Normal file
@ -0,0 +1,36 @@
|
||||
(ns commiteth.test.db.core
|
||||
(:require [commiteth.db.core :refer [*db*] :as db]
|
||||
[luminus-migrations.core :as migrations]
|
||||
[clojure.test :refer :all]
|
||||
[clojure.java.jdbc :as jdbc]
|
||||
[commiteth.config :refer [env]]
|
||||
[mount.core :as mount]))
|
||||
|
||||
(use-fixtures
|
||||
:once
|
||||
(fn [f]
|
||||
(mount/start
|
||||
#'commiteth.config/env
|
||||
#'commiteth.db.core/*db*)
|
||||
(migrations/migrate ["migrate"] (select-keys env [:database-url]))
|
||||
(f)))
|
||||
|
||||
(deftest test-users
|
||||
(jdbc/with-db-transaction [t-conn *db*]
|
||||
(jdbc/db-set-rollback-only! t-conn)
|
||||
(is (= 1 (db/create-user!
|
||||
t-conn
|
||||
{:id "1"
|
||||
:first_name "Sam"
|
||||
:last_name "Smith"
|
||||
:email "sam.smith@example.com"
|
||||
:pass "pass"})))
|
||||
(is (= {:id "1"
|
||||
:first_name "Sam"
|
||||
:last_name "Smith"
|
||||
:email "sam.smith@example.com"
|
||||
:pass "pass"
|
||||
:admin nil
|
||||
:last_login nil
|
||||
:is_active nil}
|
||||
(db/get-user t-conn {:id "1"})))))
|
13
test/clj/commiteth/test/handler.clj
Normal file
13
test/clj/commiteth/test/handler.clj
Normal file
@ -0,0 +1,13 @@
|
||||
(ns commiteth.test.handler
|
||||
(:require [clojure.test :refer :all]
|
||||
[ring.mock.request :refer :all]
|
||||
[commiteth.handler :refer :all]))
|
||||
|
||||
(deftest test-app
|
||||
(testing "main route"
|
||||
(let [response ((app) (request :get "/"))]
|
||||
(is (= 200 (:status response)))))
|
||||
|
||||
(testing "not-found route"
|
||||
(let [response ((app) (request :get "/invalid"))]
|
||||
(is (= 404 (:status response))))))
|
8
test/cljs/commiteth/core_test.cljs
Normal file
8
test/cljs/commiteth/core_test.cljs
Normal file
@ -0,0 +1,8 @@
|
||||
(ns commiteth.core-test
|
||||
(:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
|
||||
[reagent.core :as reagent :refer [atom]]
|
||||
[commiteth.core :as rc]))
|
||||
|
||||
(deftest test-home
|
||||
(is (= true true)))
|
||||
|
6
test/cljs/commiteth/doo_runner.cljs
Normal file
6
test/cljs/commiteth/doo_runner.cljs
Normal file
@ -0,0 +1,6 @@
|
||||
(ns commiteth.doo-runner
|
||||
(:require [doo.runner :refer-macros [doo-tests]]
|
||||
[commiteth.core-test]))
|
||||
|
||||
(doo-tests 'commiteth.core-test)
|
||||
|
Loading…
x
Reference in New Issue
Block a user