├── .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 | holy-lambda logo 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 | [![Clojars Project](https://img.shields.io/clojars/v/io.github.FieryCod/holy-lambda-ring-adapter?labelColor=283C67&color=729AD1&style=for-the-badge&logo=clojure&logoColor=fff)](https://clojars.org/io.github.FieryCod/holy-lambda-ring-adapter) 80 | 81 | ## Getting Help 82 | [![Get help on Slack](http://img.shields.io/badge/slack-clojurians%20%23holy--lambda-97C93C?labelColor=283C67&logo=slack&style=for-the-badge)](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 (= "iVBORw0KGgoAAAANSUhEUgAAAV4AAAFeCAMAAAD69YcoAAABxVBMVEUAAAD/mQD/lgD/igCPtf6VtP//mQD/nAD/mQD/mQD/mQD/mQCQtf//mQD/mQCPtf7/mQD/mQD/mQD/mAD/mQD/mQD/mQD/mQD/mwD/mQD/mQD/mQD/mQCR3Ef/mQD/mQD/mQD/mQD/mQD/mgCW3HOS3Uf/mQD/mQD/mQD/mAD/mQD/mQD/mQD/mQD/mQD/mgD/mQD/mQD/mQD/mQD/mQD/mQD/mQD/mQD/mQD/mwCPtf6R3Ef/mQD/mgCR3Ef/mQCR3Ef/mgD/mgCPtf//mQD/mQCR3Uf/mQD/mQCPtf+Ptf+Ntf+R3EeR307/mQD/mQCPtf3/mQCPtf+R3Eb/mACPtf6R3Ef/mQCR3Ef/mQCPtf//mQCPtf+R3EePtf6R3EePtf6Ptf6TuP+R3EePtf3/mQCPtf2Ptv2R3EePtf+Ptf//mQCR3EaR3UeR3kb/mgCX40yR3EePtf6Ptf2R3UePtf+Ptf//mQCS3EePtf6Ptf6Ptf6R3EeR3UeRtP+R3Ef/mQCR3UeR3UaPtf6R3EaQ3EiT4EeS3EeR3EePtv6R3EeRsf+R3EeR3UeR3Ef/lwCRtP+Ptv6Otf+Nt///mQCR3EePtf5ed6dTAAAAlHRSTlMA/QgC7xCABu+qsvNV+sO7oiFEDd7XvU0Q2/bPzGs/+OKnZYgDfhvGenfTSkc2MimajLeunl5b5jsL+ctpH/vrsG8jJ1VR9smROzEG3wfo5I1iRRUU9fGUhYR8bGs/2tbKwwvso5eUhGFcTi1XLCYYCrurnJd0ZCYg59Kzo3IgnHRINOE6GhDCUOzjE+iPeR0buBYYmO+62wAAGeRJREFUeNrs3N1K40AUB/AzCeQTS9rEpmqotoHYT61bqxUUqVTq1UpBkMVrr7zw0lvfYG/mgZdl2d0zTVKt+egk6e8BpAyTM+d/MhF4JpT73bOJ9NioOs62ZSmKTqmuKJa17TjVxqM0Oev1ywJsrEYuj6ftQdUi9DMUR3WbleML2PiQV5kYzj79AmJp0vRYhI1gx83nkkIjUkrP82PYYAj1lqbQ2Chaq74pyX8IvZPZFo3d1uFJr/BLXG6qOk3MbrV9BEUldB93aOJ2HrsF3MSCbSg0JbvaqFBdm9xdZW337ztVVR0MHl1Xarcl130cDFS12rlfoX9TjK4MxVA5/czakoOSIc17R1cmhDLL/d5cMkr35DMrfFqH3Htt7Xyidb2s3MiwAvmtcvmJpnmn/Qo5JnaHy1uwbbVllyP0IXZL3Vnerg17ec11nnSwrMKWXPsBYvBgnxwuq8qWlMct/GKEb1xdex8LECNh3Nb08C18+gL5MtZI6G4a2AIkQBy7DqEhSjbkhjzaC9u2w+ZPSNDV5TBsE++N8tGpCZP7kLVt3AqQOOFW3aWB7p+yH+fkkRV8kmkjE1Ii2EbwHj6YZHuBxXng4pJvZymnVPPsGwks/KPs9mniNLAJPXfLsAZe2wpc4GZGF7jSoX5Es0VYE9EO7F/2shiW3zTq913yYK086Tv1G5YhW0xpPyDy83CSyKNOQNC4MyE7xFHAHilNeSlyFY36XE94+XUf6vtTBGlw9VbmqEHoIoerXxhKcIlvcVXu3o0fGySTFaLub8aqXO6LY9W3wFYF+FYbkGws7m8vKl2kPgDHznxHWqkPHBsf+jLPFHhVM3yhfgScsy3fBq4Bl8YWZe26GTgrhLa+uCd4THGiRBbbhSvIBM9Y/OV33I2C35zFJM910WWNF4Pc7Aa40tQpY7/F3QZYRm4tZHh9DvwQTimrxF2M+MhNlbIMDuYjf/ycUYaSnfyOjJSF6sbJGK13TRmaB5nkaZRxzUWGaxO2G5tAZo10ihFp7U+hOaSM2Rtk2GL/MzRhrbw93jvGaN175wrW6OWA/7wTLXueH8Ha2Dr7KHGa1ldT0yimd2FNmlsUIe7aD4JEDmvyBGshsW1MD3Kjzs5V70RIncC2DA4nTXg8yuyJrQqQsotvbITM2ac3CzG/akKqzBJFSIajRJgJoUjJhBTVDimi5+gu8n/2LlP8HiA1D3u8tIZJ6p9TZPsVUvLa4SfYJImNpNsepMKzmKqfiywRrFaliOVBCl63KWJkfMiwnNxg9m8K9bfmUOQ5L0ktzB1TB2uQMHNGkRPIvROKzExIlMD0uy4UQJvpfxONTzLOauQdCuGd4JNcgMSIQ7y6l1AQl3h9VRGSclfI1QWYkzTOmyZF2lAgE4o8QSK6eHouQaFI+Lm1IQFHOp4wQ8G4+K5BH2Ln4beWp1A4z3iIdQUxMzs4Cec9qwUQDRzfTIiXinu/XM8Zwsh4vqNBrFp4spHjGdmnH+A2xKhCUOHJ7Xz3I2X0Apn0IDZX1+jYzNCtcyT25kkpQ0wENIMkt1BgXYLu/woQD/xSOofvhL8a3wYQi2axG14Wbs9GEIMbHT0QObstEq1Q6m8QmThD5TxXN52iH/OODFH9QMdajm7pxdOkShBRHf2xFmyw0zNSh0hqaJAzhI3fRA3dfahBFA10s7+gWXj5njMggil6DnLw3UQS5XcKX1ZDIfsHbARdfjivxdFDO4UcQoaRZzi8RX8G9Ex/DciKOWuRCnzJxQ79pwkbjEvUPVxELTAabCzQIt4F65P/YTij37gnyVPoX1tHsDIRXc0u0IWcL5WHmRhlDFmCDT8RXRadw4rMc/rXfub+c0s63n6xd2bfMURBGK+ZYOLYxSCI7SD2XSwhlhBCbIkIIZuJbBhLIhJLYokEsdP37/Wka3L13Jnur26bQ/+eHM7JQ/V1q+qrr25W8bvEJX7zWqTk5GRG4KbrBH+Z5VFDkYXEZs5uTwKWHbH/eDKciwOxYHLiwkxJKCIfQXGhj6TIp35WVPIaOLKM79D8i7OKKK/lyYwAymRRkjX0/340bCbOynppvsf3Mn+Swn0suECo8G17SPDOcDlF5GAuH9+E32Y6KspycsCnahtfHemQftjNiSpOuTkedRS+2B7zoywmLkbGhqDC+uqEj7Ih9q/9PlRL3OHjW0G5YBV9DUX4XOtZTjnYyoc3knnzZEcsb+VhcXR4keO7O9c9EtW8/tn+0+VOnr6cxRQRoHXba5wu89L7EvJN7WydasKoDvATx4fSr1+nh8bPU3gs4cHk/bz0tWQR+eaN0ukjjM68f+L59Kue9QPHKj9UOS4fJkY/v0tTGCTzUXCLFiGup+/qDyYJo1XpfCUv3rVxVHVSx8rekm3WsiUq+7ncwpPlOPmmT+m0Esak0nlDnpQ5ZiZ6HpBV4uxb2ErZ2APtZHQrnU7CqFc6j/yFl0mtHyeb8LU6k7Jw301ssS/kn29K5zFhNJt/oh5eM6kX68geG2LuTP4KebMRUyLfK51awnimdOr9h5dpe0X2YGHnJXlTilRlVK10Dt0ijGGlMz1IeJkBe9XaKZ5a5DI3XCwi//QqnXbCGFE6TwkLr1M5RJYo4uS2krzYiy0bdimdLsKYrXT60fA6qRayxHXztkWi2E1sCygA7UqnlzAuKZ1BOLxOja34LnCT29GEsbHbTQE4d0jpoC1xh9I5mzW8BRDfxcbSd76mufvkrNKZBhZC5xqVRuM5ILwc3zGywj6TrhN374ZlJRSAm0pnHjEyH6yDgPAyH+00ySWuabc4YSgsZgqpL93EyHywS1B4mQnz/yu89N1COmtA41OruFzWr3RmY+FlrpENKrK7duOzXMtpINfedOUhbmE8VTojYHgZK9dvyTL3dohnrRvWCKkvbwjjltJ5T2LhbbPSvs3MWjs0uf9ySkh9eSQulz2Dw8u8IAuc5Ad4s+kNs+KA+mJXLmsWDG/NEMlz0L1hk9qOFup8GhaXyx4pnXo8vMxzYizUDic03x62+z4Shlw2CYVX5x7Jc5jdfFlclEeE1JenhPHVPFrCw3uM5FngrZjH3ZJis5T60i8ul3XmCG8hFGdJTmGeM8xdUurLoLhcdjNneFN1bW11qRonPx6SPGc83Wa7tL/1y7pGD3ELo8NPruypfP7iVfp3Ldsw1jNQ93du362eb2ic5jONqi8sbkGsmwbmynufakItHvRbtjzjL+eA2xSDHuIWxll8tNRwrcoxUiWvnHGNsIoP6kqeckqpL5cIY1BitDSWCl3Z4Wn7AcPfwerLbFQuExktpc3xTckrD3xSNxlOtD9uHbIjl+GjpdfmC/guyWC+Z4vBV3Nqlc4wMcAHw0dLZWHXZuXuQNPdDUB/O+NjD3ELo1ZstDThmJAXdrjG3fGHe3KJlPrSTBiPxUZL94zlw3pipN06l/Vf5hRj9xnom65H5TK50dKAY6BOfOp2xbU7NOmbAUkKxA9l8IJJfbAfFJCGq46BFpKmVE9kxVPmQIXgm/4uOVq65hgYJUZ4IlTMKhr0sku3uFzWJzlaajAVZ1cfECNspP7CU0wks83zELcwukVHS6Ohlr6nNLfDRlBKp2nictk80dFSGlDVEUl9k35ZBKJaXC4jUC7TeWjSdRpIFj2VJcGerVdcLqsWHi3ddQyUkTDlUyY/CbdQu02B6BKXy3qFc+UDU21WScLsdJdYiojoROZ0szB8013So6XRMIcWN6bksoX6IAj3TY8QRrt0rhwLU/XlweXKKYrDCUB9MXjB8A+G58q2EBvjHaw6ZK5cxOKADdeuXNZBIJ9DbIzjMV4ByjD2rpbaWm+24JsGeVsVYmO8P9PmWw7WZa3iclmn2GiJeRhiY8wR1WIdgMkwnhkYIZB1dSE2xnwfaDdFAOpDeGZgmFDuhmY307LZAs5zQjbcTgu+aZRKh7HeGK/NKHy3sWFdyDd906ZvGq977TfGXPhuz/xzgfimvZ4ZADnmMPYb48wTW8EnuWB90+hoaagKcPMhkmRFhi+9BLDhWn1moJVAPoW76LbUDekNTnOrhGy4XahcJp4rx1kwC6cxnuMWYzyFvyhkw+0NzTeNd8RMi5W2rYlnFcuBrXXbzwxAnP/gMKE0xpt5XrHYfTSyUJ8ZOATKZT0OE05jPNeNKRvT9xTqMwPtwi2F/cZ4D1vUl0OSQ7/tZwbwXNkC7GGhosMKHmTulbHhNk7DaJTOldxShNYYz+dhZikPMmEbrjx4rkw7eVJmYZhZSqt5iRtWX+Th0RJukAyvMW5iRfICsjDYrOzzjRjQYGYmLW+hvsCekuuwDVcePFeuB16AwQXfYlqGqOnDyj59UEvxi71r7WkiCqK7NNj4fpuIIqL1lfiIxkdIFRvRRgiKWlMJWsFgoGKLLdJatBVbeVTEKtj29xo/2AHL3r13Zu7SVc5HTPwwLPfemXPmnMWKNHp97CLJFsMkqE9/Vh3AMnNLob8xBlGkSSrvWFU/PhP1DQqY5i8v6XAYrbKD15InX1FBOMF+OJCutp1V/UjR6HclxPivNniY0bbW+UGnluI0gxL6w+wapa3oqrKDl1rKVtTQGeJuKyhNcbqqH0M05ZMiWrmbYspIZ6SqHyMGZeNKFX7ukQ5lIDlU1Y80QZM+XFHGOPdAEsbp/9q4DGMP1c89TgcyiC7Dfd5Mwxo7Rj6Kak8dvbxk0CWjHe1glmJnzMc4qaViBYMIq5tZu/EETcS/1LqMCeMydiOHoObGGHqJu9DAbafKcOnLmEuMuumCde8b190Yg4wEL4L6WuVmzI0vjJY8WUFvJjqVY7wiKJDw7aYelEPsy5hTOlR7USOrtzG+tULCdxYrQB1wQF02oUO1FzKiehtjEKDuwcunlxxQlw1oUO3NG8YsojHGyafx4v8v/OoyPt10rmKJko1wx88p/kevrnTpU5cBmjW0FHE7gnOcc3XF6MMNHV7xq8v4bAZiNn1ZQWtjDItX+LXBBw6oy95qUO1NGr+xoLExXl3RXbil1yEHQmyeaVDt5e21OxG2pVfEyjYclNpdOXv4VXvBe/Yc5zTbyjbacKCHP8SGzWZg3N5x2reorzEGw4ErdXYZ+INyomFsBkR/+UUZZV+Myy4Dbfbykj3EJsVlMzAXlPgwIwjGGGX24t2OITP5bQYmuLrAkvhskDgdOkNMVkVYo61mJ1w5P/Kr9vJwguhqjIFeO7T6FbwVr5umh9j85NJNR+XM0iO6GGPP1tV92mnESHLAAVfOD/yLQN9W9M0LmhpjuMvOoC06PzhABA3wq/bisovG/XwWnTcQBrNTDrhyPmJX7fmlPR56OQxmT4KnpFqE7kdNrpx0S564/I0lJOpn2OyRgZY/iLcZ4HflfM5uZxiek7chmOQz934C1vTocdmDxrAZCAXlS1YQSqmxgSxvII4UHazwwgFXzhS7ai+ushlQZAtWgBnERbRuuovdlbObu6VIKi1lBcixIO/+/OgmSM6k0KPHlZOum25V+h7HNUipoWe7CfmvEMmE1E0vNYbNgKLRqR8RFI2IZDoDgWKuthkoKpYrJ8w5JwaKfbIIGcPwCmPsrpxp5vil4JzqdsAMOQ6v/otG6qaX2V05u5hbiknlxawsMcwRG0XazU4ENbOIJrLKRnDRivB7p0SR7kAH6aYkiSDKfHMn8yLQvDSxAchRiKDja8brXkTxCil2V85R5paiiHCCWbjHFAMN3/R+FK/Q3QDxbIlh4asMsyEQxQ8ctjZhI/iXnXDl/MGh2gOUrNq8sPBt5sNH8Fuwm8cQvAKVCPohvivpi0DBEGLChhg8PLVg3d+VYb3N+Xi2bQx3ZQynu4mI97AUT98twKsJDg1VXiHdAPFswkTXglEHoNz4Hg8dVlcYzHzLd1wZzyYc3s6jvX3DSjHyt+EBZvlga2+AeDZ10UQAK2oq2Fg8+BDLrvXtQ1ONcGvpcGE8W6YT+X6FKRs9yXhvS41ma7Jcki/fdjqeLU2/K79hKwRDdfrj92rZ2rwB1A6bVXmFbesezzYXVn2VATK2W/I+2XeDYMnKWzsd2va6Lp5N2FJkqf6+/ojcMmZb7WzwCiw0ylfdFs8mtu8u0H0f/LlIJlPI57KDgoPmCpwNwmHPJbfFs8VIcryQiuWOoLwHhGNHb43QNG+5LJ7NT5OaJ1nKu9uskZietf79cq36m9wVzyZsbBft+9ooS3mPl0GGLmbh+jzWvEIDxrMFiO/WxDBDeb3X7Pjgg6DWcTCerZl6V4rlCrOIVzOmvHfKdiNz4OO3uCmebZLMR2YYynsJGHgr9VmNMDa/y+umU+sczzYXpLPpSXJ5T5q1hZXH9iOJTe6JZzvBsCSRJ5d3k0TTC4zm/aZ1jGf7ilDtEQcGSWJ5O25KcO2efcAJuSWeLVoRbwLJoUAs7/Va4fZ5ZPYCDnocimfbRrwrB3k2UAKk8nr2S+2nnNwObzNJ3XRaWzwb/dTszMi/7oKU8sKrrO2NIcDlsnBF06chno12Vya5JNCtlPKeEk5zAK/N8h+cE8hw6R6aIHQnmc3F+Qwhk/jygpuWeUFS4Vdul+IVXqyvbnqab3U1E0aXd7PcsHGVu4P5WqCb1kgEjeAWgegapnynTHnFf/JHDBscEr2P3ztABL3iUu0NJzREtJwQdmOH4Ye2M3fzvEA3rY8I6sKq9uh7wf2o8p43bXkegPcoDHbseYUpsoEORTfdiniVEet7QsRgHvXIyNAsHw9jO//GKFXiUPc/KhzmvkH2xbRYWLm850wltxzvvhX3YEOjqMGmezagWt4D0A97JR0mYe7eyJhn3JsCFALWLwh/KWTB8cCWq8Lne8r4HzGbSwbrf12BUj4k2MKEMY0dHpZXXIX/JxIzrZMBf+/C8OKgP5ktxeIJwUNLVCvx/Kevw9iAAHv7gGLzKGjRgLbYgNiqF5SPsvBA69Zyw9iAJXa3STVsguuw3diAyDcHpg0onbXZ2I+zX+zcy2riUBgH8O8koCYS8X7HK2jVWq2jYwsdpKLUVUtBGErXXXXR5Wz7Bt38H3iYYZqcxDhtNdHcfi+gHPTLdzs5qgpU0q6/+m6YAqbCGS2GJuhLmlCdUMBUaPcMIDXFu+qAAiYG1T3y13uo2iIFNoixfaovsQzVFQU2PGKv3sGzgneRFgUMfkTxTjnb741ckClgMILqgXYhFKBqUGBbaCgItJMKw7tikD3oDIp4x5K0ozFU5aC44AglqIa0q/wdVE0KmD2Wpnna2QIqVqHAP0kG1ZL2UIfqLk+Bv/I1qOK0j3w6yM6MxBFU6RTt5YIFvZ3tnRzlwsJxB+tRgHLMylGZ2IYqMiffS8ygugnT3uZRqEq+HxzzGW/0F1ngHpo4+Vwcli+BjKG5Jl97heaJrCHcQMWW5GP8Y+1GIIu8zaAq9sm3zqJQ3bbIMkkFqplv04fELfg+mYU60GR8Wh3nu9C8kqUkaGIC+VA4C803slaqC03ch6NjsQ5NKUUWa9WgGZPvDKG5eyOVHY9NrMhnzqGJnpENegp8u/f7AA3LkS0a4HTIR67BeSSbnEPDfLS6cwUcYugoSr4830cGjSSSbcIy/Bd/O+BkBbKRkAV8NpzvgBO7JFtdxsD5SZ53Ds5NimyWL4Mz9Hj9Jg7BKeXJdusMOHFPL0eF6+Bk1nQArTQ4WQ/3z/JZcAotOoiXLjiFX+RRiQw4mRYdyLoEzsyjN9/6t+CU1nQw+TY41QV50LIITnlNB5SKgcM8OD9+ZeBkU3RQggxe3WP7JcIYvJFABxaWwCslyEPmJfDqYTq8EHgRDy1A9CLgrUQ6hoYCDmt6pYLrMDiiM9iLgid7osJYj8CL9uhonmvg3XpgATh5B970jI6oVQKPrVy+AhEOMfC6P+ioUhJ0us/kYoMSnBburhXwii4uMSZR8JgjxrXJGXRkl96f/zGCzq1DbvK12tCJdtyYojWi0Ck75hVjwgp6bdfd8P6Vhd7QSXMCQ9RCNeSkb/chIaRAJ+Kwl2fO29DruqgLXMlAr+24DorYUaAnO+47mnuLM+goTSf+9foF6BWbKXK8y1AVemmHZAxGqSEM7hpOzyEWNRhIxy8ltllMYdB2dAiu3MBg6ui+an7IYJB17E2tvgwDFl+Ts1UyMMqekQM9SwwGhSQ5nppDapjsuAMebB6usnLHwPCsDCMmOSpE9CUGo7Z7en2LGjaUJw7JIsRcFnBfjmOaTfIK1w749wmTDDYoKxdk6DoJCZtuQy06qrfQDJskl9SXOpUSNjE5J9KRhJcjhk1lh1ZpH8qVYGLanNMRzEM1mMg45ZGwA3FSgAmWneTpoPKTLIOJwnf3Hu4fYfMDRlWepOhAhFy8CDM1V6UL5sKPNZgqSotLst3l4lsRpmpXTuw7fl34ewnmovJVgmyUuJKLMFd2eVjQuZAZtkgPFymygZBslrFNLEfe8jyuYpvi6KQikIWEysmoiG2qY9dNWj/h5SSN7ZT2+fLFko9ZnrcVbJc+eSFvEk8lBf9TkE6Wc9qVOF+GpAL+R5FOPRRyN61fM/hAtD18PB0I9AXCoPc4vIniA5lXp3fLLVAZzvAJ03b9odHrz1O0VWre7zUe6u0pPmH25Nbi9+tBYhzBpynTbuybNB4+NZuhTifUbD4Nx9K3WHeq4NMiY28HBSPxYhjBgUTiOW8UEF8inK4ysF3m/NTlK917SDSkCGxTzHa8mOF+tbqKVWG5aqyZ9O/PVk+46MgzWGYmv14ER2swuP8Zi2BPkdjPe98HhN/DDdSEHAVkyCoreEU52cSH4qQZ/YGEnoumgZgII3EJVoZTWUlIcES1a6kC2G31dMyi2cIVjWRktERE7Dm4gV1mDnsRES0ZGSPFcLZoMx0920HdpAUAAdszFN6tZ9gAAAAASUVORK5CYII=" (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 --------------------------------------------------------------------------------