Skip to content

Commit

Permalink
release 0.2.3: added dockerized example application (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
erdos authored Oct 7, 2018
1 parent 9729258 commit 264655a
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 6 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ them to make the template more readable.

## Version

**Latest stable** version is `0.2.2`.
**Latest stable** version is `0.2.3`.

If you are using Maven, add the followings to your `pom.xml`:

Expand All @@ -30,7 +30,7 @@ The dependency:
<dependency>
<groupId>io.github.erdos</groupId>
<artifactId>stencil-core</artifactId>
<version>0.2.2</version>
<version>0.2.3</version>
</dependency>
```

Expand All @@ -45,7 +45,7 @@ And the [Clojars](https://clojars.org) repository:

Alternatively, if you are using Leiningen, add the following to
the `:dependencies` section of your `project.clj`
file: `[io.github.erdos/stencil-core "0.2.2"]`
file: `[io.github.erdos/stencil-core "0.2.3"]`

Previous versions are available on the [Stencil Clojars](https://clojars.org/io.github.erdos/stencil-core) page.

Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ You can find the project on [Stencil's GitHub](https://github.com/erdos/stencil)
## For Programmers

- First of all, read the [Getting Started](GettingStarted.md) manual
- Try out the [Dockerized Stencil](https://github.com/erdos/stencil/blob/master/service/README.md) example application.
- Source code is available on [Stencil's GitHub](https://github.com/erdos/stencil)
- If you wish to contribute, read the [Contributing](Contribution.md) page

Expand Down
Empty file added out.docx
Empty file.
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject io.github.erdos/stencil-core "0.2.2"
(defproject io.github.erdos/stencil-core "0.2.3"
:url "https://github.com/erdos/stencil"
:description "Templating engine for office documents."
:license {:name "Eclipse Public License - v 2.0"
Expand Down
11 changes: 11 additions & 0 deletions service/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/target
/classes
/checkouts
pom.xml
pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port
.hgignore
.hg/
21 changes: 21 additions & 0 deletions service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM clojure AS build-env
WORKDIR /usr/src/myapp

COPY project.clj /usr/src/myapp/
RUN lein deps

COPY . /usr/src/myapp

RUN mv "$(lein uberjar | sed -n 's/^Created \(.*standalone\.jar\)/\1/p')" myapp-standalone.jar

FROM openjdk:8-jre-alpine

ENV STENCIL_HTTP_PORT 8080
ENV STENCIL_TEMPLATE_DIR /templates

VOLUME /templates

WORKDIR /myapp
COPY --from=build-env /usr/src/myapp/myapp-standalone.jar /myapp/myapp.jar
ENTRYPOINT ["java", "-jar", "/myapp/myapp.jar"]
EXPOSE 8080
75 changes: 75 additions & 0 deletions service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Dockerized service for Stencil templates

This is a Dockerized example application running the Stencil template engine.
You can use


## Usage

Building and running the container:

1. Build the container with the `build.sh` command.
2. Run the container locally with the `run.sh` command. First parameter: directory of template files to mount as a volume. Second parameter: http port to listen on (defaults to `8080`).

After you start the container, it will enumerate and print the names of
the template files it found in the volume.

Rendering a template:

```
time curl -XPOST localhost:8080/test-control-loop.docx --header "Content-Type: application/json" --data '{"elems":[{"value": "first"}]}' > rendered.docx
```

Opening the output file shows that the file contents are rendered all right.

```
oowriter rendered.docx
```


## API

You can send requests over a HTTP api.

**request:**

- method: `POST`
- uri: relative path of template file in templates folder
- headers: `Content-Type: application/json`
- request body: a JSON map of template data.

The different responses have different HTTP staus codes.

**response (success)**

- status: `200`
- headers: `Content-Type: application/octet-stream`
- content: rendered document ready to download.

**response (template not found)**

This happens then the given file name was not found in the template directory.

- status: `404`
- content: plain text

**response (template error)**

This happens when the template file could not be prepared. For example: syntax
errors in template file, unexpected file format, etc.

- status: `500`
- content: plain text describing error

**response (eval error)**

This happens when the template could not be evaluated.

- status: `400`
- content: plain text describing error

**response (wrong url)**

This happens when you do not send a `POST` request.

- status `405`
3 changes: 3 additions & 0 deletions service/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env sh

docker build . --tag stencil-service:latest
10 changes: 10 additions & 0 deletions service/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
(defproject io.github.erdos/stencil-service "0.2.3"
:description "Web service for the Stencil templating engine"
:url "https://github.com/erdos/stencil"
:license {:name "Eclipse Public License - v 2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.8.0"]
[io.github.erdos/stencil-core "0.2.3"]
[http-kit "2.2.0"]
[ring/ring-json "0.4.0"]]
:main stencil.service.core)
10 changes: 10 additions & 0 deletions service/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env sh

# starts a stencil service docker container.
# - first parameter: directory of template files
# - second parameter: http port to listen on.

STENCIL_HOST_TEMPLATE_DIR=${1:-/home/erdos/Joy/stencil/test-resources}
STENCIL_HOST_HTTP_PORT=${2:-8080}

docker run -it -p $STENCIL_HOST_HTTP_PORT:8080 -v $STENCIL_HOST_TEMPLATE_DIR:/templates stencil-service:latest
68 changes: 68 additions & 0 deletions service/src/stencil/service/core.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
(ns stencil.service.core
(:gen-class)
(:import [java.io File])
(:require [org.httpkit.server :refer [run-server]]
[stencil.api :as api]
[clojure.java.io :refer [file]]
[ring.middleware.json :refer [wrap-json-body]]))

(set! *warn-on-reflection* true)

(defn get-http-port []
(Integer/parseInt (System/getenv "STENCIL_HTTP_PORT")))

(defn get-template-dir []
(let [dir (file (System/getenv "STENCIL_TEMPLATE_DIR"))]
(if-not (.exists dir)
(throw (ex-info "Template directory does not exist!" {:status 500}))
dir)))

(def -prepared (atom {}))

(defn prepared [template-name]
(let [template-file (file template-name)
last-modified (.lastModified template-file)]
(or (get-in @-prepared [template-file last-modified])
(let [p (api/prepare template-file)]
(swap! -prepared assoc template-file {last-modified p})
p))))

(defn get-template [^String template-name]
(let [template-name (.substring (str template-name) 1) ;; so they dont start with /
parent (get-template-dir)
template (file parent template-name)]
(if (.exists template)
(prepared template)
(throw (ex-info "Template file does not exist!" {:status 404})))))

(defmacro wrap-err [& bodies]
`(try ~@bodies
(catch clojure.lang.ExceptionInfo e#
(if-let [status# (:status (ex-data e#))]
{:status status#, :body (.getMessage e#)}
(throw e#)))))

(defn -app [request]
(wrap-err
(case (:request-method request)
:post
(if-let [prepared (get-template (:uri request))]
(let [rendered (api/render! prepared (:body request) :output :input-stream)]
{:status 200
:body rendered
:headers {"content-type" "application/octet-stream"}}))
;; otherwise:
(throw (ex-info "Method Not Allowed" {:status 405})))))

(def app (wrap-json-body -app {:keywords? false}))

(defn -main [& args]
(let [http-port (get-http-port)
template-dir ^File (get-template-dir)
server (run-server app {:port http-port})]
(println "Started listening on" http-port "serving" (str template-dir))
(println "Available template files: ")
(doseq [^File line (tree-seq #(.isDirectory ^File %) (comp next file-seq) template-dir)
:when (.isFile line)]
(println (str (.relativize (.toPath template-dir) (.toPath line)))))
(while true (read-line))))
16 changes: 14 additions & 2 deletions src/stencil/api.clj
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,27 @@

(defn render!
"Takes a prepared template instance and renders it.
By default it returns an InputStream of the rendered document."
By default it returns an InputStream of the rendered document.
Options map keys:
- {:output FNAME} renders output to file FNAME (string or File object). Throws exception
if file already exists and :overwrite? option is not set.
- {:output :input-stream} returns an input stream of the result document.
- {:output :reader} returns the input stream reader of the result document."
[template template-data & {:as opts}]
(let [template (prepare template)
template-data (make-template-data template-data)
result (API/render template ^TemplateData template-data)]
(cond
(#{:stream :input-stream} (:output opts))
(.getInputStream result)

(#{:reader} (:output opts))
(new java.io.InputStreamReader (.getInputStream result))

(:output opts)
(let [f (clojure.java.io/file (:output opts))]
(if (.exists f)
(if (and (.exists f) (not (:overwrite? opts)))
(throw (ex-info "File already exists! " {:file f}))
(do (.writeToFile result f)
(str "Written to " f))))
Expand Down

0 comments on commit 264655a

Please sign in to comment.