From 101b0b059b7d8e51e7b6da2b73cef205866a0c7b Mon Sep 17 00:00:00 2001 From: Jesse McDonald Date: Sun, 25 Sep 2022 03:27:27 +0000 Subject: [PATCH] Add missing REST API template. --- README.md | 57 +--------- sample_swagger2_api.json | 239 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+), 52 deletions(-) create mode 100644 sample_swagger2_api.json diff --git a/README.md b/README.md index 250c128..b9e3920 100644 --- a/README.md +++ b/README.md @@ -78,61 +78,14 @@ If it works, you should see the following result: The JSON event structure above is the way API Gateway interfaces with AWS Lambda. Now you will configure API Gateway to deliver HTTP requests to the Lambda function as events in this format. Likewise, JSON responses like the above will be converted by API Gateway into HTTP responses to the client. This interface is where the bulk of the frustration lies when working with Lambda and API Gateway. -This also happens to be the most tedious part of setting up DynDNS53. - #### Configure the API 1. Sign into AWS and navigate to the API Gateway console. -1. Click "Create API" and select "New API". -1. Give your API a name (I used `DynDNS53`) and click "Create API". -1. Create a `dyndns2`-compatible resource: - 1. Select `/` in the resource list. - 1. Drop down "Actions" and select "Create Resource". - 1. Enter `nic` for the resource name, which should auto-populate the path. - 1. Click "Create Resource". - 1. Select `/nic` in the resource list and repeat the above steps, using `update` for the resource name. - 1. You should now have a `/nic/update` resource. -1. Create a `GET` method for the `/nic/update` resource: - 1. Select `/nic/update` in the resource list. - 1. Drop down "Actions" and select "Create Method". - 1. Select "GET" from the drop-down that appears, then click the grey checkmark beside it. - 1. Select "Lambda Function" for the integration type, select the region in which you created the Lambda function, and enter the name you gave your Lambda function above (recall I used `dyndns53_lambda`). - 1. Click "OK" when asked to give API Gateway permission to access your Lambda function. -1. Configure the "GET" method request (note: I'm not sure if configuring the method request is required, but I have it in my setup, so I've included it here): - 1. Select the `GET` method under `/nic/update` in the resource list and click on "Method Request". - 1. Expand "URL Query String Parameters" and add three query strings: `hostname`, `myip`, and `offline`. - 1. Expand "HTTP Request Headers" and add a single header: `Authorization`. - 1. At the top, click "Method Execution" to go back to the previous screen. -1. Configure the integration request to map the request contents into the JSON format our Lambda function expects: - 1. Select the `GET` method under `/nic/update` in the resource list and click on "Integration Request". - 1. Expand "Body Mapping Templates" and add a mapping template for content-type `application/json` (note: you have to explicitly type it in, even though it is already pre-populated in grey). - 1. Paste the contents of the `api_mapping_template` file in the template box and click "Save". - 1. If asked about request body passthrough, use the recommended setting. - 1. At the top, click "Method Execution" to go back to the previous screen. -1. Configure the method responses, which correspond to HTTP response codes: - 1. Select the `GET` method under `/nic/update` in the resource list and click on "Method Response". - 1. Expand the `200` row and change the content type to `text/plain`. - 1. Click "Add Response", enter `500`, and click the grey checkmark to create a method response type for generic server errors. - 1. Expand the `500` row and click "Add Response Model". - 1. Enter `text/plain` and select "Empty" from the drop-down, then click the grey check mark. - 1. Repeat the previous steps for the other response codes that the Lambda function may return: `400`, `401`, `403`, and `404`. - 1. At the top, click "Method Execution" to go back to the previous screen. -1. Configure the integration response to map the JSON response from the Lambda function to HTTP responses sent to the user: - 1. Select the `GET` method under `/nic/update` in the resource list and click on "Integration Response". - 1. Expand the default mapping row, then expand "Body Mapping Templates". - 1. Select `application/json` and populate the template with: - `$input.path('$.response')` - 1. Click "Save". - 1. Click "Add integration response" and populate the Lambda error regex with: - `.*"status"\s*:\s*500.*` - 1. Select `500` from the "Method response status" drop-down list. - 1. Expand "Body Mapping Templates" and add a mapping template for `application/json`. - 1. Populate the mapping template with: - `$util.parseJson($input.path('$.errorMessage')).response` - 1. Click "Save". - 1. Repeat the previous steps for the other repsonse codes that the Lambda function may return: `400`, `401`, `403`, and `404`. - 1. At the top, click "Method Execution" to go back to the previous screen. -1. Optional: create a `POST` method on the `/nic/update` resource and repeat all of the above steps so that it behaves identically to the `GET` method. This is only required for DDNS clients that use the `POST` method with the `dyndns2` protocol. +1. Click "Create API" and select "Import" under "REST API". +1. Paste the included `sample_swagger2_api.json` file, or click "Select Swagger File" to upload it. +1. Replace the two instances of `` with your AWS account number (without dashes). You can find your account number under your name in the upper-right corner. +1. Update the region names (if not `us-east-1`) and Lambda function names (if not `dyndns53_lambda`) on the same lines as ``, as necessary. +1. Click "Import". #### Deploy the API diff --git a/sample_swagger2_api.json b/sample_swagger2_api.json new file mode 100644 index 0000000..c43f48a --- /dev/null +++ b/sample_swagger2_api.json @@ -0,0 +1,239 @@ +{ + "swagger" : "2.0", + "info" : { + "version" : "2022-09-25T01:47:23Z", + "title" : "DynDNS53" + }, + "paths" : { + "/nic/update" : { + "get" : { + "consumes" : [ "application/json" ], + "produces" : [ "text/plain", "application/json" ], + "parameters" : [ { + "name" : "hostname", + "in" : "query", + "required" : false, + "type" : "string" + }, { + "name" : "myip", + "in" : "query", + "required" : false, + "type" : "string" + }, { + "name" : "offline", + "in" : "query", + "required" : false, + "type" : "string" + }, { + "name" : "Authorization", + "in" : "header", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "200 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + }, + "400" : { + "description" : "400 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + }, + "401" : { + "description" : "401 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + }, + "500" : { + "description" : "500 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + }, + "403" : { + "description" : "403 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + }, + "404" : { + "description" : "404 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + } + }, + "x-amazon-apigateway-integration" : { + "httpMethod" : "POST", + "uri" : "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1::function:dyndns53_lambda/invocations", + "responses" : { + ".*\"status\"\\s*:\\s*403.*" : { + "statusCode" : "403", + "responseTemplates" : { + "application/json" : "$util.parseJson($input.path('$.errorMessage')).response" + } + }, + ".*\"status\"\\s*:\\s*500.*" : { + "statusCode" : "500", + "responseTemplates" : { + "application/json" : "$util.parseJson($input.path('$.errorMessage')).response" + } + }, + "default" : { + "statusCode" : "200", + "responseTemplates" : { + "application/json" : "$input.path('$.response')" + } + }, + ".*\"status\"\\s*:\\s*404.*" : { + "statusCode" : "404", + "responseTemplates" : { + "application/json" : "$util.parseJson($input.path('$.errorMessage')).response" + } + }, + ".*\"status\"\\s*:\\s*401.*" : { + "statusCode" : "401", + "responseTemplates" : { + "application/json" : "$util.parseJson($input.path('$.errorMessage')).response" + } + }, + ".*\"status\"\\s*:\\s*400.*" : { + "statusCode" : "400", + "responseTemplates" : { + "application/json" : "$util.parseJson($input.path('$.errorMessage')).response" + } + } + }, + "requestTemplates" : { + "application/json" : "#set($allParams = $input.params())\n{\n\"body-json\" : $input.json('$'),\n#foreach($type in $allParams.keySet())\n #set($params = $allParams.get($type))\n\"$type\" : {\n #foreach($paramName in $params.keySet())\n \"$paramName\" : \"$util.escapeJavaScript($params.get($paramName))\"\n #if($foreach.hasNext),#end\n #end\n},\n#end\n\"context\" : {\n \"http-method\" : \"$context.httpMethod\",\n \"stage\" : \"$context.stage\",\n \"source-ip\" : \"$context.identity.sourceIp\",\n \"request-id\" : \"$context.requestId\",\n \"resource-id\" : \"$context.resourceId\",\n \"resource-path\" : \"$context.resourcePath\"\n }\n}\n" + }, + "passthroughBehavior" : "when_no_templates", + "contentHandling" : "CONVERT_TO_TEXT", + "type" : "aws" + } + }, + "post" : { + "consumes" : [ "application/json" ], + "produces" : [ "text/plain", "application/json" ], + "parameters" : [ { + "name" : "hostname", + "in" : "query", + "required" : false, + "type" : "string" + }, { + "name" : "myip", + "in" : "query", + "required" : false, + "type" : "string" + }, { + "name" : "offline", + "in" : "query", + "required" : false, + "type" : "string" + }, { + "name" : "Authorization", + "in" : "header", + "required" : false, + "type" : "string" + } ], + "responses" : { + "200" : { + "description" : "200 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + }, + "400" : { + "description" : "400 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + }, + "401" : { + "description" : "401 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + }, + "500" : { + "description" : "500 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + }, + "403" : { + "description" : "403 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + }, + "404" : { + "description" : "404 response", + "schema" : { + "$ref" : "#/definitions/Empty" + } + } + }, + "x-amazon-apigateway-integration" : { + "httpMethod" : "POST", + "uri" : "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1::function:dyndns53_lambda/invocations", + "responses" : { + ".*\"status\"\\s*:\\s*403.*" : { + "statusCode" : "403", + "responseTemplates" : { + "application/json" : "$util.parseJson($input.path('$.errorMessage')).response" + } + }, + ".*\"status\"\\s*:\\s*500.*" : { + "statusCode" : "500", + "responseTemplates" : { + "application/json" : "$util.parseJson($input.path('$.errorMessage')).response" + } + }, + "default" : { + "statusCode" : "200", + "responseTemplates" : { + "application/json" : "$input.path('$.response')" + } + }, + ".*\"status\"\\s*:\\s*404.*" : { + "statusCode" : "404", + "responseTemplates" : { + "application/json" : "$util.parseJson($input.path('$.errorMessage')).response" + } + }, + ".*\"status\"\\s*:\\s*401.*" : { + "statusCode" : "401", + "responseTemplates" : { + "application/json" : "$util.parseJson($input.path('$.errorMessage')).response" + } + }, + ".*\"status\"\\s*:\\s*400.*" : { + "statusCode" : "400", + "responseTemplates" : { + "application/json" : "$util.parseJson($input.path('$.errorMessage')).response" + } + } + }, + "requestTemplates" : { + "application/json" : "#set($allParams = $input.params())\n{\n\"body-json\" : $input.json('$'),\n#foreach($type in $allParams.keySet())\n #set($params = $allParams.get($type))\n\"$type\" : {\n #foreach($paramName in $params.keySet())\n \"$paramName\" : \"$util.escapeJavaScript($params.get($paramName))\"\n #if($foreach.hasNext),#end\n #end\n},\n#end\n\"context\" : {\n \"http-method\" : \"$context.httpMethod\",\n \"stage\" : \"$context.stage\",\n \"source-ip\" : \"$context.identity.sourceIp\",\n \"request-id\" : \"$context.requestId\",\n \"resource-id\" : \"$context.resourceId\",\n \"resource-path\" : \"$context.resourcePath\"\n }\n}\n" + }, + "passthroughBehavior" : "when_no_templates", + "contentHandling" : "CONVERT_TO_TEXT", + "type" : "aws" + } + } + } + }, + "definitions" : { + "Empty" : { + "type" : "object", + "title" : "Empty Schema" + } + } +}