From a7e31acded7ed915d112b4271d2f5bc29081eddd Mon Sep 17 00:00:00 2001 From: Benjamin Clark Date: Tue, 17 Sep 2024 19:46:55 +0100 Subject: [PATCH] Add examples --- .github/workflows/commit-to-pr.yaml | 6 +- README.md | 3 +- api_gateway_stage.tf | 1 - examples/api_gateway/files/petstore.yaml | 806 +++++++++++++++++++++++ examples/api_gateway/locals.tf | 18 + examples/api_gateway/main.tf | 22 + examples/api_gateway/variables.tf | 15 + rest_api.tf | 1 - variables.tf | 2 - 9 files changed, 865 insertions(+), 9 deletions(-) create mode 100644 examples/api_gateway/files/petstore.yaml create mode 100644 examples/api_gateway/locals.tf create mode 100644 examples/api_gateway/main.tf create mode 100644 examples/api_gateway/variables.tf diff --git a/.github/workflows/commit-to-pr.yaml b/.github/workflows/commit-to-pr.yaml index 2bd9dc0..5bb9a7d 100644 --- a/.github/workflows/commit-to-pr.yaml +++ b/.github/workflows/commit-to-pr.yaml @@ -20,7 +20,7 @@ jobs: validation: strategy: matrix: - folder: ["add", "folders", "here"] + folder: ["./", "examples/api_gateway"] name: Terraform validate for ${{ matrix.folder }} runs-on: ubuntu-20.04 steps: @@ -41,7 +41,7 @@ jobs: linting: strategy: matrix: - folder: ["add", "folders", "here"] + folder: ["./", "examples/api_gateway"] name: Terraform lint for ${{ matrix.folder }} runs-on: ubuntu-20.04 steps: @@ -59,7 +59,7 @@ jobs: plan: strategy: matrix: - folder: ["add", "folders", "here"] + folder: ["examples/api_gateway"] name: Terraform plan for ${{ matrix.folder }} runs-on: ubuntu-20.04 needs: [validation, linting] diff --git a/README.md b/README.md index 5162392..9766afa 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ No modules. |------|-------------|------|---------|:--------:| | [application\_name](#input\_application\_name) | Name of the application utilising resource. | `string` | n/a | yes | | [environment](#input\_environment) | Which environment this is being instantiated in. | `string` | n/a | yes | -| [raw\_api\_gateway\_rest\_apis](#input\_raw\_api\_gateway\_rest\_apis) | Data structure
---------------
A list of dictionaries, where each dictionary has the following attributes:

REQUIRED
---------
- suffix : Suffix to use when creating the RESTAPI Gateway
- open\_api\_file\_path : Path to OpenAPI definition file
- description : A human-friendly description of the API
- tags : A dictionary of tags, required to tag Stage to ensure its protected by WAF.


OPTIONAL
---------
- template\_input : A dictionary of variable input for the OpenAPI definition file (leave blank if no template required)
- allowed\_lambdas : A list of strings, where each string is the function\_name of a lambda to allow access to.
- quota\_limit : Maximum number of requests that can be made in a given time period, defaults to 10.
- quota\_offset : Number of requests subtracted from the given limit in the initial time period, defaults to 0.
- quota\_period : Time period in which the limit applies. Valid values are "DAY", "WEEK" or "MONTH". Defaults to "DAY"
- burst\_limit : The API request burst limit, the maximum rate limit over a time ranging from one to a few seconds, depending upon whether the underlying token bucket is at its full capacity. Defaults to 5.
- rate\_limit : The API request steady-state rate limit, defaults to 10.
- api\_keys : List of strings, where each string is name of an API key to create for the API, defaults to empty list. |
list(
object({
suffix = string,
description = string,
tags = map(string),
open_api_file_path = string,
template_input = optional(map(string), {}),
quota_limit = optional(number, 10),
quota_offset = optional(number, 0),
quota_period = optional(string, "DAY"),
burst_limit = optional(number, 5),
rate_limit = optional(number, 10)
allowed_lambdas = optional(list(string), [])
api_keys = optional(list(string), [])
})
)
| n/a | yes | +| [raw\_api\_gateway\_rest\_apis](#input\_raw\_api\_gateway\_rest\_apis) | Data structure
---------------
A list of dictionaries, where each dictionary has the following attributes:

REQUIRED
---------
- suffix : Suffix to use when creating the RESTAPI Gateway
- open\_api\_file\_path : Path to OpenAPI definition file
- description : A human-friendly description of the API


OPTIONAL
---------
- template\_input : A dictionary of variable input for the OpenAPI definition file (leave blank if no template required)
- allowed\_lambdas : A list of strings, where each string is the function\_name of a lambda to allow access to.
- quota\_limit : Maximum number of requests that can be made in a given time period, defaults to 10.
- quota\_offset : Number of requests subtracted from the given limit in the initial time period, defaults to 0.
- quota\_period : Time period in which the limit applies. Valid values are "DAY", "WEEK" or "MONTH". Defaults to "DAY"
- burst\_limit : The API request burst limit, the maximum rate limit over a time ranging from one to a few seconds, depending upon whether the underlying token bucket is at its full capacity. Defaults to 5.
- rate\_limit : The API request steady-state rate limit, defaults to 10.
- api\_keys : List of strings, where each string is name of an API key to create for the API, defaults to empty list. |
list(
object({
suffix = string,
description = string,
open_api_file_path = string,
template_input = optional(map(string), {}),
quota_limit = optional(number, 10),
quota_offset = optional(number, 0),
quota_period = optional(string, "DAY"),
burst_limit = optional(number, 5),
rate_limit = optional(number, 10)
allowed_lambdas = optional(list(string), [])
api_keys = optional(list(string), [])
})
)
| n/a | yes | ## Outputs @@ -95,7 +95,6 @@ REQUIRED - suffix : Suffix to use when creating the RESTAPI Gateway - open_api_file_path : Path to OpenAPI definition file - description : A human-friendly description of the API -- tags : A dictionary of tags, required to tag Stage to ensure its protected by WAF. OPTIONAL diff --git a/api_gateway_stage.tf b/api_gateway_stage.tf index 0eb5b36..51eb574 100644 --- a/api_gateway_stage.tf +++ b/api_gateway_stage.tf @@ -4,5 +4,4 @@ resource "aws_api_gateway_stage" "stages" { deployment_id = aws_api_gateway_deployment.deployments[each.value["suffix"]].id rest_api_id = aws_api_gateway_rest_api.api_gateway[each.value["suffix"]].id stage_name = var.environment - tags = each.value["tags"] } \ No newline at end of file diff --git a/examples/api_gateway/files/petstore.yaml b/examples/api_gateway/files/petstore.yaml new file mode 100644 index 0000000..221b460 --- /dev/null +++ b/examples/api_gateway/files/petstore.yaml @@ -0,0 +1,806 @@ +openapi: 3.0.3 +info: + title: Swagger Petstore - OpenAPI 3.0 + description: |- + This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about + Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach! + You can now help us improve the API whether it's by making changes to the definition itself or to the code. + That way, with time, we can improve the API in general, and expose some of the new features in OAS3. + + _If you're looking for the Swagger 2.0/OAS 2.0 version of Petstore, then click [here](https://editor.swagger.io/?url=https://petstore.swagger.io/v2/swagger.yaml). Alternatively, you can load via the `Edit > Load Petstore OAS 2.0` menu option!_ + + Some useful links: + - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore) + - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) + termsOfService: http://swagger.io/terms/ + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + version: 1.0.11 +externalDocs: + description: Find out more about Swagger + url: http://swagger.io +servers: + - url: https://petstore3.swagger.io/api/v3 +tags: + - name: pet + description: Everything about your Pets + externalDocs: + description: Find out more + url: http://swagger.io + - name: store + description: Access to Petstore orders + externalDocs: + description: Find out more about our store + url: http://swagger.io + - name: user + description: Operations about user +paths: + /pet: + put: + tags: + - pet + summary: Update an existing pet + description: Update an existing pet by Id + operationId: updatePet + requestBody: + description: Update an existent pet in the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Pet' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + '422': + description: Validation exception + security: + - petstore_auth: + - write:pets + - read:pets + post: + tags: + - pet + summary: Add a new pet to the store + description: Add a new pet to the store + operationId: addPet + requestBody: + description: Create a new pet in the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Pet' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid input + '422': + description: Validation exception + security: + - petstore_auth: + - write:pets + - read:pets + /pet/findByStatus: + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: false + explode: true + schema: + type: string + default: available + enum: + - available + - pending + - sold + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid status value + security: + - petstore_auth: + - write:pets + - read:pets + /pet/findByTags: + get: + tags: + - pet + summary: Finds Pets by tags + description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + operationId: findPetsByTags + parameters: + - name: tags + in: query + description: Tags to filter by + required: false + explode: true + schema: + type: array + items: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid tag value + security: + - petstore_auth: + - write:pets + - read:pets + /pet/{petId}: + get: + tags: + - pet + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + security: + - api_key: [] + - petstore_auth: + - write:pets + - read:pets + post: + tags: + - pet + summary: Updates a pet in the store with form data + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + format: int64 + - name: name + in: query + description: Name of pet that needs to be updated + schema: + type: string + - name: status + in: query + description: Status of pet that needs to be updated + schema: + type: string + responses: + '400': + description: Invalid input + security: + - petstore_auth: + - write:pets + - read:pets + delete: + tags: + - pet + summary: Deletes a pet + description: delete a pet + operationId: deletePet + parameters: + - name: api_key + in: header + description: '' + required: false + schema: + type: string + - name: petId + in: path + description: Pet id to delete + required: true + schema: + type: integer + format: int64 + responses: + '400': + description: Invalid pet value + security: + - petstore_auth: + - write:pets + - read:pets + /pet/{petId}/uploadImage: + post: + tags: + - pet + summary: uploads an image + description: '' + operationId: uploadFile + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + schema: + type: integer + format: int64 + - name: additionalMetadata + in: query + description: Additional Metadata + required: false + schema: + type: string + requestBody: + content: + application/octet-stream: + schema: + type: string + format: binary + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + security: + - petstore_auth: + - write:pets + - read:pets + /store/inventory: + get: + tags: + - store + summary: Returns pet inventories by status + description: Returns a map of status codes to quantities + operationId: getInventory + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + additionalProperties: + type: integer + format: int32 + security: + - api_key: [] + /store/order: + post: + tags: + - store + summary: Place an order for a pet + description: Place a new order in the store + operationId: placeOrder + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Order' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Invalid input + '422': + description: Validation exception + /store/order/{orderId}: + get: + tags: + - store + summary: Find purchase order by ID + description: For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions. + operationId: getOrderById + parameters: + - name: orderId + in: path + description: ID of order that needs to be fetched + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + application/xml: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Invalid ID supplied + '404': + description: Order not found + delete: + tags: + - store + summary: Delete purchase order by ID + description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + operationId: deleteOrder + parameters: + - name: orderId + in: path + description: ID of the order that needs to be deleted + required: true + schema: + type: integer + format: int64 + responses: + '400': + description: Invalid ID supplied + '404': + description: Order not found + /user: + post: + tags: + - user + summary: Create user + description: This can only be done by the logged in user. + operationId: createUser + requestBody: + description: Created user object + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/User' + responses: + default: + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + /user/createWithList: + post: + tags: + - user + summary: Creates list of users with given input array + description: Creates list of users with given input array + operationId: createUsersWithListInput + requestBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + default: + description: successful operation + /user/login: + get: + tags: + - user + summary: Logs user into the system + description: '' + operationId: loginUser + parameters: + - name: username + in: query + description: The user name for login + required: false + schema: + type: string + - name: password + in: query + description: The password for login in clear text + required: false + schema: + type: string + responses: + '200': + description: successful operation + headers: + X-Rate-Limit: + description: calls per hour allowed by the user + schema: + type: integer + format: int32 + X-Expires-After: + description: date in UTC when token expires + schema: + type: string + format: date-time + content: + application/xml: + schema: + type: string + application/json: + schema: + type: string + '400': + description: Invalid username/password supplied + /user/logout: + get: + tags: + - user + summary: Logs out current logged in user session + description: '' + operationId: logoutUser + parameters: [] + responses: + default: + description: successful operation + /user/{username}: + get: + tags: + - user + summary: Get user by user name + description: '' + operationId: getUserByName + parameters: + - name: username + in: path + description: 'The name that needs to be fetched. Use user1 for testing. ' + required: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + '400': + description: Invalid username supplied + '404': + description: User not found + put: + tags: + - user + summary: Update user + description: This can only be done by the logged in user. + operationId: updateUser + parameters: + - name: username + in: path + description: name that need to be deleted + required: true + schema: + type: string + requestBody: + description: Update an existent user in the store + content: + application/json: + schema: + $ref: '#/components/schemas/User' + application/xml: + schema: + $ref: '#/components/schemas/User' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/User' + responses: + default: + description: successful operation + delete: + tags: + - user + summary: Delete user + description: This can only be done by the logged in user. + operationId: deleteUser + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid username supplied + '404': + description: User not found +components: + schemas: + Order: + type: object + properties: + id: + type: integer + format: int64 + example: 10 + petId: + type: integer + format: int64 + example: 198772 + quantity: + type: integer + format: int32 + example: 7 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + example: approved + enum: + - placed + - approved + - delivered + complete: + type: boolean + xml: + name: order + Customer: + type: object + properties: + id: + type: integer + format: int64 + example: 100000 + username: + type: string + example: fehguy + address: + type: array + xml: + name: addresses + wrapped: true + items: + $ref: '#/components/schemas/Address' + xml: + name: customer + Address: + type: object + properties: + street: + type: string + example: 437 Lytton + city: + type: string + example: Palo Alto + state: + type: string + example: CA + zip: + type: string + example: '94301' + xml: + name: address + Category: + type: object + properties: + id: + type: integer + format: int64 + example: 1 + name: + type: string + example: Dogs + xml: + name: category + User: + type: object + properties: + id: + type: integer + format: int64 + example: 10 + username: + type: string + example: theUser + firstName: + type: string + example: John + lastName: + type: string + example: James + email: + type: string + example: john@email.com + password: + type: string + example: '12345' + phone: + type: string + example: '12345' + userStatus: + type: integer + description: User Status + format: int32 + example: 1 + xml: + name: user + Tag: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: tag + Pet: + required: + - name + - photoUrls + type: object + properties: + id: + type: integer + format: int64 + example: 10 + name: + type: string + example: doggie + category: + $ref: '#/components/schemas/Category' + photoUrls: + type: array + xml: + wrapped: true + items: + type: string + xml: + name: photoUrl + tags: + type: array + xml: + wrapped: true + items: + $ref: '#/components/schemas/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: pet + ApiResponse: + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string + xml: + name: '##default' + requestBodies: + Pet: + description: Pet object that needs to be added to the store + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + UserArray: + description: List of user object + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: https://petstore3.swagger.io/oauth/authorize + scopes: + write:pets: modify pets in your account + read:pets: read your pets + api_key: + type: apiKey + name: api_key + in: header \ No newline at end of file diff --git a/examples/api_gateway/locals.tf b/examples/api_gateway/locals.tf new file mode 100644 index 0000000..2118e17 --- /dev/null +++ b/examples/api_gateway/locals.tf @@ -0,0 +1,18 @@ +locals { + raw_restapi_gateways = [ + { + suffix = "demoapi" + open_api_file_path : "${path.module}/files/petstore.yaml", + description = "DemoAPI for example purposes, utilising a remote OpenAPI definition file." + allowed_lambdas = [ + "auth-lambda", + "backend-lambda" + ] + api_keys = [ + "user1", + "user2", + "user3" + ] + } + ] +} \ No newline at end of file diff --git a/examples/api_gateway/main.tf b/examples/api_gateway/main.tf new file mode 100644 index 0000000..8b6f9e1 --- /dev/null +++ b/examples/api_gateway/main.tf @@ -0,0 +1,22 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.61.0" + } + } + required_version = "~> 1.5.0" +} + +provider "aws" { + region = "eu-west-2" +} + +module "restapi_gateway" { + source = "github.com/sudoblark/sudoblark.terraform.module.aws.api_gateway?ref=1.0.0" + + application_name = var.application_name + environment = var.environment + raw_api_gateway_rest_apis = local.raw_restapi_gateways + +} \ No newline at end of file diff --git a/examples/api_gateway/variables.tf b/examples/api_gateway/variables.tf new file mode 100644 index 0000000..9ccf4b8 --- /dev/null +++ b/examples/api_gateway/variables.tf @@ -0,0 +1,15 @@ +variable "environment" { + description = "Which environment this is being instantiated in." + type = string + validation { + condition = contains(["dev", "test", "prod"], var.environment) + error_message = "Must be either dev, test or prod" + } + default = "prod" +} + +variable "application_name" { + description = "Name of the application utilising the resource resource." + type = string + default = "demo-app" +} \ No newline at end of file diff --git a/rest_api.tf b/rest_api.tf index aec92d2..9b796a2 100644 --- a/rest_api.tf +++ b/rest_api.tf @@ -13,7 +13,6 @@ resource "aws_api_gateway_rest_api" "api_gateway" { name = format(lower("aws-${var.environment}-${var.application_name}-%s"), each.value["suffix"]) description = format("%s- managed by Terraform", each.value["description"]) body = each.value["open_api_definition"] - tags = each.value["tags"] lifecycle { create_before_destroy = true diff --git a/variables.tf b/variables.tf index b4a8950..76e5807 100644 --- a/variables.tf +++ b/variables.tf @@ -24,7 +24,6 @@ REQUIRED - suffix : Suffix to use when creating the RESTAPI Gateway - open_api_file_path : Path to OpenAPI definition file - description : A human-friendly description of the API -- tags : A dictionary of tags, required to tag Stage to ensure its protected by WAF. OPTIONAL @@ -43,7 +42,6 @@ EOF object({ suffix = string, description = string, - tags = map(string), open_api_file_path = string, template_input = optional(map(string), {}), quota_limit = optional(number, 10),