From 8b2f76440723b67d0db96179b8f587dae94ccf20 Mon Sep 17 00:00:00 2001 From: Marcin Kulik Date: Sun, 15 Jan 2017 19:11:22 +0100 Subject: [PATCH] FileStore and FileServer boundaries --- dev/resources/dev.edn | 11 ++++++-- dev/src/dev.clj | 6 ++++- project.clj | 2 ++ resources/asciinema/system.edn | 16 ++++++++++-- src/asciinema/boundary/file_server.clj | 4 +++ src/asciinema/boundary/file_store.clj | 7 ++++++ src/asciinema/component/local_file_server.clj | 12 +++++++++ src/asciinema/component/local_file_store.clj | 25 +++++++++++++++++++ src/asciinema/component/s3_file_server.clj | 15 +++++++++++ src/asciinema/component/s3_file_store.clj | 25 +++++++++++++++++++ test/asciinema/boundary/file_server_test.clj | 7 ++++++ test/asciinema/boundary/file_store_test.clj | 7 ++++++ 12 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 src/asciinema/boundary/file_server.clj create mode 100644 src/asciinema/boundary/file_store.clj create mode 100644 src/asciinema/component/local_file_server.clj create mode 100644 src/asciinema/component/local_file_store.clj create mode 100644 src/asciinema/component/s3_file_server.clj create mode 100644 src/asciinema/component/s3_file_store.clj create mode 100644 test/asciinema/boundary/file_server_test.clj create mode 100644 test/asciinema/boundary/file_store_test.clj diff --git a/dev/resources/dev.edn b/dev/resources/dev.edn index 83a8e7c..3ee655c 100644 --- a/dev/resources/dev.edn +++ b/dev/resources/dev.edn @@ -1,4 +1,9 @@ -{:config +{:components + {:file-store #var asciinema.component.local-file-store/local-file-store + :file-server #var asciinema.component.local-file-server/local-file-server} + :dependencies + {:file-server [:file-store]} + :config {:app {:middleware {:functions {:stacktrace #var ring.middleware.stacktrace/wrap-stacktrace} @@ -6,4 +11,6 @@ :http {:port 4000} :db - {:uri "jdbc:postgresql://localhost/postgres"}}} + {:uri "jdbc:postgresql://localhost:15432/asciinema_development?user=vagrant"} + :file-store + {:path "uploads/"}}} diff --git a/dev/src/dev.clj b/dev/src/dev.clj index 5889c96..b720e61 100644 --- a/dev/src/dev.clj +++ b/dev/src/dev.clj @@ -8,7 +8,11 @@ [duct.generate :as gen] [duct.util.repl :refer [setup test cljs-repl migrate rollback]] [duct.util.system :refer [load-system]] - [reloaded.repl :refer [system init start stop go reset]])) + [reloaded.repl :refer [system init start stop go reset]] + [asciinema.boundary.file-server :as file-server] + [asciinema.boundary.file-store :as file-store] + [asciinema.component.local-file-store :refer [->LocalFileStore]] + [asciinema.component.local-file-server :refer [->LocalFileServer]])) (defn new-system [] (load-system (keep io/resource ["asciinema/system.edn" "dev.edn" "local.edn"]))) diff --git a/project.clj b/project.clj index c35c7cd..1fd9213 100644 --- a/project.clj +++ b/project.clj @@ -11,6 +11,8 @@ [ring/ring-defaults "0.2.1"] [ring-jetty-component "0.3.1"] [ring-webjars "0.1.1"] + [metosin/ring-http-response "0.8.1"] + [clj-aws-s3 "0.3.10" :exclusions [clj-time]] [aleph "0.4.1"] [org.slf4j/slf4j-nop "1.7.21"] [org.webjars/normalize.css "3.0.2"] diff --git a/resources/asciinema/system.edn b/resources/asciinema/system.edn index 6d3185e..84aa5df 100644 --- a/resources/asciinema/system.edn +++ b/resources/asciinema/system.edn @@ -2,7 +2,9 @@ {:app #var duct.component.handler/handler-component :http #var asciinema.component.aleph/aleph-server :db #var duct.component.hikaricp/hikaricp - :ragtime #var duct.component.ragtime/ragtime} + :ragtime #var duct.component.ragtime/ragtime + :file-store #var asciinema.component.s3-file-store/s3-file-store + :file-server #var asciinema.component.s3-file-server/s3-file-server} :endpoints {:example #var asciinema.endpoint.example/example-endpoint} :dependencies @@ -47,4 +49,14 @@ :db {:uri db-uri} :ragtime - {:resource-path "asciinema/migrations"}}} + {:resource-path "asciinema/migrations"} + :file-store + {:cred {:access-key s3-access-key + :secret-key s3-secret-key} + :bucket s3-bucket + :path-prefix "uploads/"} + :file-server + {:cred {:access-key s3-access-key + :secret-key s3-secret-key} + :bucket s3-bucket + :path-prefix "uploads/"}}} diff --git a/src/asciinema/boundary/file_server.clj b/src/asciinema/boundary/file_server.clj new file mode 100644 index 0000000..8fb06bd --- /dev/null +++ b/src/asciinema/boundary/file_server.clj @@ -0,0 +1,4 @@ +(ns asciinema.boundary.file-server) + +(defprotocol FileServer + (serve [this path])) diff --git a/src/asciinema/boundary/file_store.clj b/src/asciinema/boundary/file_store.clj new file mode 100644 index 0000000..d9107a8 --- /dev/null +++ b/src/asciinema/boundary/file_store.clj @@ -0,0 +1,7 @@ +(ns asciinema.boundary.file-store) + +(defprotocol FileStore + (put-file [this file path] [this file path size]) + (input-stream [this path]) + (move-file [this old-path new-path]) + (delete-file [this path])) diff --git a/src/asciinema/component/local_file_server.clj b/src/asciinema/component/local_file_server.clj new file mode 100644 index 0000000..4dbeaa9 --- /dev/null +++ b/src/asciinema/component/local_file_server.clj @@ -0,0 +1,12 @@ +(ns asciinema.component.local-file-server + (:require [asciinema.boundary.file-server :as file-server] + [asciinema.boundary.file-store :as file-store] + [ring.util.http-response :as response])) + +(defrecord LocalFileServer [file-store] + file-server/FileServer + (serve [this path] + (response/ok (file-store/input-stream file-store path)))) + +(defn local-file-server [{:keys [file-store]}] + (->LocalFileServer file-store)) diff --git a/src/asciinema/component/local_file_store.clj b/src/asciinema/component/local_file_store.clj new file mode 100644 index 0000000..e250f1d --- /dev/null +++ b/src/asciinema/component/local_file_store.clj @@ -0,0 +1,25 @@ +(ns asciinema.component.local-file-store + (:require [asciinema.boundary.file-store :as file-store] + [clojure.java.io :as io])) + +(defrecord LocalFileStore [base-path] + file-store/FileStore + (put-file [this file path] + (let [path (str base-path path)] + (io/make-parents path) + (io/copy file (io/file path)))) + (put-file [this file path size] + (file-store/put-file this file path)) + (input-stream [this path] + (let [path (str base-path path)] + (io/input-stream path))) + (move-file [this old-path new-path] + (let [old-path (str base-path old-path) + new-path (str base-path new-path)] + (.renameTo (io/file old-path) (io/file new-path)))) + (delete-file [this path] + (let [path (str base-path path)] + (io/delete-file path)))) + +(defn local-file-store [{:keys [path]}] + (->LocalFileStore path)) diff --git a/src/asciinema/component/s3_file_server.clj b/src/asciinema/component/s3_file_server.clj new file mode 100644 index 0000000..c4163a7 --- /dev/null +++ b/src/asciinema/component/s3_file_server.clj @@ -0,0 +1,15 @@ +(ns asciinema.component.s3-file-server + (:require [asciinema.boundary.file-server :as file-server] + [aws.sdk.s3 :as s3] + [ring.util.http-response :as response])) + +;; TODO support custom expiry date (it's 1 day now) + +(defrecord S3FileServer [cred bucket path-prefix] + file-server/FileServer + (serve [this path] + (let [path (str path-prefix path)] + (response/found (s3/generate-presigned-url cred bucket path))))) + +(defn s3-file-server [{:keys [cred bucket path-prefix]}] + (->S3FileServer cred bucket path-prefix)) diff --git a/src/asciinema/component/s3_file_store.clj b/src/asciinema/component/s3_file_store.clj new file mode 100644 index 0000000..e9fb608 --- /dev/null +++ b/src/asciinema/component/s3_file_store.clj @@ -0,0 +1,25 @@ +(ns asciinema.component.s3-file-store + (:require [asciinema.boundary.file-store :as file-store] + [aws.sdk.s3 :as s3])) + +(defrecord S3FileStore [cred bucket path-prefix] + file-store/FileStore + (put-file [this file path] + (file-store/put-file this file path nil)) + (put-file [this file path size] + (let [path (str path-prefix path)] + (s3/put-object cred bucket path file {:content-length size}))) + (input-stream [this path] + (let [path (str path-prefix path)] + (:content (s3/get-object cred bucket path)))) + (move-file [this old-path new-path] + (let [old-path (str path-prefix old-path) + new-path (str path-prefix new-path)] + (s3/copy-object cred bucket old-path new-path) + (s3/delete-object cred bucket old-path))) + (delete-file [this path] + (let [path (str path-prefix path)] + (s3/delete-object cred bucket path)))) + +(defn s3-file-store [{:keys [cred bucket path-prefix]}] + (->S3FileStore cred bucket path-prefix)) diff --git a/test/asciinema/boundary/file_server_test.clj b/test/asciinema/boundary/file_server_test.clj new file mode 100644 index 0000000..2136484 --- /dev/null +++ b/test/asciinema/boundary/file_server_test.clj @@ -0,0 +1,7 @@ +(ns asciinema.boundary.file-server-test + (:require [clojure.test :refer :all] + [asciinema.boundary.file-server :as file-server])) + +(deftest a-test + (testing "FIXME, I fail." + (is (= 0 1)))) diff --git a/test/asciinema/boundary/file_store_test.clj b/test/asciinema/boundary/file_store_test.clj new file mode 100644 index 0000000..b6d3786 --- /dev/null +++ b/test/asciinema/boundary/file_store_test.clj @@ -0,0 +1,7 @@ +(ns asciinema.boundary.file-store-test + (:require [clojure.test :refer :all] + [asciinema.boundary.file-store :as file-store])) + +(deftest a-test + (testing "FIXME, I fail." + (is (= 0 1))))