├── .gitattributes ├── .gitignore ├── CHANGES.md ├── README.md ├── bsconfig.json ├── package.json ├── src ├── Common.re ├── V1.re └── V2.re └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | # Tell github that .re and .rei files are Reason 2 | *.re linguist-language=Reason 3 | *.rei linguist-language=Reason 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/**/** 3 | finalOutput/*.js 4 | .merlin 5 | .install 6 | /lib/ 7 | *.log 8 | .bsb.lock 9 | _esy 10 | _build 11 | *.install 12 | build/** 13 | *.bs.js 14 | 15 | # Editor 16 | /.idea/ -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # 0.2.0 2 | 3 | _Released on 2020-06-23_ 4 | 5 | This is a breaking release to solve issue #1 : 6 | - BREAKING CHANGE: `V2.Response.t` is no longer polymorphic 7 | - BREAKING CHANGE: `statusCode` is mandatory in `V2.Response.make` (otherwise the response was not correctly 8 | interpreted by AWS HttpApi gateway when `body` was the only field present in the object) 9 | - added `V2.Response.fromBody` to create a response from a JSON compliant body. 10 | 11 | 12 | # 0.1.0 13 | 14 | _Released on 2020-05-25_ 15 | 16 | Initial version -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reason-Lambda 2 | 3 | `reason-lambda` is a set of bindings for AWS Lambda handlers. 4 | It makes use of Bucklescript 7 representation of records as JS objects. 5 | It includes bindings for both [v1 and v2 of lambda payloads](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html). 6 | 7 | ## Install 8 | ``` 9 | yarn add @tsnobip/reason-lambda 10 | ``` 11 | And add `@tsnobip/reason-lambda` to the `bs-dependencies` of `bsconfig.json`. 12 | 13 | ## Usage 14 | 15 | ```reason 16 | open ReasonLambda.V2; 17 | type greetings = {greetings: string}; 18 | let handler: handler = 19 | (_event, _context) => 20 | {greetings: "Hello world!"}->Response.fromBody->Js.Promise.resolve; 21 | ``` 22 | 23 | You can see a working example with the Serverless framework [here](https://github.com/tsnobip/reason-sls-example). -------------------------------------------------------------------------------- /bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tsnobip/reason-lambda", 3 | "version": "0.2.0", 4 | "sources": { 5 | "dir": "src", 6 | "subdirs": true 7 | }, 8 | "package-specs": { 9 | "module": "commonjs", 10 | "in-source": true 11 | }, 12 | "namespace": "reason-lambda", 13 | "suffix": ".bs.js", 14 | "bs-dependencies": [], 15 | "warnings": { 16 | "error": "+101" 17 | }, 18 | "refmt": 3 19 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tsnobip/reason-lambda", 3 | "version": "0.2.0", 4 | "description": "Bucklescript bindings for AWS lambda handlers (v1 and v2)", 5 | "repository": "git+https://github.com/tsnobip/reason-lambda.git", 6 | "keywords": [ 7 | "aws", 8 | "amazon", 9 | "lambda", 10 | "FaaS", 11 | "serverless", 12 | "Bucklescript", 13 | "bindings", 14 | "ocaml", 15 | "reasonml", 16 | "reason" 17 | ], 18 | "scripts": { 19 | "clean": "bsb -clean-world", 20 | "build": "bsb -make-world", 21 | "watch": "bsb -make-world -w" 22 | }, 23 | "author": "Paul Tsnobiladzé ", 24 | "license": "MIT", 25 | "devDependencies": { 26 | "bs-platform": "^7.3.2" 27 | }, 28 | "files": [ 29 | "bsconfig.json", 30 | "src" 31 | ] 32 | } -------------------------------------------------------------------------------- /src/Common.re: -------------------------------------------------------------------------------- 1 | module Context = { 2 | type t = { 3 | callbackWaitsForEmptyEventLoop: bool, 4 | functionVersion: string, 5 | functionName: string, 6 | memoryLimitInMB: string, 7 | logGroupName: string, 8 | logStreamName: string, 9 | invokedFunctionArn: string, 10 | awsRequestId: string, 11 | }; 12 | }; -------------------------------------------------------------------------------- /src/V1.re: -------------------------------------------------------------------------------- 1 | /** 2 | Type definitions for AWS Lambda v1 Payload 3 | http://docs.aws.amazon.com/lambda 4 | https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html 5 | */ 6 | module Response = { 7 | type t; 8 | 9 | [@bs.obj] 10 | external make: 11 | ( 12 | ~isBase64Encoded: bool=?, 13 | ~statusCode: int, 14 | ~headers: Js.Dict.t(string)=?, 15 | ~multiValueHeaders: Js.Dict.t(array(string))=?, 16 | ~body: string=?, 17 | unit 18 | ) => 19 | t; 20 | }; 21 | 22 | module Context = Common.Context; 23 | 24 | type httpMethod = string; 25 | 26 | module Identity = { 27 | type t = { 28 | accessKey: Js.nullable(string), 29 | accountId: Js.nullable(string), 30 | caller: Js.nullable(string), 31 | cognitoAuthenticationProvider: Js.nullable(string), 32 | cognitoAuthenticationType: Js.nullable(string), 33 | cognitoIdentityId: Js.nullable(string), 34 | cognitoIdentityPoolId: Js.nullable(string), 35 | principalOrgId: Js.nullable(string), 36 | sourceIp: string, 37 | user: Js.nullable(string), 38 | userAgent: string, 39 | userArn: Js.nullable(string), 40 | }; 41 | }; 42 | 43 | module RequestContext = { 44 | type t = { 45 | accountId: string, 46 | apiId: string, 47 | domainName: string, 48 | domainPrefix: string, 49 | extendedRequestId: string, 50 | httpMethod, 51 | identity: Identity.t, 52 | path: string, 53 | protocol: string, 54 | requestId: string, 55 | requestTime: string, 56 | requestTimeEpoch: float, 57 | resourceId: string, 58 | resourcePath: string, 59 | stage: string, 60 | }; 61 | }; 62 | 63 | module Event = { 64 | type t = { 65 | version: string, 66 | resource: string, 67 | path: string, 68 | httpMethod, 69 | headers: Js.Dict.t(string), 70 | multiValueHeaders: Js.Dict.t(array(string)), 71 | queryStringParameters: Js.nullable(Js.Dict.t(string)), 72 | multiValueQueryStringParameters: Js.nullable(Js.Dict.t(array(string))), 73 | requestContex: RequestContext.t, 74 | pathParameters: Js.nullable(Js.Dict.t(string)), 75 | stageVariables: Js.nullable(Js.Dict.t(string)), 76 | body: Js.Json.t, 77 | isBase64Encoded: bool, 78 | }; 79 | }; 80 | 81 | type handler = (Event.t, Context.t) => Js.Promise.t(Response.t); -------------------------------------------------------------------------------- /src/V2.re: -------------------------------------------------------------------------------- 1 | /** 2 | Type definitions for AWS Lambda v2 Payload 3 | http://docs.aws.amazon.com/lambda 4 | https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html 5 | */ 6 | module Response: { 7 | type t; 8 | 9 | /** 10 | * create a response from an optional stringified body 11 | * and the different available options 12 | */ 13 | [@bs.obj] 14 | external make: 15 | ( 16 | ~cookies: array(string)=?, 17 | ~isBase64Encoded: bool=?, 18 | ~statusCode: int, 19 | ~headers: Js.Dict.t(string)=?, 20 | ~body: string=?, 21 | unit 22 | ) => 23 | t; 24 | 25 | /** 26 | * create a response from a JSON compliant body 27 | */ 28 | let fromBody: 'a => t; 29 | } = { 30 | [@unboxed] 31 | type t = 32 | | Response('a): t; 33 | 34 | [@bs.obj] 35 | external make: 36 | ( 37 | ~cookies: array(string)=?, 38 | ~isBase64Encoded: bool=?, 39 | ~statusCode: int, 40 | ~headers: Js.Dict.t(string)=?, 41 | ~body: string=?, 42 | unit 43 | ) => 44 | t; 45 | 46 | let fromBody = body => Response(body); 47 | }; 48 | 49 | module Context = Common.Context; 50 | 51 | module Authorizer = { 52 | type jwt = { 53 | claims: option(Js.Dict.t(string)), 54 | scopes: option(array(string)), 55 | }; 56 | 57 | type t = {jwt}; 58 | }; 59 | 60 | module Http = { 61 | type t = { 62 | method: string, 63 | path: string, 64 | protocol: string, 65 | sourceIp: string, 66 | userAgent: string, 67 | }; 68 | }; 69 | 70 | module RequestContext = { 71 | type t = { 72 | accountId: string, 73 | apiId: string, 74 | authorizer: option(Authorizer.t), 75 | domainName: string, 76 | domainPrefix: string, 77 | http: Http.t, 78 | requestId: string, 79 | routeKey: string, 80 | stage: string, 81 | time: string, 82 | timeEpoch: float, 83 | }; 84 | }; 85 | 86 | module Event = { 87 | type t = { 88 | version: string, 89 | routeKey: string, 90 | rawPath: string, 91 | rawQueryString: string, 92 | cookies: array(string), 93 | headers: Js.Dict.t(string), 94 | queryStringParameters: option(Js.Dict.t(string)), 95 | requestContext: RequestContext.t, 96 | body: Js.Json.t, 97 | pathParameters: option(Js.Dict.t(string)), 98 | isBase64Encoded: bool, 99 | stageVariables: option(Js.Dict.t(string)), 100 | }; 101 | }; 102 | 103 | type handler = (Event.t, Context.t) => Js.Promise.t(Response.t); -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | bs-platform@^7.3.2: 6 | version "7.3.2" 7 | resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-7.3.2.tgz#301f5c9b4e8cf5713cb60ca22e145e56e793affe" 8 | integrity sha512-seJL5g4anK9la4erv+B2o2sMHQCxDF6OCRl9en3hbaUos/S3JsusQ0sPp4ORsbx5eXfHLYBwPljwKXlgpXtsgQ== 9 | --------------------------------------------------------------------------------