├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ └── ci.yml
├── .gitignore
├── .nrepl-port
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── deps.edn
├── docs
└── media
│ └── logo.png
├── examples
├── babashka
│ ├── README.md
│ ├── bb.edn
│ ├── deps.edn
│ ├── src
│ │ ├── core.clj
│ │ ├── handler.clj
│ │ ├── local.clj
│ │ └── logo.png
│ └── template.yml
└── native
│ ├── .nrepl-port
│ ├── README.md
│ ├── bb.edn
│ ├── deps.edn
│ ├── resources
│ ├── native-configuration
│ │ ├── jni-config.json
│ │ ├── predefined-classes-config.json
│ │ ├── proxy-config.json
│ │ ├── reflect-config.json
│ │ ├── reflect-config.orig.json
│ │ ├── resource-config.json
│ │ ├── serialization-config.json
│ │ └── traces.json
│ └── public
│ │ ├── index.html
│ │ └── logo.png
│ ├── src
│ └── example
│ │ ├── lambda.clj
│ │ ├── routes.clj
│ │ └── server.clj
│ └── template.yml
├── pom.xml
├── project.clj
├── resources-test
└── logo.png
├── resources
└── META-INF
│ └── native-image
│ └── io
│ └── github
│ └── FieryCod
│ └── holy-lambda-ring-adapter
│ └── native-image.properties
├── src
└── fierycod
│ └── holy_lambda_ring_adapter
│ ├── codec.clj
│ ├── core.cljc
│ └── impl.cljc
└── test
└── fierycod
└── holy_lambda_ring_adapter
├── core_test.clj
└── logo.png
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [fierycod]
4 | open_collective: # Replace with a single Open Collective username
5 |
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG] ---"
5 | labels: bug
6 | assignees: FieryCod
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To reproduce**
14 | Steps to reproduce the behavior:
15 |
16 | **Expected behavior**
17 | A clear and concise description of what you expected to happen.
18 |
19 | **Screenshots**
20 | If applicable, add screenshots to help explain your problem.
21 |
22 | **Extra information:**
23 | - `OS`: [e.g. MacOS]
24 | - `List of dependencies` (use `clojure -Stree`):
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[FEATURE] ---"
5 | labels: enhancement
6 | assignees: FieryCod
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Holy Lambda Ring Adapter Pipeline
2 |
3 | on: [push]
4 |
5 | jobs:
6 | test:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | fail-fast: true
10 | matrix:
11 | os:
12 | - ubuntu-latest
13 |
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v2
17 |
18 | - name: Prepare java
19 | uses: actions/setup-java@v2
20 | with:
21 | distribution: 'temurin'
22 | java-version: '11'
23 |
24 | - name: Install clojure tools
25 | uses: DeLaGuardo/setup-clojure@3.5
26 | with:
27 | cli: 1.10.3.986
28 | lein: 2.9.1
29 |
30 | - name: Cache Maven
31 | uses: actions/cache@v2
32 | with:
33 | path: |
34 | ~/.m2/repository
35 | key: ${{ runner.os }}-maven-${{ hashFiles('deps.edn') }}
36 |
37 | - name: Test the adapter
38 | run: clojure -M:dev:test
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 | *.log
4 | tmp/
5 | .cpcache
6 | target
7 | .clj-kondo
8 | .lsp
9 | pom.xml.asc
10 | .holy-lambda
11 |
--------------------------------------------------------------------------------
/.nrepl-port:
--------------------------------------------------------------------------------
1 | 40771
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ## 0.1.2
4 | - Support AWS ApiGateway RestApi
5 | - Fix asynchronous error reporting
6 | - Throw meaningful error message on incorrect event shape instead of NPE
7 |
8 | ## 0.1.1
9 | - Deprecate `wrap-hl-req-res-model`. Use `ring<->hl-middleware` instead.
10 |
11 | ## 0.1.0
12 | - Add support for async non-async handlers
13 | - Support all Ring types
14 | - Support all holy-lambda backends
15 | - Support HttpAPI as Lambda Integration
16 |
17 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | karol.wojcik@tuta.io.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Karol Wójcik
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | ## Rationale
13 | I wanted to create a library that allows to use known Clojure tools to develop API's on AWS Lambda.
14 |
15 | **A library that**
16 |
17 | - prevents AWS to vendor lock you with Lambda,
18 | - allows for fast feedback loop while developing API locally,
19 | - implements a full Ring spec,
20 | - supports serving resources from AWS Lambda,
21 | - is fast, so that cold starts are minimal
22 |
23 | This is why holy-lambda-ring-adapter was released. An adapter is a part of holy-lambda project and is already used in production.
24 |
25 | ## Compatibility
26 | - AWS ApiGateway Lambda Integration
27 | - [HttpApi](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop.html#http-api-examples)
28 | - [RestApi](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-rest-api.html)
29 | - Java Version >= 11 or Babashka >= 0.8.2
30 | - GraalVM Native Image >= 21.2.0
31 | - Holy Lambda >= 0.6.0 [all backends: [native](https://fierycod.github.io/holy-lambda/#/native-backend-tutorial), [babashka](https://fierycod.github.io/holy-lambda/#/babashka-backend-tutorial), [clojure](https://fierycod.github.io/holy-lambda/#/clojure-backend-tutorial)
32 |
33 | ## Usage
34 | - **With plain [ring](https://github.com/ring-clojure/ring)**
35 | ```clojure
36 | (ns core
37 | (:require
38 | [fierycod.holy-lambda-ring-adapter.core :as hlra]
39 | [fierycod.holy-lambda.core :as h])
40 |
41 | (defn ring-handler
42 | [request]
43 | {:status 200
44 | :headers {}
45 | :body \"Hello World\"})
46 |
47 | (def HttpApiProxyGateway (hlra/ring<->hl-middleware ring-handler))
48 |
49 | (h/entrypoint [#'HttpApiProxyGateway])
50 | ```
51 |
52 | - **With Reitit & Muuntaja [reitit](https://github.com/metosin/reitit)**
53 | ```clojure
54 | (ns core
55 | (:require
56 | [fierycod.holy-lambda-ring-adapter.core :as hlra]
57 | [fierycod.holy-lambda.core :as h])
58 |
59 | (def muuntaja-ring-handler
60 | (ring/ring-handler
61 | (ring/router
62 | routes
63 | {:data {:muuntaja instance
64 | :coercion coerction
65 | :middleware middlewares}})))
66 |
67 | (def HttpApiProxyGateway (hlra/ring<->hl-middleware muuntaja-ring-handler))
68 |
69 | (h/entrypoint [#'HttpApiProxyGateway])
70 | ```
71 |
72 | ## Companies & Inviduals using Holy Lambda Ring Adapter?
73 | - [retailic](https://retailic.com/)
74 |
75 | ## Documentation
76 | The holy-lambda documentation is available [here](https://fierycod.github.io/holy-lambda).
77 |
78 | ## Current Version
79 | [](https://clojars.org/io.github.FieryCod/holy-lambda-ring-adapter)
80 |
81 | ## Getting Help
82 | [](https://clojurians.slack.com/channels/holy-lambda)
83 |
84 | ## License
85 | Copyright © 2021 Karol Wojcik aka Fierycod
86 |
87 | Released under the MIT license.
88 |
--------------------------------------------------------------------------------
/deps.edn:
--------------------------------------------------------------------------------
1 | {:deps {}
2 | :paths ["src" "resources"]
3 | :aliases
4 | {:dev {:deps {io.github.FieryCod/holy-lambda {:mvn/version "0.5.1-SNAPSHOT"}
5 | clj-http/clj-http {:mvn/version "3.12.3"}
6 | metosin/reitit {:mvn/version "0.5.15"}
7 | cheshire/cheshire {:mvn/version "5.10.0"}
8 | metosin/muuntaja {:mvn/version "0.6.8"}
9 | ring/ring {:mvn/version "1.9.4"}}}
10 | :test {:extra-paths ["test" "resources-test"]
11 | :extra-deps {io.github.cognitect-labs/test-runner
12 | {:git/tag "v0.5.0" :git/sha "b3fd0d2"}}
13 | :main-opts ["-m" "cognitect.test-runner"]
14 | :exec-fn cognitect.test-runner.api/test}}}
15 |
--------------------------------------------------------------------------------
/docs/media/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FieryCod/holy-lambda-ring-adapter/bd9c493ee0dc3e3b7d99c0f78e5bd7037d23efe9/docs/media/logo.png
--------------------------------------------------------------------------------
/examples/babashka/README.md:
--------------------------------------------------------------------------------
1 | ### [Official documentation](https://fierycod.github.io/holy-lambda)
2 | ### One line deployment procedure
3 | ```bash
4 | bb hl:babashka:sync && sam deploy --guided
5 | ```
6 |
--------------------------------------------------------------------------------
/examples/babashka/bb.edn:
--------------------------------------------------------------------------------
1 | {:deps {io.github.FieryCod/holy-lambda-babashka-tasks
2 | {:git/url "https://github.com/FieryCod/holy-lambda"
3 | :deps/root "./modules/holy-lambda-babashka-tasks"
4 | :sha "dcbcb298a6b755bf6ef6011ba029ef3d0c4b7768"}
5 | io.github.FieryCod/holy-lambda-ring-adapter {:local/root "../../"}
6 | io.github.FieryCod/holy-lambda {:mvn/version "0.6.4"}}
7 |
8 | ;; Minimal babashka version which should be used in conjuction with holy-lambda
9 | :min-bb-version "0.3.7"
10 |
11 | :holy-lambda/options {
12 | :docker {
13 |
14 | ;; Check https://docs.docker.com/network/
15 | ;; Network setting for future versions of HL will propagate to AWS SAM as well
16 | ;; Options: "host"|"bridge"|"overlay"|"none"|nil|"macvlan"
17 | :network nil
18 |
19 | ;; HL runs some bb tasks in docker context. You can put additional resources to the context by using volumes.
20 | ;; ----------------------------------------------------------------------------
21 | ;; Single volume definition:
22 | ;;
23 | ;; {:docker "/where-to-mount-in-docker"
24 | ;; :host "relative-local-path"}
25 | :volumes []
26 |
27 | ;; GraalVM Community holy-lambda compatible docker image
28 | ;; You can always build your own GraalVM image with enterprise edition
29 | :image "ghcr.io/fierycod/holy-lambda-builder:amd64-java11-21.3.0"}
30 |
31 | :build {:compile-cmd "clojure -X:uberjar"
32 | ;; Used when either :docker is nil or
33 | ;; `HL_NO_DOCKER` environment variable is set to "true"
34 | ;; Might be set via `GRAALVM_HOME` environment variable
35 | :graalvm-home "~/.graalvm"}
36 |
37 | :backend
38 | {
39 | ;; Babashka pods should be shipped using AWS Lambda Layer
40 | ;; Check this template https://github.com/aws-samples/aws-lambda-layers-aws-sam-examples/blob/master/aws-sdk-layer/template.yaml
41 | ;; and official docs https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-layers.html
42 | ;; CodeUri should be `.holy-lambda/pods`
43 | ;; For now pods should be declared in `bb.edn`. See: https://github.com/babashka/babashka/issues/768#issuecomment-825016317
44 | ;;
45 | ;; `IMPORTANT:` 3rd party babashka compatible libraries should be distributed as a layers (CodeUri: .holy-lambda/bb-clj-deps)
46 | :pods {}
47 |
48 | ;; For `:native` backend you can provide your own bootstrap file
49 | :bootstrap-file "bootstrap"
50 |
51 | ;; For `:native` backend you can provide some native resources which will be available during lambda execution
52 | ;; Resources are packed as is.
53 | :native-deps "resources"
54 |
55 | ;; Specify custom arguments for native image generation
56 | ;; Check https://www.graalvm.org/reference-manual/native-image/Options/
57 | :native-image-args ["--verbose"
58 | "--no-fallback"
59 | "--report-unsupported-elements-at-runtime"
60 | "-H:+AllowIncompleteClasspath"
61 | "--no-server"]}}
62 |
63 | :tasks {:requires ([holy-lambda.tasks])
64 | hl:docker:run holy-lambda.tasks/hl:docker:run
65 | hl:native:conf holy-lambda.tasks/hl:native:conf
66 | hl:native:executable holy-lambda.tasks/hl:native:executable
67 | hl:babashka:sync holy-lambda.tasks/hl:babashka:sync
68 | hl:compile holy-lambda.tasks/hl:compile
69 | hl:doctor holy-lambda.tasks/hl:doctor
70 | hl:clean holy-lambda.tasks/hl:clean
71 | hl:update-bb-tasks holy-lambda.tasks/hl:update-bb-tasks
72 | hl:version holy-lambda.tasks/hl:version}}
73 |
--------------------------------------------------------------------------------
/examples/babashka/deps.edn:
--------------------------------------------------------------------------------
1 | {:paths ["src"]
2 | :deps {io.github.FieryCod/holy-lambda {:mvn/version "0.6.4"}
3 | io.github.FieryCod/holy-lambda-ring-adapter {:local/root "../../"}}}
4 |
--------------------------------------------------------------------------------
/examples/babashka/src/core.clj:
--------------------------------------------------------------------------------
1 | (ns core
2 | (:require
3 | [handler :as handler]
4 | [fierycod.holy-lambda-ring-adapter.core :as hlra]
5 | [fierycod.holy-lambda.core :as h]))
6 |
7 | (def HttpApiGatewayProxy (hlra/ring<->hl-middleware handler/router))
8 |
9 | (h/entrypoint [#'HttpApiGatewayProxy])
10 |
--------------------------------------------------------------------------------
/examples/babashka/src/handler.clj:
--------------------------------------------------------------------------------
1 | (ns handler
2 | (:require
3 | [clojure.string :as str]
4 | [hiccup2.core :refer [html]]
5 | [clojure.core.match :refer [match]]
6 | [clojure.java.io :as io]))
7 |
8 | (defn logo
9 | [_request]
10 | {:body (io/file "./logo.png")
11 | :status 200
12 | :headers {"content-type" "image/png"}})
13 |
14 | (defn hello
15 | [_request]
16 | {:body {"hello" "world"}
17 | :status 200
18 | :headers {"content-type" "application/json"}})
19 |
20 | (defn router
21 | [req]
22 | (let [paths (vec (rest (str/split (:uri req) #"/")))]
23 | (match [(:request-method req) paths]
24 | [:get ["logo"]] (handler/logo req)
25 | [:get ["hello"]] (handler/hello req)
26 | [:get ["welcome"]] {:body (str (html [:html "Welcome!"
27 | [:img {:src "./logo"}]]))
28 | :headers {"content-type" "text/html; charset=utf-8"}
29 | :status 200}
30 | :else {:body "Not Found"
31 | :status 404}
32 | )))
33 |
--------------------------------------------------------------------------------
/examples/babashka/src/local.clj:
--------------------------------------------------------------------------------
1 | (ns local
2 | (:require
3 | [handler :as handler]
4 | [org.httpkit.server :as srv]))
5 |
6 |
7 | (def server (srv/run-server handler/router {:port 3000}))
8 |
--------------------------------------------------------------------------------
/examples/babashka/src/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FieryCod/holy-lambda-ring-adapter/bd9c493ee0dc3e3b7d99c0f78e5bd7037d23efe9/examples/babashka/src/logo.png
--------------------------------------------------------------------------------
/examples/babashka/template.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Transform: AWS::Serverless-2016-10-31
3 | Description: >
4 | Example basic lambda using `holy-lambda` micro library
5 |
6 | Parameters:
7 | Timeout:
8 | Type: Number
9 | Default: 40
10 | MemorySize:
11 | Type: Number
12 | Default: 512
13 | Entrypoint:
14 | Type: String
15 | Default: core
16 |
17 | Globals:
18 | Function:
19 | Timeout: !Ref Timeout
20 | MemorySize: !Ref MemorySize
21 | Environment:
22 | Variables:
23 | HL_ENTRYPOINT: !Ref Entrypoint
24 |
25 | Resources:
26 | DependenciesLayer:
27 | Type: AWS::Serverless::LayerVersion
28 | Properties:
29 | LayerName: DependenciesLayer
30 | ContentUri: ./.holy-lambda/bb-clj-deps
31 |
32 | ExampleLambdaFunction:
33 | Type: AWS::Serverless::Function
34 | Properties:
35 | Runtime: provided.al2
36 | Handler: core.HttpApiGatewayProxy
37 | CodeUri: src
38 | Layers:
39 | - arn:aws:lambda:eu-central-1:443526418261:layer:holy-lambda-babashka-runtime-amd64:3
40 | - !Ref DependenciesLayer
41 | # Architectures:
42 | # - arm64
43 | Events:
44 | HelloEvent:
45 | Type: HttpApi
46 | Properties:
47 | ApiId: !Ref ServerlessHttpApi
48 | Path: /{proxy+}
49 | Method: ANY
50 |
51 | ServerlessHttpApi:
52 | Type: AWS::Serverless::HttpApi
53 | DeletionPolicy: Retain
54 | Properties:
55 | StageName: Prod
56 |
57 | Outputs:
58 | TestEndpoint:
59 | Description: Test endpoint
60 | Value:
61 | Fn::Sub: https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com
62 |
--------------------------------------------------------------------------------
/examples/native/.nrepl-port:
--------------------------------------------------------------------------------
1 | 42507
--------------------------------------------------------------------------------
/examples/native/README.md:
--------------------------------------------------------------------------------
1 | ### [Official documentation](https://fierycod.github.io/holy-lambda)
2 | ### One line deployment procedure
3 | ```bash
4 | bb hl:compile && bb hl:native:executable && sam deploy --guided
5 | ```
6 |
--------------------------------------------------------------------------------
/examples/native/bb.edn:
--------------------------------------------------------------------------------
1 | {:deps {io.github.FieryCod/holy-lambda-babashka-tasks
2 | {:git/url "https://github.com/FieryCod/holy-lambda"
3 | :deps/root "./modules/holy-lambda-babashka-tasks"
4 | :sha "e6c47274a2bfc7576a9da0ccdbc079c1e83bee17"}
5 | io.github.FieryCod/holy-lambda-ring-adapter {:local/root "../../"}
6 | io.github.FieryCod/holy-lambda {:mvn/version "0.6.4"}}
7 |
8 | ;; Minimal babashka version which should be used in conjuction with holy-lambda
9 | :min-bb-version "0.3.7"
10 |
11 | :holy-lambda/options {
12 | :docker {
13 |
14 | ;; Check https://docs.docker.com/network/
15 | ;; Network setting for future versions of HL will propagate to AWS SAM as well
16 | ;; Options: "host"|"bridge"|"overlay"|"none"|nil|"macvlan"
17 | :network nil
18 |
19 | ;; HL runs some bb tasks in docker context. You can put additional resources to the context by using volumes.
20 | ;; ----------------------------------------------------------------------------
21 | ;; Single volume definition:
22 | ;;
23 | ;; {:docker "/where-to-mount-in-docker"
24 | ;; :host "relative-local-path"}
25 | :volumes []
26 |
27 | ;; GraalVM Community holy-lambda compatible docker image
28 | ;; You can always build your own GraalVM image with enterprise edition
29 | :image "ghcr.io/fierycod/holy-lambda-builder:amd64-java11-21.3.0"}
30 |
31 | :build {:compile-cmd "clojure -X:uberjar-lambda"
32 | ;; Used when either :docker is nil or
33 | ;; `HL_NO_DOCKER` environment variable is set to "true"
34 | ;; Might be set via `GRAALVM_HOME` environment variable
35 | :graalvm-home "~/.graalvm"}
36 |
37 | :backend
38 | {
39 | ;; Babashka pods should be shipped using AWS Lambda Layer
40 | ;; Check this template https://github.com/aws-samples/aws-lambda-layers-aws-sam-examples/blob/master/aws-sdk-layer/template.yaml
41 | ;; and official docs https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-layers.html
42 | ;; CodeUri should be `.holy-lambda/pods`
43 | ;; For now pods should be declared in `bb.edn`. See: https://github.com/babashka/babashka/issues/768#issuecomment-825016317
44 | ;;
45 | ;; `IMPORTANT:` 3rd party babashka compatible libraries should be distributed as a layers (CodeUri: .holy-lambda/bb-clj-deps)
46 | :pods {}
47 |
48 | ;; For `:native` backend you can provide your own bootstrap file
49 | :bootstrap-file "bootstrap"
50 |
51 | ;; For `:native` backend you can provide some native resources which will be available during lambda execution
52 | ;; Resources are packed as is.
53 | ;; :native-deps "resources"
54 |
55 | ;; Specify custom arguments for native image generation
56 | ;; Check https://www.graalvm.org/reference-manual/native-image/Options/
57 | :native-image-args ["--verbose"
58 | "--no-fallback"
59 | "--report-unsupported-elements-at-runtime"
60 | "-H:+AllowIncompleteClasspath"
61 | "-H:IncludeResources=public/.*"
62 | "--initialize-at-build-time=javax.xml.datatype,jdk.xml.internal"
63 | "--no-server"]}}
64 |
65 | :tasks {:requires ([holy-lambda.tasks])
66 | hl:docker:run holy-lambda.tasks/hl:docker:run
67 | hl:native:conf holy-lambda.tasks/hl:native:conf
68 | hl:native:executable holy-lambda.tasks/hl:native:executable
69 | hl:babashka:sync holy-lambda.tasks/hl:babashka:sync
70 | hl:compile holy-lambda.tasks/hl:compile
71 | hl:doctor holy-lambda.tasks/hl:doctor
72 | hl:clean holy-lambda.tasks/hl:clean
73 | hl:update-bb-tasks holy-lambda.tasks/hl:update-bb-tasks
74 | hl:version holy-lambda.tasks/hl:version}}
75 |
--------------------------------------------------------------------------------
/examples/native/deps.edn:
--------------------------------------------------------------------------------
1 | {:deps {org.clojure/clojure {:mvn/version "1.10.3"}
2 | com.github.clj-easy/graal-build-time {:mvn/version "0.1.4"}
3 | io.github.FieryCod/holy-lambda {:mvn/version "0.6.2"}
4 | io.github.FieryCod/holy-lambda-ring-adapter {:local/root "../../"}
5 | ring/ring {:mvn/version "1.9.4"}
6 | metosin/muuntaja {:mvn/version "0.6.8"}
7 | metosin/malli {:mvn/version "0.6.2"}
8 | metosin/reitit-core {:mvn/version "0.5.15"}
9 | metosin/reitit-middleware {:mvn/version "0.5.15"}
10 | metosin/reitit-malli {:mvn/version "0.5.15"}
11 | metosin/reitit-ring {:mvn/version "0.5.15"}
12 | metosin/reitit-swagger {:mvn/version "0.5.15"}
13 | com.stuartsierra/component {:mvn/version "1.0.0"}
14 | metosin/reitit-swagger-ui {:mvn/version "0.5.15"
15 | :exclusions [metosin/ring-swagger-ui]}
16 | metosin/ring-swagger-ui {:mvn/version "3.46.0-1"}}
17 |
18 | :paths ["src" "resources"]
19 |
20 | :aliases {:uberjar-lambda {:replace-deps {com.github.seancorfield/depstar {:mvn/version "2.1.303"}}
21 | :exec-fn hf.depstar/uberjar
22 | :exec-args {:aot ["example.lambda"]
23 | :main-class "example.lambda"
24 | :jar ".holy-lambda/build/output.jar"
25 | :jvm-opts ["-Dclojure.compiler.direct-linking=true"
26 | "-Dclojure.spec.skip-macros=true"]}}
27 | :uberjar-server {:replace-deps {com.github.seancorfield/depstar {:mvn/version "2.1.303"}}
28 | :exec-fn hf.depstar/uberjar
29 | :exec-args {:aot ["example.server"]
30 | :main-class "example.server"
31 | :jar ".holy-lambda/build/output.jar"
32 | :jvm-opts ["-Dclojure.compiler.direct-linking=true"
33 | "-Dclojure.spec.skip-macros=true"]}}
34 | }}
35 |
--------------------------------------------------------------------------------
/examples/native/resources/native-configuration/jni-config.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name":"example.lambda",
4 | "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }]}
5 | ,
6 | {
7 | "name":"java.lang.ClassLoader",
8 | "methods":[
9 | {"name":"getPlatformClassLoader","parameterTypes":[] },
10 | {"name":"loadClass","parameterTypes":["java.lang.String"] }
11 | ]}
12 | ,
13 | {
14 | "name":"jdk.internal.loader.ClassLoaders$PlatformClassLoader"}
15 | ,
16 | {
17 | "name":"org.graalvm.nativebridge.jni.JNIExceptionWrapperEntryPoints",
18 | "methods":[{"name":"getClassName","parameterTypes":["java.lang.Class"] }]}
19 |
20 | ]
21 |
--------------------------------------------------------------------------------
/examples/native/resources/native-configuration/predefined-classes-config.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type":"agent-extracted",
4 | "classes":[
5 | ]
6 | }
7 | ]
8 |
9 |
--------------------------------------------------------------------------------
/examples/native/resources/native-configuration/proxy-config.json:
--------------------------------------------------------------------------------
1 | [
2 | ]
3 |
--------------------------------------------------------------------------------
/examples/native/resources/native-configuration/reflect-config.json:
--------------------------------------------------------------------------------
1 | [ {
2 | "name" : "[B"
3 | }, {
4 | "name" : "[C"
5 | }, {
6 | "name" : "[D"
7 | }, {
8 | "name" : "[F"
9 | }, {
10 | "name" : "[I"
11 | }, {
12 | "name" : "[J"
13 | }, {
14 | "name" : "[Lcom.fasterxml.jackson.databind.deser.Deserializers;"
15 | }, {
16 | "name" : "[Lcom.fasterxml.jackson.databind.deser.KeyDeserializers;"
17 | }, {
18 | "name" : "[Lcom.fasterxml.jackson.databind.deser.ValueInstantiators;"
19 | }, {
20 | "name" : "[Lcom.fasterxml.jackson.databind.ser.Serializers;"
21 | }, {
22 | "name" : "[Ljava.lang.Object;"
23 | }, {
24 | "name" : "[Lreitit.Trie$Matcher;"
25 | }, {
26 | "name" : "[S"
27 | }, {
28 | "name" : "[Z"
29 | }, {
30 | "name" : "com.fasterxml.jackson.databind.ext.Java7SupportImpl",
31 | "methods" : [ {
32 | "name" : "",
33 | "parameterTypes" : [ ]
34 | } ]
35 | },
36 | { "name": "org.msgpack.template.builder.JavassistTemplateBuilder",
37 | "allPublicMethods": true,
38 | "allPublicConstructors": true
39 | },
40 | {
41 | "name" : "java.io.OutputStream",
42 | "queryAllPublicMethods" : true
43 | }, {
44 | "name" : "java.io.Serializable",
45 | "queryAllDeclaredMethods" : true
46 | }, {
47 | "name" : "java.lang.Class",
48 | "queryAllPublicMethods" : true
49 | }, {
50 | "name" : "java.lang.Iterable",
51 | "queryAllDeclaredMethods" : true
52 | }, {
53 | "name" : "java.lang.Object",
54 | "queryAllPublicMethods" : true
55 | }, {
56 | "name" : "java.lang.Runnable",
57 | "queryAllDeclaredMethods" : true
58 | }, {
59 | "name" : "java.lang.String",
60 | "queryAllPublicMethods" : true,
61 | "methods" : [ {
62 | "name" : "contains",
63 | "parameterTypes" : [ "java.lang.CharSequence" ]
64 | } ]
65 | }, {
66 | "name" : "java.lang.reflect.AccessibleObject",
67 | "methods" : [ {
68 | "name" : "canAccess",
69 | "parameterTypes" : [ "java.lang.Object" ]
70 | } ]
71 | }, {
72 | "name" : "java.security.MessageDigestSpi"
73 | }, {
74 | "name" : "java.security.SecureRandomParameters"
75 | }, {
76 | "name" : "java.util.Map",
77 | "queryAllDeclaredMethods" : true
78 | }, {
79 | "name" : "java.util.Properties",
80 | "queryAllPublicMethods" : true,
81 | "methods" : [ {
82 | "name" : "getProperty",
83 | "parameterTypes" : [ "java.lang.String" ]
84 | } ]
85 | }, {
86 | "name" : "java.util.concurrent.Callable",
87 | "queryAllDeclaredMethods" : true
88 | }, {
89 | "name" : "muuntaja.protocols.StreamableResponse",
90 | "allPublicFields" : true,
91 | "queryAllPublicMethods" : true
92 | }, {
93 | "name" : "sun.security.provider.NativePRNG",
94 | "methods" : [ {
95 | "name" : "",
96 | "parameterTypes" : [ ]
97 | } ]
98 | }, {
99 | "name" : "sun.security.provider.SHA",
100 | "methods" : [ {
101 | "name" : "",
102 | "parameterTypes" : [ ]
103 | } ]
104 | } ]
105 |
--------------------------------------------------------------------------------
/examples/native/resources/native-configuration/reflect-config.orig.json:
--------------------------------------------------------------------------------
1 | [ {
2 | "name" : "[B"
3 | }, {
4 | "name" : "[C"
5 | }, {
6 | "name" : "[D"
7 | }, {
8 | "name" : "[F"
9 | }, {
10 | "name" : "[I"
11 | }, {
12 | "name" : "[J"
13 | }, {
14 | "name" : "[Lcom.fasterxml.jackson.databind.deser.Deserializers;"
15 | }, {
16 | "name" : "[Lcom.fasterxml.jackson.databind.deser.KeyDeserializers;"
17 | }, {
18 | "name" : "[Lcom.fasterxml.jackson.databind.deser.ValueInstantiators;"
19 | }, {
20 | "name" : "[Lcom.fasterxml.jackson.databind.ser.Serializers;"
21 | }, {
22 | "name" : "[Ljava.lang.Object;"
23 | }, {
24 | "name" : "[Lreitit.Trie$Matcher;"
25 | }, {
26 | "name" : "[S"
27 | }, {
28 | "name" : "[Z"
29 | }, {
30 | "name" : "borkdude.dynaload.LazyVar"
31 | }, {
32 | "name" : "borkdude.dynaload__init"
33 | }, {
34 | "name" : "clojure.asm.ClassVisitor"
35 | }, {
36 | "name" : "clojure.asm.ClassWriter"
37 | }, {
38 | "name" : "clojure.asm.Opcodes"
39 | }, {
40 | "name" : "clojure.asm.Type"
41 | }, {
42 | "name" : "clojure.asm.commons.GeneratorAdapter"
43 | }, {
44 | "name" : "clojure.asm.commons.Method"
45 | }, {
46 | "name" : "clojure.core.ArrayChunk"
47 | }, {
48 | "name" : "clojure.core.ArrayManager"
49 | }, {
50 | "name" : "clojure.core.Eduction"
51 | }, {
52 | "name" : "clojure.core.IVecImpl"
53 | }, {
54 | "name" : "clojure.core.Inst"
55 | }, {
56 | "name" : "clojure.core.Vec"
57 | }, {
58 | "name" : "clojure.core.VecNode"
59 | }, {
60 | "name" : "clojure.core.VecSeq"
61 | }, {
62 | "name" : "clojure.core.protocols.CollReduce"
63 | }, {
64 | "name" : "clojure.core.protocols.Datafiable"
65 | }, {
66 | "name" : "clojure.core.protocols.IKVReduce"
67 | }, {
68 | "name" : "clojure.core.protocols.InternalReduce"
69 | }, {
70 | "name" : "clojure.core.protocols.Navigable"
71 | }, {
72 | "name" : "clojure.core.protocols__init"
73 | }, {
74 | "name" : "clojure.core.server__init"
75 | }, {
76 | "name" : "clojure.core.specs.alpha$eval11",
77 | "methods" : [ {
78 | "name" : "",
79 | "parameterTypes" : [ ]
80 | } ]
81 | }, {
82 | "name" : "clojure.core.specs.alpha$eval134",
83 | "methods" : [ {
84 | "name" : "",
85 | "parameterTypes" : [ ]
86 | } ]
87 | }, {
88 | "name" : "clojure.core.specs.alpha$eval136",
89 | "methods" : [ {
90 | "name" : "",
91 | "parameterTypes" : [ ]
92 | } ]
93 | }, {
94 | "name" : "clojure.core.specs.alpha$eval5",
95 | "methods" : [ {
96 | "name" : "",
97 | "parameterTypes" : [ ]
98 | } ]
99 | }, {
100 | "name" : "clojure.core.specs.alpha$eval7",
101 | "methods" : [ {
102 | "name" : "",
103 | "parameterTypes" : [ ]
104 | } ]
105 | }, {
106 | "name" : "clojure.core.specs.alpha$even_number_of_forms_QMARK_",
107 | "methods" : [ {
108 | "name" : "",
109 | "parameterTypes" : [ ]
110 | } ]
111 | }, {
112 | "name" : "clojure.core.specs.alpha$fn__101",
113 | "methods" : [ {
114 | "name" : "",
115 | "parameterTypes" : [ ]
116 | } ]
117 | }, {
118 | "name" : "clojure.core.specs.alpha$fn__104",
119 | "methods" : [ {
120 | "name" : "",
121 | "parameterTypes" : [ ]
122 | } ]
123 | }, {
124 | "name" : "clojure.core.specs.alpha$fn__106",
125 | "methods" : [ {
126 | "name" : "",
127 | "parameterTypes" : [ ]
128 | } ]
129 | }, {
130 | "name" : "clojure.core.specs.alpha$fn__109",
131 | "methods" : [ {
132 | "name" : "",
133 | "parameterTypes" : [ ]
134 | } ]
135 | }, {
136 | "name" : "clojure.core.specs.alpha$fn__112",
137 | "methods" : [ {
138 | "name" : "",
139 | "parameterTypes" : [ ]
140 | } ]
141 | }, {
142 | "name" : "clojure.core.specs.alpha$fn__114",
143 | "methods" : [ {
144 | "name" : "",
145 | "parameterTypes" : [ ]
146 | } ]
147 | }, {
148 | "name" : "clojure.core.specs.alpha$fn__117",
149 | "methods" : [ {
150 | "name" : "",
151 | "parameterTypes" : [ ]
152 | } ]
153 | }, {
154 | "name" : "clojure.core.specs.alpha$fn__119",
155 | "methods" : [ {
156 | "name" : "",
157 | "parameterTypes" : [ ]
158 | } ]
159 | }, {
160 | "name" : "clojure.core.specs.alpha$fn__121",
161 | "methods" : [ {
162 | "name" : "",
163 | "parameterTypes" : [ ]
164 | } ]
165 | }, {
166 | "name" : "clojure.core.specs.alpha$fn__16",
167 | "methods" : [ {
168 | "name" : "",
169 | "parameterTypes" : [ ]
170 | } ]
171 | }, {
172 | "name" : "clojure.core.specs.alpha$fn__19",
173 | "methods" : [ {
174 | "name" : "",
175 | "parameterTypes" : [ ]
176 | } ]
177 | }, {
178 | "name" : "clojure.core.specs.alpha$fn__22",
179 | "methods" : [ {
180 | "name" : "",
181 | "parameterTypes" : [ ]
182 | } ]
183 | }, {
184 | "name" : "clojure.core.specs.alpha$fn__25",
185 | "methods" : [ {
186 | "name" : "",
187 | "parameterTypes" : [ ]
188 | } ]
189 | }, {
190 | "name" : "clojure.core.specs.alpha$fn__28",
191 | "methods" : [ {
192 | "name" : "",
193 | "parameterTypes" : [ ]
194 | } ]
195 | }, {
196 | "name" : "clojure.core.specs.alpha$fn__30",
197 | "methods" : [ {
198 | "name" : "",
199 | "parameterTypes" : [ ]
200 | } ]
201 | }, {
202 | "name" : "clojure.core.specs.alpha$fn__33",
203 | "methods" : [ {
204 | "name" : "",
205 | "parameterTypes" : [ ]
206 | } ]
207 | }, {
208 | "name" : "clojure.core.specs.alpha$fn__35",
209 | "methods" : [ {
210 | "name" : "",
211 | "parameterTypes" : [ ]
212 | } ]
213 | }, {
214 | "name" : "clojure.core.specs.alpha$fn__38",
215 | "methods" : [ {
216 | "name" : "",
217 | "parameterTypes" : [ ]
218 | } ]
219 | }, {
220 | "name" : "clojure.core.specs.alpha$fn__41",
221 | "methods" : [ {
222 | "name" : "",
223 | "parameterTypes" : [ ]
224 | } ]
225 | }, {
226 | "name" : "clojure.core.specs.alpha$fn__44",
227 | "methods" : [ {
228 | "name" : "",
229 | "parameterTypes" : [ ]
230 | } ]
231 | }, {
232 | "name" : "clojure.core.specs.alpha$fn__48",
233 | "methods" : [ {
234 | "name" : "",
235 | "parameterTypes" : [ ]
236 | } ]
237 | }, {
238 | "name" : "clojure.core.specs.alpha$fn__51",
239 | "methods" : [ {
240 | "name" : "",
241 | "parameterTypes" : [ ]
242 | } ]
243 | }, {
244 | "name" : "clojure.core.specs.alpha$fn__54",
245 | "methods" : [ {
246 | "name" : "",
247 | "parameterTypes" : [ ]
248 | } ]
249 | }, {
250 | "name" : "clojure.core.specs.alpha$fn__56",
251 | "methods" : [ {
252 | "name" : "",
253 | "parameterTypes" : [ ]
254 | } ]
255 | }, {
256 | "name" : "clojure.core.specs.alpha$fn__58",
257 | "methods" : [ {
258 | "name" : "",
259 | "parameterTypes" : [ ]
260 | } ]
261 | }, {
262 | "name" : "clojure.core.specs.alpha$fn__71",
263 | "methods" : [ {
264 | "name" : "",
265 | "parameterTypes" : [ ]
266 | } ]
267 | }, {
268 | "name" : "clojure.core.specs.alpha$fn__73",
269 | "methods" : [ {
270 | "name" : "",
271 | "parameterTypes" : [ ]
272 | } ]
273 | }, {
274 | "name" : "clojure.core.specs.alpha$fn__85",
275 | "methods" : [ {
276 | "name" : "",
277 | "parameterTypes" : [ ]
278 | } ]
279 | }, {
280 | "name" : "clojure.core.specs.alpha$fn__98",
281 | "methods" : [ {
282 | "name" : "",
283 | "parameterTypes" : [ ]
284 | } ]
285 | }, {
286 | "name" : "clojure.core.specs.alpha$quotable",
287 | "methods" : [ {
288 | "name" : "",
289 | "parameterTypes" : [ ]
290 | } ]
291 | }, {
292 | "name" : "clojure.core__init"
293 | }, {
294 | "name" : "clojure.core_deftype__init"
295 | }, {
296 | "name" : "clojure.core_print__init"
297 | }, {
298 | "name" : "clojure.core_proxy__init"
299 | }, {
300 | "name" : "clojure.edn__init"
301 | }, {
302 | "name" : "clojure.genclass__init"
303 | }, {
304 | "name" : "clojure.gvec__init"
305 | }, {
306 | "name" : "clojure.instant__init"
307 | }, {
308 | "name" : "clojure.java.io.Coercions"
309 | }, {
310 | "name" : "clojure.java.io.IOFactory"
311 | }, {
312 | "name" : "clojure.java.io__init"
313 | }, {
314 | "name" : "clojure.lang.AFn",
315 | "allDeclaredFields" : true,
316 | "queryAllDeclaredMethods" : true
317 | }, {
318 | "name" : "clojure.lang.APersistentMap",
319 | "allDeclaredFields" : true,
320 | "queryAllDeclaredMethods" : true
321 | }, {
322 | "name" : "clojure.lang.APersistentMap$KeySeq"
323 | }, {
324 | "name" : "clojure.lang.APersistentMap$ValSeq"
325 | }, {
326 | "name" : "clojure.lang.APersistentVector"
327 | }, {
328 | "name" : "clojure.lang.ASeq"
329 | }, {
330 | "name" : "clojure.lang.Associative",
331 | "queryAllDeclaredMethods" : true
332 | }, {
333 | "name" : "clojure.lang.BigInt"
334 | }, {
335 | "name" : "clojure.lang.ChunkBuffer"
336 | }, {
337 | "name" : "clojure.lang.Compiler",
338 | "allPublicFields" : true
339 | }, {
340 | "name" : "clojure.lang.Compiler$CompilerException"
341 | }, {
342 | "name" : "clojure.lang.Cons"
343 | }, {
344 | "name" : "clojure.lang.Counted",
345 | "queryAllDeclaredMethods" : true
346 | }, {
347 | "name" : "clojure.lang.DynamicClassLoader"
348 | }, {
349 | "name" : "clojure.lang.ExceptionInfo"
350 | }, {
351 | "name" : "clojure.lang.Fn"
352 | }, {
353 | "name" : "clojure.lang.IChunk"
354 | }, {
355 | "name" : "clojure.lang.IChunkedSeq"
356 | }, {
357 | "name" : "clojure.lang.IDeref"
358 | }, {
359 | "name" : "clojure.lang.IEditableCollection",
360 | "queryAllDeclaredMethods" : true
361 | }, {
362 | "name" : "clojure.lang.IExceptionInfo"
363 | }, {
364 | "name" : "clojure.lang.IFn",
365 | "queryAllDeclaredMethods" : true
366 | }, {
367 | "name" : "clojure.lang.IHashEq",
368 | "queryAllDeclaredMethods" : true
369 | }, {
370 | "name" : "clojure.lang.IKVReduce",
371 | "queryAllDeclaredMethods" : true
372 | }, {
373 | "name" : "clojure.lang.ILookup",
374 | "queryAllDeclaredMethods" : true
375 | }, {
376 | "name" : "clojure.lang.IMapIterable",
377 | "queryAllDeclaredMethods" : true
378 | }, {
379 | "name" : "clojure.lang.IMeta",
380 | "queryAllDeclaredMethods" : true
381 | }, {
382 | "name" : "clojure.lang.IObj",
383 | "queryAllDeclaredMethods" : true
384 | }, {
385 | "name" : "clojure.lang.IPending"
386 | }, {
387 | "name" : "clojure.lang.IPersistentCollection",
388 | "queryAllDeclaredMethods" : true
389 | }, {
390 | "name" : "clojure.lang.IPersistentList"
391 | }, {
392 | "name" : "clojure.lang.IPersistentMap",
393 | "queryAllDeclaredMethods" : true
394 | }, {
395 | "name" : "clojure.lang.IPersistentSet"
396 | }, {
397 | "name" : "clojure.lang.IPersistentVector"
398 | }, {
399 | "name" : "clojure.lang.IProxy"
400 | }, {
401 | "name" : "clojure.lang.IRecord"
402 | }, {
403 | "name" : "clojure.lang.IReduceInit"
404 | }, {
405 | "name" : "clojure.lang.ISeq"
406 | }, {
407 | "name" : "clojure.lang.Keyword"
408 | }, {
409 | "name" : "clojure.lang.LazilyPersistentVector"
410 | }, {
411 | "name" : "clojure.lang.LazySeq"
412 | }, {
413 | "name" : "clojure.lang.LineNumberingPushbackReader"
414 | }, {
415 | "name" : "clojure.lang.LispReader$ReaderException"
416 | }, {
417 | "name" : "clojure.lang.LockingTransaction",
418 | "queryAllPublicMethods" : true
419 | }, {
420 | "name" : "clojure.lang.MapEntry"
421 | }, {
422 | "name" : "clojure.lang.MapEquivalence",
423 | "queryAllDeclaredMethods" : true
424 | }, {
425 | "name" : "clojure.lang.Murmur3"
426 | }, {
427 | "name" : "clojure.lang.Namespace",
428 | "queryAllPublicMethods" : true
429 | }, {
430 | "name" : "clojure.lang.Numbers"
431 | }, {
432 | "name" : "clojure.lang.PersistentArrayMap",
433 | "allDeclaredFields" : true,
434 | "queryAllDeclaredMethods" : true,
435 | "queryAllDeclaredConstructors" : true
436 | }, {
437 | "name" : "clojure.lang.PersistentArrayMap$Seq"
438 | }, {
439 | "name" : "clojure.lang.PersistentHashMap"
440 | }, {
441 | "name" : "clojure.lang.PersistentHashMap$NodeSeq"
442 | }, {
443 | "name" : "clojure.lang.PersistentHashSet"
444 | }, {
445 | "name" : "clojure.lang.PersistentQueue"
446 | }, {
447 | "name" : "clojure.lang.PersistentVector"
448 | }, {
449 | "name" : "clojure.lang.PersistentVector$ChunkedSeq"
450 | }, {
451 | "name" : "clojure.lang.RT",
452 | "queryAllPublicMethods" : true
453 | }, {
454 | "name" : "clojure.lang.Ratio"
455 | }, {
456 | "name" : "clojure.lang.ReaderConditional"
457 | }, {
458 | "name" : "clojure.lang.Reflector"
459 | }, {
460 | "name" : "clojure.lang.SeqIterator"
461 | }, {
462 | "name" : "clojure.lang.Seqable",
463 | "queryAllDeclaredMethods" : true
464 | }, {
465 | "name" : "clojure.lang.Sequential"
466 | }, {
467 | "name" : "clojure.lang.StringSeq"
468 | }, {
469 | "name" : "clojure.lang.Symbol",
470 | "queryAllPublicMethods" : true
471 | }, {
472 | "name" : "clojure.lang.TaggedLiteral"
473 | }, {
474 | "name" : "clojure.lang.Util"
475 | }, {
476 | "name" : "clojure.lang.Var",
477 | "queryAllPublicMethods" : true
478 | }, {
479 | "name" : "clojure.lang.Volatile"
480 | }, {
481 | "name" : "clojure.main__init"
482 | }, {
483 | "name" : "clojure.pprint.PrettyFlush"
484 | }, {
485 | "name" : "clojure.pprint.cl_format__init"
486 | }, {
487 | "name" : "clojure.pprint.column_writer__init"
488 | }, {
489 | "name" : "clojure.pprint.dispatch__init"
490 | }, {
491 | "name" : "clojure.pprint.pprint_base__init"
492 | }, {
493 | "name" : "clojure.pprint.pretty_writer__init"
494 | }, {
495 | "name" : "clojure.pprint.print_table__init"
496 | }, {
497 | "name" : "clojure.pprint.utilities__init"
498 | }, {
499 | "name" : "clojure.pprint__init"
500 | }, {
501 | "name" : "clojure.set__init"
502 | }, {
503 | "name" : "clojure.spec.alpha.Spec"
504 | }, {
505 | "name" : "clojure.spec.alpha.Specize"
506 | }, {
507 | "name" : "clojure.spec.alpha__init"
508 | }, {
509 | "name" : "clojure.spec.gen.alpha__init"
510 | }, {
511 | "name" : "clojure.string__init"
512 | }, {
513 | "name" : "clojure.tools.reader.default_data_readers__init"
514 | }, {
515 | "name" : "clojure.tools.reader.edn__init"
516 | }, {
517 | "name" : "clojure.tools.reader.impl.commons__init"
518 | }, {
519 | "name" : "clojure.tools.reader.impl.errors__init"
520 | }, {
521 | "name" : "clojure.tools.reader.impl.inspect__init"
522 | }, {
523 | "name" : "clojure.tools.reader.impl.utils__init"
524 | }, {
525 | "name" : "clojure.tools.reader.reader_types.IPushbackReader"
526 | }, {
527 | "name" : "clojure.tools.reader.reader_types.IndexingPushbackReader"
528 | }, {
529 | "name" : "clojure.tools.reader.reader_types.IndexingReader"
530 | }, {
531 | "name" : "clojure.tools.reader.reader_types.InputStreamReader"
532 | }, {
533 | "name" : "clojure.tools.reader.reader_types.PushbackReader"
534 | }, {
535 | "name" : "clojure.tools.reader.reader_types.PushbackReaderCoercer"
536 | }, {
537 | "name" : "clojure.tools.reader.reader_types.Reader"
538 | }, {
539 | "name" : "clojure.tools.reader.reader_types.ReaderCoercer"
540 | }, {
541 | "name" : "clojure.tools.reader.reader_types.SourceLoggingPushbackReader"
542 | }, {
543 | "name" : "clojure.tools.reader.reader_types.StringReader"
544 | }, {
545 | "name" : "clojure.tools.reader.reader_types__init"
546 | }, {
547 | "name" : "clojure.tools.reader__init"
548 | }, {
549 | "name" : "clojure.uuid__init"
550 | }, {
551 | "name" : "clojure.walk__init"
552 | }, {
553 | "name" : "cognitect.transit.HandlerMapContainer"
554 | }, {
555 | "name" : "cognitect.transit.HandlerMapProvider"
556 | }, {
557 | "name" : "cognitect.transit.Reader"
558 | }, {
559 | "name" : "cognitect.transit.WithMeta"
560 | }, {
561 | "name" : "cognitect.transit.Writer"
562 | }, {
563 | "name" : "cognitect.transit__init"
564 | }, {
565 | "name" : "com.cognitect.transit.ArrayReadHandler"
566 | }, {
567 | "name" : "com.cognitect.transit.ArrayReader"
568 | }, {
569 | "name" : "com.cognitect.transit.MapReadHandler"
570 | }, {
571 | "name" : "com.cognitect.transit.MapReader"
572 | }, {
573 | "name" : "com.cognitect.transit.ReadHandler"
574 | }, {
575 | "name" : "com.cognitect.transit.SPI.ReaderSPI"
576 | }, {
577 | "name" : "com.cognitect.transit.TransitFactory"
578 | }, {
579 | "name" : "com.cognitect.transit.TransitFactory$Format"
580 | }, {
581 | "name" : "com.cognitect.transit.WriteHandler"
582 | }, {
583 | "name" : "com.fasterxml.jackson.core.JsonFactory"
584 | }, {
585 | "name" : "com.fasterxml.jackson.core.JsonGenerator$Feature"
586 | }, {
587 | "name" : "com.fasterxml.jackson.databind.DeserializationFeature"
588 | }, {
589 | "name" : "com.fasterxml.jackson.databind.JsonSerializer"
590 | }, {
591 | "name" : "com.fasterxml.jackson.databind.Module"
592 | }, {
593 | "name" : "com.fasterxml.jackson.databind.ObjectMapper"
594 | }, {
595 | "name" : "com.fasterxml.jackson.databind.SerializationFeature"
596 | }, {
597 | "name" : "com.fasterxml.jackson.databind.ext.Java7SupportImpl",
598 | "methods" : [ {
599 | "name" : "",
600 | "parameterTypes" : [ ]
601 | } ]
602 | }, {
603 | "name" : "com.fasterxml.jackson.databind.module.SimpleModule"
604 | }, {
605 | "name" : "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule"
606 | }, {
607 | "name" : "com.stuartsierra.component.Lifecycle"
608 | }, {
609 | "name" : "com.stuartsierra.component.SystemMap"
610 | }, {
611 | "name" : "com.stuartsierra.component.platform__init"
612 | }, {
613 | "name" : "com.stuartsierra.component__init"
614 | }, {
615 | "name" : "com.stuartsierra.dependency.DependencyGraph"
616 | }, {
617 | "name" : "com.stuartsierra.dependency.DependencyGraphUpdate"
618 | }, {
619 | "name" : "com.stuartsierra.dependency.MapDependencyGraph"
620 | }, {
621 | "name" : "com.stuartsierra.dependency__init"
622 | }, {
623 | "name" : "edamame.core__init"
624 | }, {
625 | "name" : "edamame.impl.parser.Loc"
626 | }, {
627 | "name" : "edamame.impl.parser.Options"
628 | }, {
629 | "name" : "edamame.impl.parser__init"
630 | }, {
631 | "name" : "edamame.impl.read_fn__init"
632 | }, {
633 | "name" : "edamame.impl.syntax_quote__init"
634 | }, {
635 | "name" : "example.lambda__init"
636 | }, {
637 | "name" : "example.routes.RingHandlerComponent"
638 | }, {
639 | "name" : "example.routes__init"
640 | }, {
641 | "name" : "fierycod.holy_lambda.agent__init"
642 | }, {
643 | "name" : "fierycod.holy_lambda.core$entrypoint$fn__10371",
644 | "queryAllPublicConstructors" : true,
645 | "methods" : [ {
646 | "name" : "",
647 | "parameterTypes" : [ ]
648 | } ]
649 | }, {
650 | "name" : "fierycod.holy_lambda.core__init"
651 | }, {
652 | "name" : "fierycod.holy_lambda.custom_runtime__init"
653 | }, {
654 | "name" : "fierycod.holy_lambda.retriever__init"
655 | }, {
656 | "name" : "fierycod.holy_lambda.util__init"
657 | }, {
658 | "name" : "fierycod.holy_lambda_ring_adapter.core__init"
659 | }, {
660 | "name" : "fierycod.holy_lambda_ring_adapter.impl.HLRequestBody"
661 | }, {
662 | "name" : "fierycod.holy_lambda_ring_adapter.impl.RingResponseBody"
663 | }, {
664 | "name" : "fierycod.holy_lambda_ring_adapter.impl__init"
665 | }, {
666 | "name" : "java.io.BufferedInputStream"
667 | }, {
668 | "name" : "java.io.BufferedOutputStream"
669 | }, {
670 | "name" : "java.io.BufferedReader"
671 | }, {
672 | "name" : "java.io.BufferedWriter"
673 | }, {
674 | "name" : "java.io.ByteArrayInputStream"
675 | }, {
676 | "name" : "java.io.ByteArrayOutputStream"
677 | }, {
678 | "name" : "java.io.CharArrayReader"
679 | }, {
680 | "name" : "java.io.Closeable"
681 | }, {
682 | "name" : "java.io.DataOutput"
683 | }, {
684 | "name" : "java.io.EOFException"
685 | }, {
686 | "name" : "java.io.File"
687 | }, {
688 | "name" : "java.io.FileInputStream"
689 | }, {
690 | "name" : "java.io.FileOutputStream"
691 | }, {
692 | "name" : "java.io.FileWriter"
693 | }, {
694 | "name" : "java.io.IOException"
695 | }, {
696 | "name" : "java.io.InputStream"
697 | }, {
698 | "name" : "java.io.InputStreamReader"
699 | }, {
700 | "name" : "java.io.NotSerializableException"
701 | }, {
702 | "name" : "java.io.OutputStream",
703 | "queryAllPublicMethods" : true
704 | }, {
705 | "name" : "java.io.OutputStreamWriter"
706 | }, {
707 | "name" : "java.io.PipedInputStream"
708 | }, {
709 | "name" : "java.io.PipedOutputStream"
710 | }, {
711 | "name" : "java.io.PrintWriter"
712 | }, {
713 | "name" : "java.io.PushbackReader"
714 | }, {
715 | "name" : "java.io.Reader"
716 | }, {
717 | "name" : "java.io.Serializable",
718 | "queryAllDeclaredMethods" : true
719 | }, {
720 | "name" : "java.io.StringReader"
721 | }, {
722 | "name" : "java.io.Writer"
723 | }, {
724 | "name" : "java.lang.Boolean"
725 | }, {
726 | "name" : "java.lang.Character"
727 | }, {
728 | "name" : "java.lang.Class",
729 | "queryAllPublicMethods" : true
730 | }, {
731 | "name" : "java.lang.Double"
732 | }, {
733 | "name" : "java.lang.Float"
734 | }, {
735 | "name" : "java.lang.Integer"
736 | }, {
737 | "name" : "java.lang.Iterable",
738 | "queryAllDeclaredMethods" : true
739 | }, {
740 | "name" : "java.lang.Long"
741 | }, {
742 | "name" : "java.lang.Number"
743 | }, {
744 | "name" : "java.lang.Object",
745 | "queryAllPublicMethods" : true
746 | }, {
747 | "name" : "java.lang.Runnable",
748 | "queryAllDeclaredMethods" : true
749 | }, {
750 | "name" : "java.lang.StackTraceElement"
751 | }, {
752 | "name" : "java.lang.String",
753 | "queryAllPublicMethods" : true,
754 | "methods" : [ {
755 | "name" : "contains",
756 | "parameterTypes" : [ "java.lang.CharSequence" ]
757 | } ]
758 | }, {
759 | "name" : "java.lang.ThreadLocal"
760 | }, {
761 | "name" : "java.lang.Throwable"
762 | }, {
763 | "name" : "java.lang.UnsupportedOperationException"
764 | }, {
765 | "name" : "java.lang.annotation.Annotation"
766 | }, {
767 | "name" : "java.lang.annotation.Retention"
768 | }, {
769 | "name" : "java.lang.reflect.Array"
770 | }, {
771 | "name" : "java.lang.reflect.Constructor"
772 | }, {
773 | "name" : "java.lang.reflect.Field"
774 | }, {
775 | "name" : "java.lang.reflect.Method",
776 | "methods" : [ {
777 | "name" : "canAccess",
778 | "parameterTypes" : [ "java.lang.Object" ]
779 | } ]
780 | }, {
781 | "name" : "java.lang.reflect.Modifier"
782 | }, {
783 | "name" : "java.math.BigDecimal"
784 | }, {
785 | "name" : "java.math.BigInteger"
786 | }, {
787 | "name" : "java.net.HttpURLConnection"
788 | }, {
789 | "name" : "java.net.InetAddress"
790 | }, {
791 | "name" : "java.net.MalformedURLException"
792 | }, {
793 | "name" : "java.net.ServerSocket"
794 | }, {
795 | "name" : "java.net.Socket"
796 | }, {
797 | "name" : "java.net.SocketException"
798 | }, {
799 | "name" : "java.net.URI"
800 | }, {
801 | "name" : "java.net.URL"
802 | }, {
803 | "name" : "java.net.URLDecoder"
804 | }, {
805 | "name" : "java.net.URLEncoder"
806 | }, {
807 | "name" : "java.nio.charset.Charset"
808 | }, {
809 | "name" : "java.nio.file.Files"
810 | }, {
811 | "name" : "java.nio.file.attribute.FileAttribute"
812 | }, {
813 | "name" : "java.security.MessageDigestSpi"
814 | }, {
815 | "name" : "java.security.SecureRandomParameters"
816 | }, {
817 | "name" : "java.sql.Timestamp"
818 | }, {
819 | "name" : "java.text.ParseException"
820 | }, {
821 | "name" : "java.text.SimpleDateFormat"
822 | }, {
823 | "name" : "java.time.Instant"
824 | }, {
825 | "name" : "java.time.ZoneId"
826 | }, {
827 | "name" : "java.time.format.DateTimeFormatter"
828 | }, {
829 | "name" : "java.time.format.DateTimeFormatterBuilder"
830 | }, {
831 | "name" : "java.time.temporal.ChronoField"
832 | }, {
833 | "name" : "java.util.ArrayDeque"
834 | }, {
835 | "name" : "java.util.Base64"
836 | }, {
837 | "name" : "java.util.Base64$Decoder"
838 | }, {
839 | "name" : "java.util.Base64$Encoder"
840 | }, {
841 | "name" : "java.util.Calendar"
842 | }, {
843 | "name" : "java.util.Collection"
844 | }, {
845 | "name" : "java.util.Date"
846 | }, {
847 | "name" : "java.util.GregorianCalendar"
848 | }, {
849 | "name" : "java.util.HashMap"
850 | }, {
851 | "name" : "java.util.LinkedList"
852 | }, {
853 | "name" : "java.util.List"
854 | }, {
855 | "name" : "java.util.Locale"
856 | }, {
857 | "name" : "java.util.Map",
858 | "queryAllDeclaredMethods" : true
859 | }, {
860 | "name" : "java.util.Properties",
861 | "queryAllPublicMethods" : true,
862 | "methods" : [ {
863 | "name" : "getProperty",
864 | "parameterTypes" : [ "java.lang.String" ]
865 | } ]
866 | }, {
867 | "name" : "java.util.RandomAccess"
868 | }, {
869 | "name" : "java.util.Set"
870 | }, {
871 | "name" : "java.util.TimeZone"
872 | }, {
873 | "name" : "java.util.UUID"
874 | }, {
875 | "name" : "java.util.concurrent.ArrayBlockingQueue"
876 | }, {
877 | "name" : "java.util.concurrent.BlockingQueue"
878 | }, {
879 | "name" : "java.util.concurrent.Callable",
880 | "queryAllDeclaredMethods" : true
881 | }, {
882 | "name" : "java.util.concurrent.ConcurrentHashMap"
883 | }, {
884 | "name" : "java.util.concurrent.FutureTask"
885 | }, {
886 | "name" : "java.util.concurrent.LinkedBlockingQueue"
887 | }, {
888 | "name" : "java.util.concurrent.TimeUnit"
889 | }, {
890 | "name" : "java.util.concurrent.TimeoutException"
891 | }, {
892 | "name" : "java.util.concurrent.atomic.AtomicReference"
893 | }, {
894 | "name" : "java.util.concurrent.locks.ReentrantLock"
895 | }, {
896 | "name" : "java.util.function.Function"
897 | }, {
898 | "name" : "java.util.regex.Matcher"
899 | }, {
900 | "name" : "java.util.regex.Pattern"
901 | }, {
902 | "name" : "jsonista.core.ReadValue"
903 | }, {
904 | "name" : "jsonista.core.WriteValue"
905 | }, {
906 | "name" : "jsonista.core__init"
907 | }, {
908 | "name" : "jsonista.jackson.DateSerializer"
909 | }, {
910 | "name" : "jsonista.jackson.FunctionalKeyDeserializer"
911 | }, {
912 | "name" : "jsonista.jackson.FunctionalKeywordSerializer"
913 | }, {
914 | "name" : "jsonista.jackson.FunctionalSerializer"
915 | }, {
916 | "name" : "jsonista.jackson.KeywordKeyDeserializer"
917 | }, {
918 | "name" : "jsonista.jackson.KeywordSerializer"
919 | }, {
920 | "name" : "jsonista.jackson.PersistentHashMapDeserializer"
921 | }, {
922 | "name" : "jsonista.jackson.PersistentVectorDeserializer"
923 | }, {
924 | "name" : "jsonista.jackson.RatioSerializer"
925 | }, {
926 | "name" : "jsonista.jackson.SymbolSerializer"
927 | }, {
928 | "name" : "malli.core.IntoSchema"
929 | }, {
930 | "name" : "malli.core.LensSchema"
931 | }, {
932 | "name" : "malli.core.MapSchema"
933 | }, {
934 | "name" : "malli.core.RefSchema"
935 | }, {
936 | "name" : "malli.core.RegexSchema"
937 | }, {
938 | "name" : "malli.core.Schema"
939 | }, {
940 | "name" : "malli.core.Schemas"
941 | }, {
942 | "name" : "malli.core.Transformer"
943 | }, {
944 | "name" : "malli.core.Walker"
945 | }, {
946 | "name" : "malli.core__init"
947 | }, {
948 | "name" : "malli.edn__init"
949 | }, {
950 | "name" : "malli.error__init"
951 | }, {
952 | "name" : "malli.impl.regex.Cache"
953 | }, {
954 | "name" : "malli.impl.regex.CacheEntry"
955 | }, {
956 | "name" : "malli.impl.regex.CheckDriver"
957 | }, {
958 | "name" : "malli.impl.regex.Driver"
959 | }, {
960 | "name" : "malli.impl.regex.ExplanationDriver"
961 | }, {
962 | "name" : "malli.impl.regex.ICache"
963 | }, {
964 | "name" : "malli.impl.regex.IExplanationDriver"
965 | }, {
966 | "name" : "malli.impl.regex.IParseDriver"
967 | }, {
968 | "name" : "malli.impl.regex.IValidationDriver"
969 | }, {
970 | "name" : "malli.impl.regex.ParseDriver"
971 | }, {
972 | "name" : "malli.impl.regex__init"
973 | }, {
974 | "name" : "malli.impl.util.SchemaError"
975 | }, {
976 | "name" : "malli.impl.util__init"
977 | }, {
978 | "name" : "malli.json_schema.JsonSchema"
979 | }, {
980 | "name" : "malli.json_schema__init"
981 | }, {
982 | "name" : "malli.registry.Registry"
983 | }, {
984 | "name" : "malli.registry__init"
985 | }, {
986 | "name" : "malli.sci__init"
987 | }, {
988 | "name" : "malli.swagger.SwaggerSchema"
989 | }, {
990 | "name" : "malli.swagger__init"
991 | }, {
992 | "name" : "malli.transform__init"
993 | }, {
994 | "name" : "malli.util__init"
995 | }, {
996 | "name" : "meta_merge.core__init"
997 | }, {
998 | "name" : "muuntaja.core.Adapter"
999 | }, {
1000 | "name" : "muuntaja.core.FormatAndCharset"
1001 | }, {
1002 | "name" : "muuntaja.core.Muuntaja"
1003 | }, {
1004 | "name" : "muuntaja.core.MuuntajaHttp"
1005 | }, {
1006 | "name" : "muuntaja.core__init"
1007 | }, {
1008 | "name" : "muuntaja.format.core.Decode"
1009 | }, {
1010 | "name" : "muuntaja.format.core.EncodeToBytes"
1011 | }, {
1012 | "name" : "muuntaja.format.core.EncodeToOutputStream"
1013 | }, {
1014 | "name" : "muuntaja.format.core.Format"
1015 | }, {
1016 | "name" : "muuntaja.format.core__init"
1017 | }, {
1018 | "name" : "muuntaja.format.edn__init"
1019 | }, {
1020 | "name" : "muuntaja.format.json__init"
1021 | }, {
1022 | "name" : "muuntaja.format.transit__init"
1023 | }, {
1024 | "name" : "muuntaja.middleware__init"
1025 | }, {
1026 | "name" : "muuntaja.parse__init"
1027 | }, {
1028 | "name" : "muuntaja.protocols$eval1",
1029 | "methods" : [ {
1030 | "name" : "",
1031 | "parameterTypes" : [ ]
1032 | } ]
1033 | }, {
1034 | "name" : "muuntaja.protocols$eval138",
1035 | "methods" : [ {
1036 | "name" : "",
1037 | "parameterTypes" : [ ]
1038 | } ]
1039 | }, {
1040 | "name" : "muuntaja.protocols$eval142",
1041 | "methods" : [ {
1042 | "name" : "",
1043 | "parameterTypes" : [ ]
1044 | } ]
1045 | }, {
1046 | "name" : "muuntaja.protocols$eval3",
1047 | "methods" : [ {
1048 | "name" : "",
1049 | "parameterTypes" : [ ]
1050 | } ]
1051 | }, {
1052 | "name" : "muuntaja.protocols.IntoInputStream"
1053 | }, {
1054 | "name" : "muuntaja.protocols.StreamableResponse",
1055 | "allPublicFields" : true,
1056 | "queryAllPublicMethods" : true
1057 | }, {
1058 | "name" : "muuntaja.protocols__init"
1059 | }, {
1060 | "name" : "muuntaja.util__init"
1061 | }, {
1062 | "name" : "org.apache.commons.codec.binary.Base64"
1063 | }, {
1064 | "name" : "org.apache.commons.fileupload.FileItemIterator"
1065 | }, {
1066 | "name" : "org.apache.commons.fileupload.FileItemStream"
1067 | }, {
1068 | "name" : "org.apache.commons.fileupload.FileUpload"
1069 | }, {
1070 | "name" : "org.apache.commons.fileupload.ProgressListener"
1071 | }, {
1072 | "name" : "org.apache.commons.fileupload.UploadContext"
1073 | }, {
1074 | "name" : "org.apache.commons.io.IOUtils"
1075 | }, {
1076 | "name" : "reitit.Trie"
1077 | }, {
1078 | "name" : "reitit.Trie$Match"
1079 | }, {
1080 | "name" : "reitit.Trie$Matcher"
1081 | }, {
1082 | "name" : "reitit.coercion.Coercion"
1083 | }, {
1084 | "name" : "reitit.coercion.CoercionError"
1085 | }, {
1086 | "name" : "reitit.coercion.ParameterCoercion"
1087 | }, {
1088 | "name" : "reitit.coercion.malli.Coercer"
1089 | }, {
1090 | "name" : "reitit.coercion.malli.TransformationProvider"
1091 | }, {
1092 | "name" : "reitit.coercion.malli__init"
1093 | }, {
1094 | "name" : "reitit.coercion__init"
1095 | }, {
1096 | "name" : "reitit.core.Expand"
1097 | }, {
1098 | "name" : "reitit.core.Match"
1099 | }, {
1100 | "name" : "reitit.core.PartialMatch"
1101 | }, {
1102 | "name" : "reitit.core.Router"
1103 | }, {
1104 | "name" : "reitit.core__init"
1105 | }, {
1106 | "name" : "reitit.exception__init"
1107 | }, {
1108 | "name" : "reitit.impl.IntoString"
1109 | }, {
1110 | "name" : "reitit.impl__init"
1111 | }, {
1112 | "name" : "reitit.middleware.Endpoint"
1113 | }, {
1114 | "name" : "reitit.middleware.IntoMiddleware"
1115 | }, {
1116 | "name" : "reitit.middleware.Middleware"
1117 | }, {
1118 | "name" : "reitit.middleware__init"
1119 | }, {
1120 | "name" : "reitit.ring.Endpoint"
1121 | }, {
1122 | "name" : "reitit.ring.Methods"
1123 | }, {
1124 | "name" : "reitit.ring.coercion__init"
1125 | }, {
1126 | "name" : "reitit.ring.middleware.exception__init"
1127 | }, {
1128 | "name" : "reitit.ring.middleware.multipart__init"
1129 | }, {
1130 | "name" : "reitit.ring.middleware.muuntaja__init"
1131 | }, {
1132 | "name" : "reitit.ring.middleware.parameters__init"
1133 | }, {
1134 | "name" : "reitit.ring__init"
1135 | }, {
1136 | "name" : "reitit.spec.Problem"
1137 | }, {
1138 | "name" : "reitit.spec__init"
1139 | }, {
1140 | "name" : "reitit.swagger__init"
1141 | }, {
1142 | "name" : "reitit.swagger_ui__init"
1143 | }, {
1144 | "name" : "reitit.trie.CatchAll"
1145 | }, {
1146 | "name" : "reitit.trie.Match"
1147 | }, {
1148 | "name" : "reitit.trie.Matcher"
1149 | }, {
1150 | "name" : "reitit.trie.Node"
1151 | }, {
1152 | "name" : "reitit.trie.TrieCompiler"
1153 | }, {
1154 | "name" : "reitit.trie.Wild"
1155 | }, {
1156 | "name" : "reitit.trie__init"
1157 | }, {
1158 | "name" : "ring.core.protocols.StreamableResponseBody"
1159 | }, {
1160 | "name" : "ring.core.protocols__init"
1161 | }, {
1162 | "name" : "ring.middleware.multipart_params__init"
1163 | }, {
1164 | "name" : "ring.middleware.params__init"
1165 | }, {
1166 | "name" : "ring.util.codec.FormEncodeable"
1167 | }, {
1168 | "name" : "ring.util.codec__init"
1169 | }, {
1170 | "name" : "ring.util.io__init"
1171 | }, {
1172 | "name" : "ring.util.mime_type__init"
1173 | }, {
1174 | "name" : "ring.util.parsing__init"
1175 | }, {
1176 | "name" : "ring.util.request__init"
1177 | }, {
1178 | "name" : "ring.util.response__init"
1179 | }, {
1180 | "name" : "ring.util.time__init"
1181 | }, {
1182 | "name" : "spec_tools.core.Coercion"
1183 | }, {
1184 | "name" : "spec_tools.core.DynamicConforming"
1185 | }, {
1186 | "name" : "spec_tools.core.Spec"
1187 | }, {
1188 | "name" : "spec_tools.core.Transformer"
1189 | }, {
1190 | "name" : "spec_tools.core__init"
1191 | }, {
1192 | "name" : "spec_tools.form__init"
1193 | }, {
1194 | "name" : "spec_tools.impl__init"
1195 | }, {
1196 | "name" : "spec_tools.parse__init"
1197 | }, {
1198 | "name" : "spec_tools.transform__init"
1199 | }, {
1200 | "name" : "sun.security.provider.NativePRNG",
1201 | "methods" : [ {
1202 | "name" : "",
1203 | "parameterTypes" : [ ]
1204 | } ]
1205 | }, {
1206 | "name" : "sun.security.provider.SHA",
1207 | "methods" : [ {
1208 | "name" : "",
1209 | "parameterTypes" : [ ]
1210 | } ]
1211 | } ]
--------------------------------------------------------------------------------
/examples/native/resources/native-configuration/resource-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "resources" : {
3 | "includes" : [ {
4 | "pattern" : "\\Qclojure/version.properties\\E"
5 | } ]
6 | },
7 | "bundles" : [ ]
8 | }
--------------------------------------------------------------------------------
/examples/native/resources/native-configuration/serialization-config.json:
--------------------------------------------------------------------------------
1 | [
2 | ]
3 |
--------------------------------------------------------------------------------
/examples/native/resources/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Hello world
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/native/resources/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FieryCod/holy-lambda-ring-adapter/bd9c493ee0dc3e3b7d99c0f78e5bd7037d23efe9/examples/native/resources/public/logo.png
--------------------------------------------------------------------------------
/examples/native/src/example/lambda.clj:
--------------------------------------------------------------------------------
1 | (ns example.lambda
2 | (:gen-class)
3 | (:require
4 | [example.routes :as routes]
5 | [fierycod.holy-lambda.core :as h]
6 | [com.stuartsierra.component :as component]
7 | [fierycod.holy-lambda-ring-adapter.core :as hra]))
8 |
9 | (def handler (:ring-handler (component/start (routes/->ring-handler-component {}))))
10 | (def HttpAPIProxyGateway (hra/ring<->hl-middleware handler))
11 |
12 | (h/entrypoint [#'HttpAPIProxyGateway])
13 |
--------------------------------------------------------------------------------
/examples/native/src/example/routes.clj:
--------------------------------------------------------------------------------
1 | (ns example.routes
2 | (:require
3 | [reitit.coercion.malli :as coercion-malli]
4 | [reitit.ring.middleware.muuntaja :as muuntaja]
5 | [reitit.ring.coercion :as coercion]
6 | [reitit.ring.middleware.exception :as exception]
7 | [muuntaja.core :as m]
8 | [ring.util.response :as response]
9 | [reitit.ring.middleware.multipart :as multipart]
10 | [reitit.ring.middleware.parameters :as parameters]
11 | [reitit.swagger :as swagger]
12 | [ring.util.io :as ring-io]
13 | [com.stuartsierra.component :as component]
14 | [reitit.swagger-ui :as swagger-ui]
15 | [reitit.ring :as ring]))
16 |
17 | (defn- ring-handler
18 | [_dependencies]
19 | (ring/ring-handler
20 | (ring/router
21 | [["/resources/*" {:no-doc true
22 | :get {:handler (ring/create-resource-handler)}}]
23 | ["/welcome-screen" {:no-doc true
24 | :get {:handler (fn [_req]
25 | {:body ""
26 | :status 200
27 | :headers {"content-type" "text/html"}})}}]
28 | ["/api/v1" {:swagger {:info {:title "Application routes"
29 | :description "Lorem ipsum"}}}
30 | ["/seq" {:get {:handler (fn [_req]
31 | {:body '("hello" "world")
32 | :status 200})}}]
33 | ["/byte-array-hello" {:get {:handler (fn [_req]
34 | {:body (ring-io/string-input-stream "Hello world" "utf-8")
35 | :status 200})}}]
36 | ["/hello" {:description "Says Hello!"
37 | :get {:handler (fn [_req]
38 | (response/response {:hello "Hello world"}))}}]
39 | ["/say-hello" {:description "Now it's your turn to say hello"
40 | :post {:parameters {:body [:map
41 | [:hello string?]]}
42 | :handler (fn [{:keys [parameters]}]
43 | (response/response {:hello (:body parameters)}))}}]]
44 | ["" {:no-doc true}
45 | ["/swagger.json" {:get {:swagger {:info {:title "Ring API"
46 | :description "Ring API running on AWS Lambda"
47 | :version "1.0.0"}}
48 | :handler (swagger/create-swagger-handler)}}]
49 | ["/api-docs/*" {:get (swagger-ui/create-swagger-ui-handler)}]]]
50 | {:data {:coercion coercion-malli/coercion
51 | :muuntaja m/instance
52 | :middleware [;; query-params & form-params
53 | parameters/parameters-middleware
54 | ;; content-negotiation
55 | muuntaja/format-negotiate-middleware
56 | ;; encoding response body
57 | muuntaja/format-response-middleware
58 |
59 |
60 | ;; exception handling
61 | exception/exception-middleware
62 | ;; decoding request body
63 | muuntaja/format-request-middleware
64 | ;; coercing response bodys
65 | coercion/coerce-response-middleware
66 | ;; coercing request parameters
67 | coercion/coerce-request-middleware
68 | ;; multipart
69 | multipart/multipart-middleware
70 | ]}})
71 | (ring/create-default-handler
72 | {:not-found (constantly {:status 404
73 | :body "Not found route!"})})))
74 |
75 | (defrecord ^:private RingHandlerComponent [dependencies]
76 | component/Lifecycle
77 | (start [this]
78 | (assoc this :ring-handler (ring-handler dependencies)))
79 | (stop [this]
80 | (dissoc this :ring-handler)))
81 |
82 | (defn ->ring-handler-component
83 | [opts]
84 | (map->RingHandlerComponent opts))
85 |
--------------------------------------------------------------------------------
/examples/native/src/example/server.clj:
--------------------------------------------------------------------------------
1 | (ns example.server
2 | (:require
3 | [example.routes :as routes]
4 | [com.stuartsierra.component :as component]
5 | [ring.adapter.jetty :as jetty]))
6 |
7 | (defonce system nil)
8 |
9 | (defrecord Server [ring-handler]
10 | component/Lifecycle
11 | (start [this]
12 | (assoc this :server (jetty/run-jetty (:ring-handler ring-handler)
13 | {:port 3000
14 | :join? false})))
15 | (stop [this]
16 | (when-let [server (:server this)]
17 | (.stop server))
18 | (dissoc this :server)))
19 |
20 | (defn ->server
21 | [opts]
22 | (map->Server opts))
23 |
24 | (defn ->system
25 | []
26 | (-> (component/system-map
27 | :server (->server {})
28 | :ring-handler (routes/->ring-handler-component {}))
29 | (component/system-using
30 | {:server {:ring-handler :ring-handler}})))
31 |
32 | (alter-var-root #'system (fn [x]
33 | (when x (component/stop x))
34 | (->system)))
35 |
36 | (alter-var-root #'system (fn [x] (when x (component/start x))))
37 |
--------------------------------------------------------------------------------
/examples/native/template.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Transform: AWS::Serverless-2016-10-31
3 | Description: >
4 | Example basic lambda using `holy-lambda` micro library
5 |
6 | Parameters:
7 | Timeout:
8 | Type: Number
9 | Default: 40
10 | MemorySize:
11 | Type: Number
12 | Default: 512
13 |
14 | Globals:
15 | Function:
16 | Timeout: !Ref Timeout
17 | MemorySize: !Ref MemorySize
18 |
19 | Resources:
20 | ExampleLambdaFunction:
21 | Type: AWS::Serverless::Function
22 | Properties:
23 | Runtime: provided.al2
24 | Handler: example.lambda.HttpAPIProxyGateway
25 | CodeUri: ./.holy-lambda/build/latest.zip
26 | Events:
27 | HelloEvent:
28 | Type: HttpApi
29 | Properties:
30 | ApiId: !Ref ServerlessHttpApi
31 | Path: /{proxy+}
32 | Method: ANY
33 |
34 | ServerlessHttpApi:
35 | Type: AWS::Serverless::HttpApi
36 | DeletionPolicy: Retain
37 | Properties:
38 | StageName: Prod
39 |
40 | Outputs:
41 | TestEndpoint:
42 | Description: Test endpoint
43 | Value:
44 | Fn::Sub: https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com
45 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | io.github.FieryCod
5 | holy-lambda-ring-adapter
6 | jar
7 | 0.1.2
8 | holy-lambda-ring-adapter
9 | An adapter between Ring Core request/response model and Holy Lambda. Run Ring applications on AWS Lambda
10 | https://github.com/FieryCod/holy-lambda-ring-adapter
11 |
12 |
13 | MIT
14 | https://opensource.org/licenses/MIT
15 |
16 |
17 |
18 | https://github.com/FieryCod/holy-lambda-ring-adapter
19 | scm:git:git://github.com/FieryCod/holy-lambda-ring-adapter.git
20 | scm:git:ssh://git@github.com/FieryCod/holy-lambda-ring-adapter.git
21 | c168a86f1c4382479d2ac72993b610d1193b4774
22 |
23 |
24 | src
25 | test
26 |
27 |
28 | resources
29 |
30 |
31 |
32 |
33 | resources
34 |
35 |
36 | target
37 | target/classes
38 |
39 |
40 | org.codehaus.mojo
41 | build-helper-maven-plugin
42 | 1.7
43 |
44 |
45 | add-source
46 | generate-sources
47 |
48 | add-source
49 |
50 |
51 |
52 | test
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | central
63 | https://repo1.maven.org/maven2/
64 |
65 | false
66 |
67 |
68 | true
69 |
70 |
71 |
72 | clojars
73 | https://repo.clojars.org/
74 |
75 | true
76 |
77 |
78 | true
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | org.clojure
88 | clojure
89 | 1.10.3
90 | provided
91 |
92 |
93 | ring
94 | ring-core
95 | 1.9.4
96 | provided
97 |
98 |
99 | eftest
100 | eftest
101 | 0.5.9
102 | test
103 |
104 |
105 | io.github.FieryCod
106 | holy-lambda
107 | 0.5.1-SNAPSHOT
108 | test
109 |
110 |
111 | cheshire
112 | cheshire
113 | 5.10.0
114 | test
115 |
116 |
117 | metosin
118 | muuntaja
119 | 0.6.8
120 | test
121 |
122 |
123 | metosin
124 | reitit
125 | 0.5.15
126 | test
127 |
128 |
129 | clj-http
130 | clj-http
131 | 3.12.3
132 | test
133 |
134 |
135 |
136 |
137 |
141 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject io.github.FieryCod/holy-lambda-ring-adapter "0.1.2"
2 | :description "An adapter between Ring Core request/response model and Holy Lambda. Run Ring applications on AWS Lambda"
3 |
4 | :url "https://github.com/FieryCod/holy-lambda-ring-adapter"
5 |
6 | :license {:name "MIT"
7 | :url "https://opensource.org/licenses/MIT"}
8 |
9 | :source-paths ["src" "test"]
10 |
11 | :resources ["resources"]
12 |
13 | :global-vars {*warn-on-reflection* true}
14 |
15 | :dependencies [[org.clojure/clojure "1.10.3" :scope "provided"]
16 | [ring/ring-core "1.9.4" :scope "provided"]]
17 |
18 | :deploy-repositories [["releases" {:url "https://clojars.org/repo"
19 | :creds :gpg}
20 | "snapshots" {:url "https://clojars.org/repo"
21 | :creds :gpg}]]
22 | :scm {:name "git"
23 | :url "https://github.com/FieryCod/holy-lambda-ring-adapter"}
24 |
25 |
26 | :profiles {:dev {:dependencies [[eftest/eftest "0.5.9"]
27 | [io.github.FieryCod/holy-lambda "0.5.1-SNAPSHOT"]
28 | [cheshire/cheshire "5.10.0"]
29 | [metosin/muuntaja "0.6.8"]
30 | [metosin/reitit "0.5.15"]
31 | [clj-http/clj-http "3.12.3"]]}})
32 |
--------------------------------------------------------------------------------
/resources-test/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FieryCod/holy-lambda-ring-adapter/bd9c493ee0dc3e3b7d99c0f78e5bd7037d23efe9/resources-test/logo.png
--------------------------------------------------------------------------------
/resources/META-INF/native-image/io/github/FieryCod/holy-lambda-ring-adapter/native-image.properties:
--------------------------------------------------------------------------------
1 | Args=--initialize-at-build-time=clojure,fierycod.holy_lambda_ring_adapter
2 |
--------------------------------------------------------------------------------
/src/fierycod/holy_lambda_ring_adapter/codec.clj:
--------------------------------------------------------------------------------
1 | (ns fierycod.holy-lambda-ring-adapter.codec
2 | "Functions for encoding and decoding data. All credits to @weavejester.
3 | https://github.com/ring-clojure/ring-codec"
4 | (:require [clojure.string :as str])
5 | (:import
6 | java.util.Map
7 | [java.net URLEncoder]))
8 |
9 | (defn- double-escape [^String x]
10 | (.replace (.replace x "\\" "\\\\") "$" "\\$"))
11 |
12 | (def ^:private string-replace-bug?
13 | (= "x" (str/replace "x" #"." (fn [_x] "$0"))))
14 |
15 | (defmacro ^:no-doc fix-string-replace-bug [x]
16 | (if string-replace-bug?
17 | `(double-escape ~x)
18 | x))
19 |
20 | (defn- parse-bytes ^bytes [encoded-bytes]
21 | (let [encoded-len (count encoded-bytes)
22 | bs (byte-array (/ encoded-len 3))]
23 | (loop [encoded-index 1, byte-index 0]
24 | (if (< encoded-index encoded-len)
25 | (let [encoded-byte (subs encoded-bytes encoded-index (+ encoded-index 2))
26 | b (.byteValue (Integer/valueOf encoded-byte 16))]
27 | (aset bs byte-index b)
28 | (recur (+ encoded-index 3) (inc byte-index)))
29 | bs))))
30 |
31 | (defprotocol ^:no-doc FormEncodeable
32 | (form-encode* [x encoding]))
33 |
34 | (extend-protocol FormEncodeable
35 | String
36 | (form-encode* [^String unencoded ^String encoding]
37 | (URLEncoder/encode unencoded encoding))
38 | Map
39 | (form-encode* [params encoding]
40 | (letfn [(encode [x] (form-encode* x encoding))
41 | (encode-param [k v] (str (encode (name k)) "=" (encode v)))]
42 | (->> params
43 | (mapcat
44 | (fn [[k v]]
45 | (cond
46 | (sequential? v) (map #(encode-param k %) v)
47 | (set? v) (sort (map #(encode-param k %) v))
48 | :else (list (encode-param k v)))))
49 | (str/join "&"))))
50 | Object
51 | (form-encode* [x encoding]
52 | (form-encode* (str x) encoding))
53 | nil
54 | (form-encode* [_ __] ""))
55 |
56 | (defn form-encode
57 | "Encode the supplied value into www-form-urlencoded format, often used in
58 | URL query strings and POST request bodies, using the specified encoding.
59 | If the encoding is not specified, it defaults to UTF-8"
60 | ([x]
61 | (form-encode x "UTF-8"))
62 | ([x encoding]
63 | (form-encode* x encoding)))
64 |
--------------------------------------------------------------------------------
/src/fierycod/holy_lambda_ring_adapter/core.cljc:
--------------------------------------------------------------------------------
1 | (ns fierycod.holy-lambda-ring-adapter.core
2 | (:require
3 | [clojure.string :as s]
4 | [fierycod.holy-lambda-ring-adapter.codec :as codec]
5 | [fierycod.holy-lambda-ring-adapter.impl :as impl]))
6 |
7 | #?(:bb nil
8 | :clj (set! *warn-on-reflection* true))
9 |
10 | (defn hl-request->ring-request
11 | "Transforms valid Holy Lambda request to compatible Ring request
12 |
13 | **Examples**
14 |
15 | ```clojure
16 | (ns core
17 | (:require
18 | [fierycod.holy-lambda-ring-adapter.core :as hlra]
19 | [fierycod.holy-lambda.core :as h])
20 |
21 | (defn ring-handler
22 | [response]
23 | {:status 200
24 | :headers {}
25 | :body \"Hello World\"})
26 |
27 | (defn HttpApiProxyGateway
28 | [request]
29 | (hlra/ring-response->hl-response (ring-handler (hlra/hl-request->ring-request request))))
30 |
31 | (h/entrypoint [#'HttpApiProxyGateway])
32 | ```"
33 | [{:keys [event ctx]}]
34 | (let [request-ctx (get event :requestContext)
35 | http (get request-ctx :http)
36 | headers (get event :headers)
37 | base64? (get event :isBase64Encoded)]
38 | (when-not request-ctx
39 | (throw (ex-info "Incorrect shape of AWS event. The adapter is compatible with following integrations: HttpApi and RestApi on AWS Api Gateway service. If you're testing locally make sure the event shape is valid e.g. use `sam local start-api` instead of `sam local invoke`." {:ctx :hl-ring-adapter})))
40 |
41 | {:server-port (some-> (get headers "x-forwarded-port") (Integer/parseInt))
42 | :body (impl/to-ring-request-body (:body event) base64?)
43 | :server-name (or (get http :sourceIp)
44 | (get-in request-ctx [:identity :sourceIp]))
45 | :remote-addr (or (get http :sourceIp)
46 | (get-in request-ctx [:identity :sourceIp]))
47 | :uri (or (get http :path)
48 | (get event :path))
49 | :query-string (or (get event :rawQueryString)
50 | (some-> (get event :queryStringParameters)
51 | codec/form-encode))
52 | :scheme (some-> (get headers "x-forwarded-proto" "http") keyword)
53 | :request-method (some-> (or (get http :method)
54 | (get request-ctx :httpMethod))
55 | s/lower-case
56 | keyword)
57 | :protocol (or (get http :protocol)
58 | (get request-ctx :protocol))
59 | :headers headers
60 | :lambda {:ctx ctx
61 | :event event}}))
62 |
63 | (defn- hl-request->ring-request!!
64 | [request]
65 | (assert (and (contains? request :event) (contains? request :ctx))
66 | "Incorrect Holy Lambda Request/Response model. Incorrect middleware position?")
67 | (hl-request->ring-request request))
68 |
69 | (defn ring-response->hl-response
70 | "Transforms valid Ring response to Holy Lambda compatible response
71 |
72 | **Examples**
73 |
74 | ```clojure
75 | (ns core
76 | (:require
77 | [fierycod.holy-lambda-ring-adapter.core :as hlra]
78 | [fierycod.holy-lambda.core :as h])
79 |
80 | (defn ring-handler
81 | [response]
82 | {:status 200
83 | :headers {}
84 | :body \"Hello World\"})
85 |
86 | (defn HttpApiProxyGateway
87 | [request]
88 | (hlra/ring-response->hl-response (ring-handler (hlra/hl-request->ring-request request))))
89 |
90 | (h/entrypoint [#'HttpApiProxyGateway])
91 | ```"
92 | [response]
93 | (let [^impl/RingResponseBody body (:body response)
94 | {:keys [body encoded?]} (impl/to-hl-response-body body)]
95 | {:statusCode (:status response)
96 | :body body
97 | :isBase64Encoded encoded?
98 | :headers (:headers response)}))
99 |
100 | (defn ring<->hl-middleware
101 | "Middleware that converts Ring Request/Response model to Holy Lambda (AWS Lambda) Request/Response model.
102 | Ideal for running regular ring applications on AWS Lambda.
103 |
104 | Middleware supports both `sync/async` ring handlers.
105 |
106 | **Examples**
107 |
108 | 1. With plain Ring:
109 |
110 | ```clojure
111 | (ns core
112 | (:require
113 | [fierycod.holy-lambda-ring-adapter.core :as hlra]
114 | [fierycod.holy-lambda.core :as h])
115 |
116 | (defn ring-handler
117 | [request]
118 | {:status 200
119 | :headers {}
120 | :body \"Hello World\"}
121 |
122 | (def HttpApiProxyGateway (hlra/ring<->hl-middleware ring-handler))
123 |
124 | (h/entrypoint [#'HttpApiProxyGateway])
125 | ```
126 |
127 | 2. With muuntaja:
128 |
129 | ```clojure
130 | (ns core
131 | (:require
132 | [fierycod.holy-lambda-ring-adapter.core :as hlra]
133 | [fierycod.holy-lambda.core :as h])
134 |
135 | (def muuntaja-ring-handler
136 | (ring/ring-handler
137 | (ring/router
138 | routes
139 | {:data {:muuntaja instance
140 | :coercion coerction
141 | :middleware middlewares}})))
142 |
143 | (def HttpApiProxyGateway (hlra/ring<->hl-middleware muuntaja-ring-handler))
144 |
145 | (h/entrypoint [#'HttpApiProxyGateway])
146 | ```"
147 | [handler]
148 | (fn
149 | ([request]
150 | (ring-response->hl-response (handler (hl-request->ring-request!! request))))
151 | ([request respond raise]
152 | (try
153 | (handler (hl-request->ring-request!! request)
154 | (fn [response]
155 | (try
156 | (respond (ring-response->hl-response response))
157 | (catch Exception ex
158 | (raise ex))))
159 | raise)
160 | (catch Exception ex
161 | (raise ex))))))
162 |
163 | (def ^:deprecated wrap-hl-req-res-model
164 | "DEPRECATED. Subject to remove in 0.5.0.
165 | Use `ring<->hl-middleware` instead!"
166 | ring<->hl-middleware)
167 |
--------------------------------------------------------------------------------
/src/fierycod/holy_lambda_ring_adapter/impl.cljc:
--------------------------------------------------------------------------------
1 | (ns fierycod.holy-lambda-ring-adapter.impl
2 | (:import
3 | [java.io InputStream File ByteArrayInputStream]
4 | [java.util Base64 Base64$Decoder Base64$Encoder]
5 | [java.net URL]
6 | [java.nio.file Files]
7 | [clojure.lang IPersistentCollection])
8 | #?(:bb
9 | (:require
10 | [clojure.java.io :as io])
11 | :clj
12 | (:require
13 | [ring.util.response :as resp]
14 | [clojure.java.io :as io])))
15 |
16 | #?(:bb nil
17 | :clj (set! *warn-on-reflection* true))
18 |
19 | ;; See https://github.com/ring-clojure/ring/pull/447/files
20 | #?(:bb nil
21 | :clj
22 | (defmethod resp/resource-data :resource
23 | [^URL url]
24 | ;; GraalVM resource scheme
25 | (let [resource (.openConnection url)]
26 | {:content (.getInputStream resource)
27 | :content-length (#'ring.util.response/connection-content-length resource)
28 | :last-modified (#'ring.util.response/connection-last-modified resource)})))
29 |
30 | (def ^:private base64-decoder (delay (Base64/getDecoder)))
31 | (def ^:private base64-encoder (delay (Base64/getEncoder)))
32 |
33 | (defprotocol RingResponseBody
34 | (to-hl-response-body [body] "Adapts the RingResponseBody to valid HLResponseBody"))
35 |
36 | (extend-protocol RingResponseBody
37 | ByteArrayInputStream
38 | (to-hl-response-body [^ByteArrayInputStream body]
39 | {:body (new String ^bytes (.encode ^Base64$Encoder @base64-encoder (.readAllBytes body)))
40 | :encoded? true})
41 |
42 | InputStream
43 | (to-hl-response-body [^InputStream body]
44 | {:body (new String ^bytes (.encode ^Base64$Encoder @base64-encoder (.readAllBytes body)))
45 | :encoded? true})
46 |
47 | File
48 | (to-hl-response-body [^File body]
49 | {:body (new String ^bytes (.encode ^Base64$Encoder @base64-encoder ^bytes (Files/readAllBytes (.toPath body))))
50 | :encoded? true})
51 |
52 | String
53 | (to-hl-response-body [^String body]
54 | {:body body
55 | :encoded? false})
56 |
57 | URL
58 | (to-hl-response-body [^URL body]
59 | {:body (new String ^bytes (.encode ^Base64$Encoder @base64-encoder ^bytes (.readAllBytes (.getInputStream (.openConnection body)))))
60 | :encoded? true})
61 |
62 | IPersistentCollection
63 | (to-hl-response-body [^IPersistentCollection body]
64 | {:body body
65 | :encoded? false})
66 |
67 | Object
68 | (to-hl-response-body [^Object body]
69 | {:body (str body)
70 | :encoded? false})
71 |
72 | nil
73 | (to-hl-response-body [_]
74 | {:body nil
75 | :encoded? false}))
76 |
77 | (defprotocol HLRequestBody
78 | (to-ring-request-body [body base64?] "Adapts the HLRequestBody to valid RingRequestBody"))
79 |
80 | #?(:clj
81 | (extend-protocol HLRequestBody
82 | String
83 | (to-ring-request-body [^String body base64?]
84 | (io/input-stream
85 | (if-not base64?
86 | (.getBytes body)
87 | (.decode ^Base64$Decoder @base64-decoder ^String body))))
88 |
89 | Object
90 | (to-ring-request-body [^Object body _base64?]
91 | (pr-str body))
92 |
93 | nil
94 | (to-ring-request-body [_ _]
95 | nil)))
96 |
--------------------------------------------------------------------------------
/test/fierycod/holy_lambda_ring_adapter/core_test.clj:
--------------------------------------------------------------------------------
1 | (ns fierycod.holy-lambda-ring-adapter.core-test
2 | (:require
3 | [clj-http.client :as client]
4 | [ring.adapter.jetty :as jetty]
5 | [muuntaja.core :as m]
6 | [ring.util.response :as response]
7 | [reitit.coercion.malli :as rcm]
8 | [reitit.ring :as ring]
9 | [reitit.ring.coercion :as rrc]
10 | [reitit.ring.middleware.parameters :as parameters]
11 | [reitit.ring.middleware.muuntaja :as rrmm]
12 | [reitit.dev.pretty :as pretty]
13 | [clojure.test :as t]
14 | [fierycod.holy-lambda-ring-adapter.core :as hra]
15 | [clojure.java.io :as io]))
16 |
17 | (defn bytes-response->string-response
18 | [response]
19 | (assoc response :body (new String ^bytes (:body response))))
20 |
21 | (defn ->reitit-ring-handler
22 | [routes]
23 | (ring/ring-handler
24 | (ring/router
25 | routes
26 | {:exception pretty/exception
27 | :data {:muuntaja m/instance
28 | :coercion rcm/coercion
29 | :middleware [parameters/parameters-middleware
30 | rrmm/format-middleware
31 | rrc/coerce-exceptions-middleware
32 | rrc/coerce-response-middleware
33 | rrc/coerce-request-middleware]}})
34 | (ring/create-default-handler
35 | {:not-found (constantly {:status 404 :body "Not found" :headers {"content-type" "text/plain"}})})))
36 |
37 | (defn ring-spec-props
38 | [request]
39 | (select-keys request [:server-port
40 | :server-name
41 | :remote-addr
42 | :uri
43 | :query-string
44 | :scheme
45 | :request-method
46 | :protocol
47 | :headers]))
48 |
49 | (defn basic-ring-handler
50 | [request]
51 | {:status 200
52 | :body (str "hello world " (:protocol request))
53 | :headers {"something" "something"}})
54 |
55 | (defn basic-ring-handler-async
56 | [request respond _raise]
57 | (respond {:status 200
58 | :body (str "hello world " (:protocol request))
59 | :headers {"something" "something"}}))
60 |
61 | (defn request->ring-request-test
62 | [hl-request]
63 | (ring-spec-props (hra/hl-request->ring-request hl-request)))
64 |
65 | (t/deftest http-api-basic-1
66 | (t/testing "should correctly transform request->ring-request #1"
67 | (let [hl-request {:event
68 | {:routeKey "GET /bb-amd64",
69 | :queryStringParameters {:holy-lambda "example", :hello "world"},
70 | :pathParameters {},
71 | :cookies ["totalOrders=76" "merged-cart=1"],
72 | :headers
73 | {"sec-fetch-site" "none",
74 | "host" "localhost:3000",
75 | "user-agent"
76 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36",
77 | "cookie"
78 | "totalOrders=76; merged-cart=1",
79 | "sec-fetch-user" "?1",
80 | "x-forwarded-port" "3000",
81 | "sec-ch-ua-platform" "\"Linux\"",
82 | "connection" "keep-alive",
83 | "upgrade-insecure-requests" "1",
84 | "accept"
85 | "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
86 | "x-forwarded-proto" "http",
87 | "sec-fetch-mode" "navigate"},
88 | :stageVariables nil,
89 | :rawQueryString "holy-lambda=example&hello=world",
90 | :rawPath "/bb-amd64",
91 | :isBase64Encoded false,
92 | :requestContext
93 | {:accountId "123456789012",
94 | :apiId "1234567890",
95 | :http
96 | {:method "GET",
97 | :path "/bb-amd64",
98 | :protocol "HTTP/1.1",
99 | :sourceIp "127.0.0.1",
100 | :userAgent "Custom User Agent String"},
101 | :requestId "692eb753-3353-4a03-9321-26376e8d02e7",
102 | :routeKey "GET /bb-amd64",
103 | :stage nil},
104 | :version "2.0",
105 | :body ""},
106 | :ctx {}}]
107 | (t/is (= {:protocol "HTTP/1.1",
108 | :remote-addr "127.0.0.1",
109 | :headers
110 | {"sec-fetch-site" "none",
111 | "host" "localhost:3000",
112 | "user-agent"
113 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36",
114 | "cookie" "totalOrders=76; merged-cart=1",
115 | "sec-fetch-user" "?1",
116 | "x-forwarded-port" "3000",
117 | "sec-ch-ua-platform" "\"Linux\"",
118 | "connection" "keep-alive",
119 | "upgrade-insecure-requests" "1",
120 | "accept"
121 | "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
122 | "x-forwarded-proto" "http",
123 | "sec-fetch-mode" "navigate"},
124 | :server-port 3000,
125 | :uri "/bb-amd64",
126 | :server-name "127.0.0.1",
127 | :query-string "holy-lambda=example&hello=world",
128 | :scheme :http,
129 | :request-method :get}
130 | (request->ring-request-test hl-request)))
131 | (t/is (= {:status 200,
132 | :body "hello world HTTP/1.1",
133 | :headers {"something" "something"}}
134 | (basic-ring-handler (request->ring-request-test hl-request))))))
135 |
136 | (t/testing "should correctly transform request->ring-request #2"
137 | (let [request1 {:event
138 | {:routeKey "GET /bb-amd64",
139 | :queryStringParameters {:holy-lambda "example", :hello "world"},
140 | :pathParameters {},
141 | :cookies ["totalOrders=76" "merged-cart=1"],
142 | :headers
143 | {"sec-fetch-site" "none",
144 | "host" "localhost:3000",
145 | "user-agent"
146 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36",
147 | "cookie"
148 | "totalOrders=76; merged-cart=1",
149 | "sec-fetch-user" "?1",
150 | "x-forwarded-port" "3000",
151 | "sec-ch-ua-platform" "\"Linux\"",
152 | "connection" "keep-alive",
153 | "upgrade-insecure-requests" "1",
154 | "accept"
155 | "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
156 | "x-forwarded-proto" "http",
157 | "sec-fetch-mode" "navigate"},
158 | :stageVariables nil,
159 | :rawQueryString "holy-lambda=example&hello=world",
160 | :rawPath "/bb-amd64",
161 | :isBase64Encoded false,
162 | :requestContext
163 | {:accountId "123456789012",
164 | :apiId "1234567890",
165 | :http
166 | {:method "GET",
167 | :path "/bb-amd64",
168 | :protocol "HTTP/1.1",
169 | :sourceIp "127.0.0.1",
170 | :userAgent "Custom User Agent String"},
171 | :requestId "692eb753-3353-4a03-9321-26376e8d02e7",
172 | :routeKey "GET /bb-amd64",
173 | :stage nil},
174 | :version "2.0",
175 | :body ""},
176 | :ctx {}}
177 | request2 (assoc-in request1 [:event :requestContext :http :path] "/hello/world")
178 | handler (->reitit-ring-handler
179 | [["/:hello/:world"
180 | {:get {:parameters {:path
181 | [:map
182 | [:hello string?]
183 | [:world string?]]}
184 | :handler (fn [_request]
185 | {:status 200
186 | :body "Hello world"})}}]])]
187 | (t/is (= {:status 404, :body "Not found", :headers {"content-type" "text/plain"}}
188 | (handler (request->ring-request-test request1))))
189 | (t/is (= {:status 404, :body "Not found", :headers {"content-type" "text/plain"}}
190 | (handler (request->ring-request-test request1))))
191 | (t/is (= {:statusCode 200,
192 | :body "Hello world",
193 | :isBase64Encoded false,
194 | :headers nil}
195 | (hra/ring-response->hl-response (handler (request->ring-request-test request2)))))
196 | (t/is (= {:statusCode 200,
197 | :body "hello world HTTP/1.1",
198 | :isBase64Encoded false,
199 | :headers {"something" "something"}}
200 | ((hra/ring<->hl-middleware basic-ring-handler) request2)))
201 | (t/is (= {:statusCode 200,
202 | :body "hello world HTTP/1.1",
203 | :isBase64Encoded false,
204 | :headers {"something" "something"}}
205 | ((hra/ring<->hl-middleware basic-ring-handler-async) request2 identity identity))))))
206 |
207 | (t/deftest http-api-json-coerce-1
208 | (t/testing "json coercion should work"
209 | (let [request {:event
210 | {:routeKey "POST /",
211 | :queryStringParameters {},
212 | :pathParameters {:proxy "hello"},
213 | :headers
214 | {"host" "localhost:3000",
215 | "content-type" "application/json",
216 | "accept" "*/*",
217 | "content-length" "21",
218 | "x-forwarded-proto" "http",
219 | "x-forwarded-port" "3000"},
220 | :stageVariables nil,
221 | :rawQueryString "",
222 | :rawPath "/hello",
223 | :isBase64Encoded false,
224 | :requestContext
225 | {:accountId "123456789012",
226 | :apiId "1234567890",
227 | :http
228 | {:method "POST",
229 | :path "/hello",
230 | :protocol "HTTP/1.1",
231 | :sourceIp "127.0.0.1",
232 | :userAgent "Custom User Agent String"},
233 | :requestId "85f48e37-542e-4fae-afa0-c9ee1fa48a05",
234 | :routeKey "POST /",
235 | :stage nil},
236 | :version "2.0",
237 | :body "{\n\t\"hello\": \"world\"\n}"}
238 | :ctx {}}
239 | handler (->reitit-ring-handler
240 | [["/hello" {:post {:handler (fn [_request]
241 | {:status 200
242 | :body {:hello "world"
243 | :inner-body (:body-params _request)}})}}]])]
244 |
245 | ;; The case where (HL <= 0.6.1) does not automatically decodes the input
246 | (t/is (= {:statusCode 200,
247 | :body "eyJoZWxsbyI6IndvcmxkIiwiaW5uZXItYm9keSI6eyJoZWxsbyI6IndvcmxkIn19",
248 | :isBase64Encoded true,
249 | :headers {"Content-Type" "application/json; charset=utf-8"}}
250 | ((hra/ring<->hl-middleware handler) request)))
251 |
252 | ;; The case where (HL >= 0.6.2) does automatically decodes the input
253 | (t/is (= {:statusCode 200,
254 | :body "eyJoZWxsbyI6IndvcmxkIiwiaW5uZXItYm9keSI6eyJoZWxsbyI6IndvcmxkIn19",
255 | :isBase64Encoded true,
256 | :headers {"Content-Type" "application/json; charset=utf-8"}}
257 | ((hra/ring<->hl-middleware handler) (-> request
258 | (assoc-in [:event :body] "{\n\t\"hello\": \"world\"\n}")
259 | (assoc-in [:event :body-parsed] {:hello "world"}))))))))
260 |
261 |
262 | (t/deftest http-api-form-coerce-1
263 | (t/testing "x-www-form-urlencoded coercion should work"
264 | (let [request {:event
265 | {:routeKey "POST /",
266 | :queryStringParameters {},
267 | :pathParameters {:proxy "hello"},
268 | :cookies [],
269 | :headers
270 | {"connection" "close",
271 | "content-type" "application/x-www-form-urlencoded",
272 | "accept-encoding" "gzip, deflate",
273 | "content-length" "11",
274 | "host" "localhost:3000",
275 | "x-forwarded-proto" "http",
276 | "x-forwarded-port" "3000"},
277 | :stageVariables nil,
278 | :rawQueryString "",
279 | :rawPath "/hello",
280 | :isBase64Encoded false,
281 | :requestContext
282 | {:accountId "123456789012",
283 | :apiId "1234567890",
284 | :http
285 | {:method "POST",
286 | :path "/hello",
287 | :protocol "HTTP/1.1",
288 | :sourceIp "127.0.0.1",
289 | :userAgent "Custom User Agent String"},
290 | :requestId "af9f7839-2aa5-4aef-94fd-111b267f5dfa",
291 | :routeKey "POST /",
292 | :stage nil},
293 | :version "2.0",
294 | :body "hello=world"},
295 | :ctx
296 | {}}
297 | handler (->reitit-ring-handler
298 | [["/hello" {:post {:handler (fn [_request]
299 | {:status 200
300 | :body {:hello "world"
301 | :form-params (:form-params _request)}})}}]])]
302 |
303 | (t/is (= {:statusCode 200,
304 | :body "eyJoZWxsbyI6IndvcmxkIiwiZm9ybS1wYXJhbXMiOnsiaGVsbG8iOiJ3b3JsZCJ9fQ==",
305 | :isBase64Encoded true,
306 | :headers {"Content-Type" "application/json; charset=utf-8"}}
307 | ((hra/ring<->hl-middleware handler) request)))
308 | (t/is (= {:statusCode 200,
309 | :body "eyJoZWxsbyI6IndvcmxkIiwiZm9ybS1wYXJhbXMiOnsiYTMiOiIzIiwiYTkiOiI5IiwiYTciOiI3IiwiYTYiOiI2IiwiYTgiOiI4IiwiYSI6WyIxIiwiMiIsIjMiXSwiYTQiOiI0IiwiYTEiOiIxIiwiYTUiOiI1IiwiYTIiOiIyIn19",
310 | :isBase64Encoded true,
311 | :headers {"Content-Type" "application/json; charset=utf-8"}}
312 | ((hra/ring<->hl-middleware handler) (assoc-in request [:event :body] "a3=3&a9=9&a7=7&a6=6&a8=8&a4=4&a1=1&a5=5&a2=2&a=1&a=2&a=3"))))
313 | (t/is (= {:statusCode 200,
314 | :body "eyJoZWxsbyI6IndvcmxkIiwiZm9ybS1wYXJhbXMiOnsiYTMiOiIzIiwiYTkiOiI5IiwiYTciOiI3IiwiYTYiOiI2IiwiYTgiOiI4IiwiYSI6WyIxIiwiMiIsIjMiXSwiYTQiOiI0IiwiYTEiOiIxIiwiYTUiOiI1IiwiSGVsbG8gV29ybGQgSXQncyBNZSBZb3UgTG9va2luZyBGb3IiOiJIZWxsbyBXb3JsZCAhICEgISIsImEyIjoiMiJ9fQ==",
315 | :isBase64Encoded true,
316 | :headers {"Content-Type" "application/json; charset=utf-8"}}
317 | ((hra/ring<->hl-middleware handler) (assoc-in request [:event :body] "a3=3&a9=9&a7=7&a6=6&a8=8&a4=4&a1=1&a5=5&Hello+World+It%27s+Me+You+Looking+For=Hello+World+%21+%21+%21&a2=2&a=1&a=2&a=3")))))))
318 |
319 | (t/deftest binary-alike-data-response-1
320 | (t/testing "should correctly base64 encode a file"
321 | (let [request {:event
322 | {:routeKey "GET /",
323 | :rawQueryString "",
324 | :rawPath "/",
325 | :isBase64Encoded false,
326 | :requestContext
327 | {:http
328 | {:method "GET",
329 | :path "/",
330 | :protocol "HTTP/1.1",
331 | :sourceIp "127.0.0.1",
332 | :userAgent "Custom User Agent String"},
333 | :requestId "af9f7839-2aa5-4aef-94fd-111b267f5dfa",
334 | :routeKey "GET /",
335 | :stage nil}},
336 | :ctx
337 | {}}
338 | file (io/file "test/fierycod/holy_lambda_ring_adapter/logo.png")
339 | handler (hra/ring<->hl-middleware
340 | (->reitit-ring-handler
341 | [["/" {:get {:handler (fn [_request]
342 | (response/response file))}}]]))]
343 |
344 | (t/is (= true (:isBase64Encoded (handler request))))
345 | (t/is (= "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" (new String (:body (handler request)))))))
346 |
347 | (t/testing "should correctly base64 encode a resource"
348 | (let [request {:event
349 | {:routeKey "GET /",
350 | :rawQueryString "",
351 | :rawPath "/",
352 | :isBase64Encoded false,
353 | :requestContext
354 | {:http
355 | {:method "GET",
356 | :path "/",
357 | :protocol "HTTP/1.1",
358 | :sourceIp "127.0.0.1",
359 | :userAgent "Custom User Agent String"},
360 | :requestId "af9f7839-2aa5-4aef-94fd-111b267f5dfa",
361 | :routeKey "GET /",
362 | :stage nil}},
363 | :ctx
364 | {}}
365 | resource (io/resource "logo.png")
366 | handler (hra/ring<->hl-middleware
367 | (->reitit-ring-handler
368 | [["/" {:get {:handler (fn [_request]
369 | (response/response resource))}}]]))]
370 | (t/is (= true (:isBase64Encoded (handler request))))
371 | (t/is (= "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" (new String (:body (handler request))))))))
372 |
373 | (comment
374 | (def server (jetty/run-jetty (->reitit-ring-handler
375 | [["/" {:get {:handler (fn [_request]
376 | (response/response (io/file "test/src/fierycod/holy_lambda_ring_adapter/logo.png")))}}]])
377 | {:join? false
378 | :port 3000}))
379 |
380 | (client/post "http://localhost:3000/hello"
381 | {:accept :json
382 | :as :json
383 | :body "{\"hello\": \"world\"}"
384 | :content-type :json})
385 |
386 | (client/post "http://localhost:3000/hello"
387 | {:form-params
388 | (into {:a [1 2 3]
389 | "Hello World It's Me You Looking For" "Hello World ! ! !"}
390 | (for [i (range 1 10)]
391 | [(str "a" i) i]))})
392 |
393 | (.stop server)
394 | )
395 |
--------------------------------------------------------------------------------
/test/fierycod/holy_lambda_ring_adapter/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FieryCod/holy-lambda-ring-adapter/bd9c493ee0dc3e3b7d99c0f78e5bd7037d23efe9/test/fierycod/holy_lambda_ring_adapter/logo.png
--------------------------------------------------------------------------------