diff --git a/README.md b/README.md index 97f960e..0115ae9 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,19 @@ Add `ring/ring-core`, `ring/ring-json`,`compojure`,`lambdada` and `cheshire` dep (with-open [writer (io/writer os)] (let [request (parse-stream (io/reader is :encoding "UTF-8") true)] (generate-stream (handler request) writer)))) +``` + +Alteratively, you can use the ring-handler->lambda utility function +```clojure +;; no wrap-apigw +(def handler (wrap-json-response + (wrap-json-params + (wrap-params + (wrap-keyword-params + app))))) + +(deflambda-ring-handler example.LambdaRingHandler handler) ``` It is a common to use AWS Scheduled Events to warmup the Lambda function. diff --git a/src/ring/middleware/apigw.clj b/src/ring/middleware/apigw.clj index 5b81f05..dfca7d5 100644 --- a/src/ring/middleware/apigw.clj +++ b/src/ring/middleware/apigw.clj @@ -1,47 +1,9 @@ (ns ring.middleware.apigw - (:require [clojure.string :as string]) - (:import [java.io ByteArrayInputStream] - [java.net URLEncoder])) - -(defn- generate-query-string [params] - (string/join "&" (map (fn [[k v]] - (str (URLEncoder/encode (name k)) "=" (URLEncoder/encode v))) - params))) - -(defn- request->http-method [request] - (-> (:httpMethod request) - (string/lower-case) - (keyword))) - -(defn- apigw-request->ring-request [apigw-request] - {:pre [(every? #(contains? apigw-request %) [:httpMethod :path :queryStringParameters]) - (contains? #{"GET" "POST" "OPTIONS" "DELETE" "PUT"} (:httpMethod apigw-request))]} - {:uri (:path apigw-request) - :query-string (generate-query-string (:queryStringParameters apigw-request)) - :request-method (request->http-method apigw-request) - :headers (:headers apigw-request) - :body (when-let [body (:body apigw-request)] (ByteArrayInputStream. (.getBytes body "UTF-8")))}) - -(defn- no-scheduled-route-configured-error [request] - (throw (ex-info "Got Scheduled Event but no scheduled-event-route configured" - {:request request}))) - -(defn- apigw->ring-request [request scheduled-event-route] - (let [scheduled-event? (= "Scheduled Event" (:detail-type request))] - (cond - (and scheduled-event? scheduled-event-route) (apigw-request->ring-request {:path scheduled-event-route - :queryStringParameters "" - :headers nil - :httpMethod "GET"}) - scheduled-event? (no-scheduled-route-configured-error request) - :else (apigw-request->ring-request request)))) + (:require [ring-apigw-lambda-proxy.core :refer [apigw->ring-request ring-response->apigw]])) (defn wrap-apigw-lambda-proxy ([handler] (wrap-apigw-lambda-proxy handler {})) ([handler {:keys [scheduled-event-route]}] (fn [request] (let [response (handler (apigw->ring-request request scheduled-event-route))] - {:statusCode (:status response) - :headers (:headers response) - :body (:body response)})))) - + (ring-response->apigw response))))) diff --git a/src/ring_apigw_lambda_proxy/core.clj b/src/ring_apigw_lambda_proxy/core.clj new file mode 100644 index 0000000..990b9ba --- /dev/null +++ b/src/ring_apigw_lambda_proxy/core.clj @@ -0,0 +1,49 @@ +(ns ring-apigw-lambda-proxy.core + (:require [clojure.string :as string]) + (:import [java.io ByteArrayInputStream] + [java.net URLEncoder])) + +(defn- generate-query-string [params] + (string/join "&" (map (fn [[k v]] + (str (URLEncoder/encode (name k)) "=" (URLEncoder/encode v))) + params))) + +(defn- request->http-method [request] + (-> (:httpMethod request) + (string/lower-case) + (keyword))) + +(defn- apigw-request->ring-request [apigw-request] + {:pre [(every? #(contains? apigw-request %) [:httpMethod :path :queryStringParameters]) + (contains? #{"GET" "POST" "OPTIONS" "DELETE" "PUT"} (:httpMethod apigw-request))]} + {:uri (:path apigw-request) + :query-string (generate-query-string (:queryStringParameters apigw-request)) + :request-method (request->http-method apigw-request) + :headers (:headers apigw-request) + :body (when-let [body (:body apigw-request)] (ByteArrayInputStream. (.getBytes body "UTF-8")))}) + +(defn- no-scheduled-route-configured-error [request] + (throw (ex-info "Got Scheduled Event but no scheduled-event-route configured" + {:request request}))) + +(defn apigw->ring-request [request scheduled-event-route] + (let [scheduled-event? (= "Scheduled Event" (:detail-type request))] + (cond + (and scheduled-event? scheduled-event-route) (apigw-request->ring-request {:path scheduled-event-route + :queryStringParameters "" + :headers nil + :httpMethod "GET"}) + scheduled-event? (no-scheduled-route-configured-error request) + :else (apigw-request->ring-request request)))) + +(defn ring-response->apigw [response] + {:statusCode (:status response) + :headers (:headers response) + :body (:body response)}) + +(defmacro deflambda-ring-handler [name handler & [scheduled-event-route]] + `(uswitch.lambada.core/deflambdafn ~name [is# os# ctx#] + (with-open [writer# (clojure.java.io/writer os#)] + (let [request# (cheshire.core/parse-stream (io/reader is# :encoding "UTF-8") keyword) + response# (-> request# (apigw->ring-request ~scheduled-event-route) ~handler ring-response->apigw)] + (cheshire.core/generate-stream response# writer#)))))