From 24f1fc239302e249ba998d415228ac8eddb32524 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 18:20:35 -0600 Subject: [PATCH 001/119] Updating to add a logger for publishing --- functions/clash-bot/event-publisher/src/handler.ts | 1 + terraform/workflow/main.tf | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/functions/clash-bot/event-publisher/src/handler.ts b/functions/clash-bot/event-publisher/src/handler.ts index b06bc64..a072657 100644 --- a/functions/clash-bot/event-publisher/src/handler.ts +++ b/functions/clash-bot/event-publisher/src/handler.ts @@ -47,6 +47,7 @@ export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEven }) }; } catch (error) { + logger.error(error, "Failed to publish event."); return { statusCode: 500, body: JSON.stringify({ diff --git a/terraform/workflow/main.tf b/terraform/workflow/main.tf index 41b0c8e..a886653 100644 --- a/terraform/workflow/main.tf +++ b/terraform/workflow/main.tf @@ -51,7 +51,7 @@ module "api_gateway" { # Routes and integrations integrations = { - "$default" = { + "POST /api/v2" = { lambda_arn = aws_lambda_function.event_publisher_lambda.arn, integration_type = "AWS_PROXY" } From 0da4d3c07ca5a6c804fde6a1ddb7680e25fb6459 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 18:31:41 -0600 Subject: [PATCH 002/119] Adding in deployment to dev as part of PR --- .github/workflows/dev-release.yml | 11 +++++------ terraform/workflow/event-handler-lambda.tf | 6 +++--- terraform/workflow/event-publisher-lambda.tf | 10 +++++----- terraform/workflow/main.tf | 4 ++-- terraform/workflow/variables.tf | 5 +++++ 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index 67d0ea1..e250bc7 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -1,12 +1,10 @@ name: Build and Deploy into Development on: - push: - branches: - - main - paths-ignore: - - '**/README.md' - - '**/.gitignore' + workflow_run: + workflows: [Build - Clash Bot Workflow] + types: + - completed jobs: terraformPreReqs: @@ -195,4 +193,5 @@ jobs: TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher-${{ github.run_number }}.zip TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler-${{ github.run_number }}.zip TF_VAR_sqs_batch_size: "1" + TF_VAR_workspace: ${{ vars.WORKSPACE}} run: terraform apply -no-color -input=false --auto-approve \ No newline at end of file diff --git a/terraform/workflow/event-handler-lambda.tf b/terraform/workflow/event-handler-lambda.tf index 3c09786..70ed282 100644 --- a/terraform/workflow/event-handler-lambda.tf +++ b/terraform/workflow/event-handler-lambda.tf @@ -1,5 +1,5 @@ resource "aws_lambda_function" "event_handler_lambda" { - function_name = "clash-bot-event-handler" + function_name = "clash-bot-event-handler-${lower(var.environment)}" handler = "prod/handler.handler" runtime = "nodejs16.x" role = aws_iam_role.lambda_handler_exec.arn @@ -26,7 +26,7 @@ resource "aws_lambda_event_source_mapping" "sqs_trigger" { } resource "aws_iam_role" "lambda_handler_exec" { - name = "clash_bot_lambda_event_handler_exec_role" + name = "clash_bot_event_handler_exec_role-${lower(var.environment)}" assume_role_policy = jsonencode({ Version = "2012-10-17" @@ -70,7 +70,7 @@ data "aws_iam_policy_document" "event_handler_policy_document" { } resource "aws_iam_policy" "event_handler_policy" { - name = "ClashBotWorkflowEventHandlerPolicy" + name = "ClashBotWorkflowEventHandlerPolicy-${lower(var.environment)}" description = "Allows the event handler lambda to interact with SQS and CloudWatch Logs" policy = data.aws_iam_policy_document.event_handler_policy_document.json } \ No newline at end of file diff --git a/terraform/workflow/event-publisher-lambda.tf b/terraform/workflow/event-publisher-lambda.tf index 8958943..4728975 100644 --- a/terraform/workflow/event-publisher-lambda.tf +++ b/terraform/workflow/event-publisher-lambda.tf @@ -1,5 +1,5 @@ resource "aws_lambda_function" "event_publisher_lambda" { - function_name = "clash-bot-event-publisher" + function_name = "clash-bot-event-publisher-${lower(var.environment)}" handler = "prod/handler.handler" runtime = "nodejs16.x" role = aws_iam_role.lambda_publisher_exec.arn @@ -15,7 +15,7 @@ resource "aws_lambda_function" "event_publisher_lambda" { } resource "aws_iam_role" "lambda_publisher_exec" { - name = "clash_bot_lambda_event_publisher_exec_role" + name = "clash_bot_event_publisher_exec_role-${lower(var.environment)}" assume_role_policy = jsonencode({ Version = "2012-10-17" @@ -32,14 +32,14 @@ resource "aws_iam_role" "lambda_publisher_exec" { } resource "aws_lambda_permission" "apigw" { - statement_id = "AllowExecutionFromAPIGateway" + statement_id = "AllowExecutionFromAPIGateway-${lower(var.environment)}" action = "lambda:InvokeFunction" function_name = aws_lambda_function.event_publisher_lambda.function_name principal = "apigateway.amazonaws.com" # The /*/* portion grants access from any method on any resource # within the API Gateway "REST API". - source_arn = "${module.api_gateway.apigatewayv2_api_execution_arn}/*/$default" + source_arn = "${module.api_gateway.apigatewayv2_api_execution_arn}/*/*" } resource "aws_iam_role_policy_attachment" "lambda_publisher_exec_policy" { @@ -70,7 +70,7 @@ data "aws_iam_policy_document" "event_publisher_policy_document" { } resource "aws_iam_policy" "event_publisher_policy" { - name = "ClashBotEventPublisherPolicy" + name = "ClashBotEventPublisherPolicy-${lower(var.environment)}" description = "Allows the event publisher lambda to publish events to the event queue and log events to CloudWatch" policy = data.aws_iam_policy_document.event_publisher_policy_document.json } \ No newline at end of file diff --git a/terraform/workflow/main.tf b/terraform/workflow/main.tf index a886653..5d983cd 100644 --- a/terraform/workflow/main.tf +++ b/terraform/workflow/main.tf @@ -3,7 +3,7 @@ provider "aws" { default_tags { tags = { - Application = "ClashBot" + Application = "ClashBot-Workflow" Environment = var.environment } } @@ -14,7 +14,7 @@ terraform { organization = "ClashBot" workspaces { - name = "ClashBotWorkflow" + name = var.workspace } } } diff --git a/terraform/workflow/variables.tf b/terraform/workflow/variables.tf index 80ad18e..f6ea7c5 100644 --- a/terraform/workflow/variables.tf +++ b/terraform/workflow/variables.tf @@ -29,4 +29,9 @@ variable "sqs_batch_size" { type = number default = 1 description = "value of the batch size for the sqs trigger." +} + +variable "workspace" { + type = string + description = "The name of the workspace to use." } \ No newline at end of file From 88cf1cfe7f08649f1d10cfc9d400caa1b21c87dc Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 18:38:06 -0600 Subject: [PATCH 003/119] Dyanmically passing workspace name to use --- .github/workflows/dev-release.yml | 4 ++++ .github/workflows/pull-request.yml | 6 +++++- terraform/workflow/main.tf | 4 ---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index e250bc7..a668570 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -183,6 +183,10 @@ jobs: - name: Terraform Init id: init run: terraform init + + - name: Select Workspace + id: select_workspace + run: terraform workspace select ${{ vars.WORKSPACE }} - name: Terraform Apply id: apply diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 92de6d2..04f89cc 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -224,13 +224,17 @@ jobs: id: validate run: terraform validate -no-color + - name: Select Workspace + id: select_workspace + run: terraform workspace select ${{ vars.WORKSPACE }} + - name: Terraform Plan id: plan if: github.event_name == 'pull_request' env: TF_VAR_region: us-east-1 TF_VAR_environment: development - TF_VAR_s3_bucket_name: ${{ env.S3_BUCKET_NAME }} + TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} TF_VAR_event_publisher_artifact_path: event-publisher-${{ github.run_number }}.tar.gz TF_VAR_event_handler_artifact_path: event-handler-${{ github.run_number }}.tar.gz TF_VAR_sqs_batch_size: "1" diff --git a/terraform/workflow/main.tf b/terraform/workflow/main.tf index 5d983cd..c600431 100644 --- a/terraform/workflow/main.tf +++ b/terraform/workflow/main.tf @@ -12,10 +12,6 @@ provider "aws" { terraform { backend "remote" { organization = "ClashBot" - - workspaces { - name = var.workspace - } } } From 24287fb1868570616f52c2d688c3c61963a27fb5 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 18:41:08 -0600 Subject: [PATCH 004/119] Removing terraform backend block --- terraform/workflow/main.tf | 6 ------ 1 file changed, 6 deletions(-) diff --git a/terraform/workflow/main.tf b/terraform/workflow/main.tf index c600431..7dbbd4b 100644 --- a/terraform/workflow/main.tf +++ b/terraform/workflow/main.tf @@ -9,12 +9,6 @@ provider "aws" { } } -terraform { - backend "remote" { - organization = "ClashBot" - } -} - data "aws_acm_certificate" "issued" { domain = "clash-bot.ninja" statuses = ["ISSUED"] From 913ecec6c5c0d3262928ff32a81586d9e5b1ae77 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 18:53:31 -0600 Subject: [PATCH 005/119] Setting up state migration --- .github/workflows/dev-release.yml | 6 +- .github/workflows/pull-request.yml | 4 - .../event-handler-lambda.tf | 0 .../event-publisher-lambda.tf | 0 terraform/backup/main.tf | 71 ++++++++++++++++++ terraform/{workflow => backup}/output.tf | 0 terraform/{workflow => backup}/sqs.tf | 0 .../step-functions.lambda.tf | 0 terraform/{workflow => backup}/variables.tf | 0 terraform/workflow/main.tf | 74 ++----------------- 10 files changed, 78 insertions(+), 77 deletions(-) rename terraform/{workflow => backup}/event-handler-lambda.tf (100%) rename terraform/{workflow => backup}/event-publisher-lambda.tf (100%) create mode 100644 terraform/backup/main.tf rename terraform/{workflow => backup}/output.tf (100%) rename terraform/{workflow => backup}/sqs.tf (100%) rename terraform/{workflow => backup}/step-functions.lambda.tf (100%) rename terraform/{workflow => backup}/variables.tf (100%) diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index a668570..5390bcd 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -179,14 +179,10 @@ jobs: uses: hashicorp/setup-terraform@v1 with: cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} - + - name: Terraform Init id: init run: terraform init - - - name: Select Workspace - id: select_workspace - run: terraform workspace select ${{ vars.WORKSPACE }} - name: Terraform Apply id: apply diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 04f89cc..7ba6c5b 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -224,10 +224,6 @@ jobs: id: validate run: terraform validate -no-color - - name: Select Workspace - id: select_workspace - run: terraform workspace select ${{ vars.WORKSPACE }} - - name: Terraform Plan id: plan if: github.event_name == 'pull_request' diff --git a/terraform/workflow/event-handler-lambda.tf b/terraform/backup/event-handler-lambda.tf similarity index 100% rename from terraform/workflow/event-handler-lambda.tf rename to terraform/backup/event-handler-lambda.tf diff --git a/terraform/workflow/event-publisher-lambda.tf b/terraform/backup/event-publisher-lambda.tf similarity index 100% rename from terraform/workflow/event-publisher-lambda.tf rename to terraform/backup/event-publisher-lambda.tf diff --git a/terraform/backup/main.tf b/terraform/backup/main.tf new file mode 100644 index 0000000..7dbbd4b --- /dev/null +++ b/terraform/backup/main.tf @@ -0,0 +1,71 @@ +provider "aws" { + region = var.region + + default_tags { + tags = { + Application = "ClashBot-Workflow" + Environment = var.environment + } + } +} + +data "aws_acm_certificate" "issued" { + domain = "clash-bot.ninja" + statuses = ["ISSUED"] +} + +resource "aws_cloudwatch_log_group" "api_gateway_default_log_group" { + name = "api_gateway_default_log_group" +} + +module "api_gateway" { + source = "terraform-aws-modules/apigateway-v2/aws" + + name = "clash-bot-workflow-${var.environment}" + description = "Clash Bot Workflow API Gateway for the ${var.environment} environment" + protocol_type = "HTTP" + + cors_configuration = { + allow_headers = ["content-type", "x-amz-date", "authorization", "x-api-key", "x-amz-security-token", "x-amz-user-agent"] + allow_methods = ["*"] + allow_origins = ["*"] + } + + # Custom domain + domain_name = data.aws_acm_certificate.issued.domain + domain_name_certificate_arn = data.aws_acm_certificate.issued.arn + + # Access logs + default_stage_access_log_destination_arn = aws_cloudwatch_log_group.api_gateway_default_log_group.arn + default_stage_access_log_format = "$context.identity.sourceIp - - [$context.requestTime] \"$context.httpMethod $context.routeKey $context.protocol\" $context.status $context.responseLength $context.requestId $context.integrationErrorMessage" + + # Routes and integrations + integrations = { + "POST /api/v2" = { + lambda_arn = aws_lambda_function.event_publisher_lambda.arn, + integration_type = "AWS_PROXY" + } + } +} + +module "dynamodb_table" { + source = "terraform-aws-modules/dynamodb-table/aws" + + name = "clash-bot-workflow-${var.environment}" + hash_key = "type" + range_key = "id" + billing_mode = "PROVISIONED" + write_capacity = 5 + read_capacity = 1 + + attributes = [ + { + name = "type" + type = "S" + }, + { + name = "id" + type = "S" + } + ] +} diff --git a/terraform/workflow/output.tf b/terraform/backup/output.tf similarity index 100% rename from terraform/workflow/output.tf rename to terraform/backup/output.tf diff --git a/terraform/workflow/sqs.tf b/terraform/backup/sqs.tf similarity index 100% rename from terraform/workflow/sqs.tf rename to terraform/backup/sqs.tf diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/backup/step-functions.lambda.tf similarity index 100% rename from terraform/workflow/step-functions.lambda.tf rename to terraform/backup/step-functions.lambda.tf diff --git a/terraform/workflow/variables.tf b/terraform/backup/variables.tf similarity index 100% rename from terraform/workflow/variables.tf rename to terraform/backup/variables.tf diff --git a/terraform/workflow/main.tf b/terraform/workflow/main.tf index 7dbbd4b..11abde7 100644 --- a/terraform/workflow/main.tf +++ b/terraform/workflow/main.tf @@ -1,71 +1,9 @@ -provider "aws" { - region = var.region +terraform { + backend "remote" { + organization = "Clash Bot" - default_tags { - tags = { - Application = "ClashBot-Workflow" - Environment = var.environment + workspaces { + name = "ClashBotWorkflow" } } -} - -data "aws_acm_certificate" "issued" { - domain = "clash-bot.ninja" - statuses = ["ISSUED"] -} - -resource "aws_cloudwatch_log_group" "api_gateway_default_log_group" { - name = "api_gateway_default_log_group" -} - -module "api_gateway" { - source = "terraform-aws-modules/apigateway-v2/aws" - - name = "clash-bot-workflow-${var.environment}" - description = "Clash Bot Workflow API Gateway for the ${var.environment} environment" - protocol_type = "HTTP" - - cors_configuration = { - allow_headers = ["content-type", "x-amz-date", "authorization", "x-api-key", "x-amz-security-token", "x-amz-user-agent"] - allow_methods = ["*"] - allow_origins = ["*"] - } - - # Custom domain - domain_name = data.aws_acm_certificate.issued.domain - domain_name_certificate_arn = data.aws_acm_certificate.issued.arn - - # Access logs - default_stage_access_log_destination_arn = aws_cloudwatch_log_group.api_gateway_default_log_group.arn - default_stage_access_log_format = "$context.identity.sourceIp - - [$context.requestTime] \"$context.httpMethod $context.routeKey $context.protocol\" $context.status $context.responseLength $context.requestId $context.integrationErrorMessage" - - # Routes and integrations - integrations = { - "POST /api/v2" = { - lambda_arn = aws_lambda_function.event_publisher_lambda.arn, - integration_type = "AWS_PROXY" - } - } -} - -module "dynamodb_table" { - source = "terraform-aws-modules/dynamodb-table/aws" - - name = "clash-bot-workflow-${var.environment}" - hash_key = "type" - range_key = "id" - billing_mode = "PROVISIONED" - write_capacity = 5 - read_capacity = 1 - - attributes = [ - { - name = "type" - type = "S" - }, - { - name = "id" - type = "S" - } - ] -} +} \ No newline at end of file From 9a915c0a4a05cee0a584e9776fd40abe5e4f906d Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 18:55:39 -0600 Subject: [PATCH 006/119] Fixing org name --- terraform/workflow/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/main.tf b/terraform/workflow/main.tf index 11abde7..167cfda 100644 --- a/terraform/workflow/main.tf +++ b/terraform/workflow/main.tf @@ -1,6 +1,6 @@ terraform { backend "remote" { - organization = "Clash Bot" + organization = "ClashBot" workspaces { name = "ClashBotWorkflow" From 1eb499f2e3ba22d8e0b72413059ff2fd574206e9 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 19:26:52 -0600 Subject: [PATCH 007/119] Updating to use pull-request.yml as dev deployment --- .github/workflows/pull-request.yml | 241 +++++++++++++++-------------- 1 file changed, 126 insertions(+), 115 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 7ba6c5b..f49ac70 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -9,87 +9,11 @@ on: - '**/.gitignore' jobs: - eventPublisher: - name: Event Publisher Lambda - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./functions/clash-bot/event-publisher - - steps: - - uses: FranzDiebold/github-env-vars-action@v2.1.0 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: '16' - - - name: Checkout - uses: actions/checkout@v2 - - - name: Install depedencies - run: npm install - - - name: Test - run: npm test - - - name: Build - run: npm run build - - - name: Archive - run: | - tar -czf ./event-publisher-${{ github.run_number }}.tar.gz ./prod ./node_modules ./package.json - ls -lha - - - name: Upload Artifacts - uses: actions/upload-artifact@v2 - with: - name: event-publisher-${{ github.run_number }} - path: ./functions/clash-bot/event-publisher/event-publisher-${{ github.run_number }}.tar.gz - if-no-files-found: error - - eventHandler: - name: Event Handler Lambda - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./functions/clash-bot/event-handler - - steps: - - uses: FranzDiebold/github-env-vars-action@v2.1.0 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: '16' - - - name: Checkout - uses: actions/checkout@v2 - - - name: Install dependencies - run: npm install - - - name: Test - run: npm test - - - name: Build - run: npm run build - - - name: Archive - run: | - tar -czf ./event-handler-${{ github.run_number }}.tar.gz ./prod ./node_modules ./package.json - ls -lha - - - name: Upload Artifacts - uses: actions/upload-artifact@v2 - with: - name: event-handler-${{ github.run_number }} - path: ./functions/clash-bot/event-handler/event-handler-${{ github.run_number }}.tar.gz - if-no-files-found: error - terraformPreReqs: - name: 'Terraform Plan Prereqs' + name: Prerequisites Apply runs-on: ubuntu-latest + environment: + name: Development defaults: run: working-directory: ./terraform/prereqs @@ -127,34 +51,34 @@ jobs: id: validate run: terraform validate -no-color - - name: Terraform Plan - id: plan + - name: Apply + id: apply if: github.event_name == 'pull_request' env: TF_VAR_region: us-east-1 - TF_VAR_environment: development - TF_VAR_s3_bucket_name: ${{ env.S3_BUCKET_NAME }} - run: terraform plan -no-color -input=false + TF_VAR_environment: ${{ vars.ENVIRONMENT }} + TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} + run: terraform apply -no-color -input=false - name: Update Pull Request uses: actions/github-script@v6.1.0 if: github.event_name == 'pull_request' env: - PLAN: "terraform\n${{ steps.plan.outputs.stdout }}" + APPLY: "terraform\n${{ steps.apply.outputs.stdout }}" with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const output = `Clash Bot Workflow Prerequisites - plan + const output = `Clash Bot Workflow Prerequisites - Apply #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` - #### Terraform Prerequisite Plan 📖\`${{ steps.plan.outcome }}\` + #### Prerequisite Apply 📖\`${{ steps.apply.outcome }}\` #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` -
Show Plan +
Show Apply #### Prerequisites \`\`\`\n - ${process.env.PLAN} + ${process.env.APPLY} \`\`\`
@@ -168,9 +92,109 @@ jobs: body: output }) + eventPublisher: + name: Event Publisher Lambda Build & Deploy + runs-on: ubuntu-latest + environment: + name: Development + defaults: + run: + working-directory: ./functions/clash-bot/event-publisher + needs: + - terraformPreReqs + + steps: + - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install depedencies + run: npm install + + - name: Test + run: npm test + + - name: Build + run: npm run build + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser + aws-region: us-east-1 + + - name: Archive and Publish + run: | + zip -r ./event-publisher-${{ github.run_number }}.zip ./prod ./node_modules ./package.json + aws s3 cp ./event-publisher-${{ github.run_number }}.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip + + - name: Upload Artifacts + uses: actions/upload-artifact@v2 + with: + name: event-publisher-${{ vars.ENVIRONMENT}}-${{ github.run_number }} + path: ./functions/clash-bot/event-publisher/event-publisher-${{ github.run_number }}.tar.gz + if-no-files-found: error + + eventHandler: + name: Event Handler Lambda Build & Deploy + runs-on: ubuntu-latest + environment: + name: Development + defaults: + run: + working-directory: ./functions/clash-bot/event-handler + needs: + - terraformPreReqs + + steps: + - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install dependencies + run: npm install + + - name: Test + run: npm test + + - name: Build + run: npm run build + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser + aws-region: us-east-1 + + - name: Archive and Publish + run: | + zip -r ./event-handler-${{ github.run_number }}.zip ./prod ./node_modules ./package.json + aws s3 cp ./event-handler-${{ github.run_number }}.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip + + - name: Upload Artifacts + uses: actions/upload-artifact@v2 + with: + name: event-handler-${{ vars.ENVIRONMENT}}-${{ github.run_number }} + path: ./functions/clash-bot/event-handler/event-handler-${{ github.run_number }}.tar.gz + if-no-files-found: error + terraformWorkflow: - name: 'Terraform Plan Workflow' + name: Workflow Apply runs-on: ubuntu-latest + environment: + name: Development defaults: run: working-directory: ./terraform/workflow @@ -189,18 +213,6 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Extract Event Publisher Artifact - uses: actions/download-artifact@v2 - with: - name: event-publisher-${{ github.run_number }} - path: ./terraform/prereqs - - - name: Extract Event Handler Artifact - uses: actions/download-artifact@v2 - with: - name: event-handler-${{ github.run_number }} - path: ./terraform/prereqs - - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v3 with: @@ -224,37 +236,36 @@ jobs: id: validate run: terraform validate -no-color - - name: Terraform Plan - id: plan - if: github.event_name == 'pull_request' + - name: Apply + id: apply env: TF_VAR_region: us-east-1 - TF_VAR_environment: development + TF_VAR_environment: ${{ vars.ENVIRONMENT }} TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} - TF_VAR_event_publisher_artifact_path: event-publisher-${{ github.run_number }}.tar.gz - TF_VAR_event_handler_artifact_path: event-handler-${{ github.run_number }}.tar.gz + TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip + TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip TF_VAR_sqs_batch_size: "1" - run: terraform plan -no-color -input=false + run: terraform apply -no-color -input=false --auto-approve - name: Update Pull Request uses: actions/github-script@v6.1.0 if: github.event_name == 'pull_request' env: - PLAN: "terraform\n${{ steps.plan.outputs.stdout }}" + APPLY: "terraform\n${{ steps.apply.outputs.stdout }}" with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const output = `Clash Bot Workflow - plan + const output = `Clash Bot Workflow - Apply #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` - #### Terraform Workflow Plan 📖\`${{ steps.plan.outcome }}\` + #### Workflow Apply 📖\`${{ steps.apply.outcome }}\` #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` -
Show Plan +
Show Run - #### Prerequisites + #### Workflow Apply \`\`\`\n - ${process.env.PLAN} + ${process.env.APPLY} \`\`\`
From ad9a527a54277b81d2b404721c181b14307fb762 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 19:29:45 -0600 Subject: [PATCH 008/119] Fixing pull request permissions --- .github/workflows/pull-request.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index f49ac70..b4fcd7e 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -100,6 +100,9 @@ jobs: defaults: run: working-directory: ./functions/clash-bot/event-publisher + permissions: + id-token: write + contents: read needs: - terraformPreReqs @@ -149,6 +152,9 @@ jobs: defaults: run: working-directory: ./functions/clash-bot/event-handler + permissions: + id-token: write + contents: read needs: - terraformPreReqs @@ -198,14 +204,14 @@ jobs: defaults: run: working-directory: ./terraform/workflow - needs: - - terraformPreReqs - - eventPublisher - - eventHandler permissions: id-token: write contents: read pull-requests: write + needs: + - terraformPreReqs + - eventPublisher + - eventHandler steps: - uses: FranzDiebold/github-env-vars-action@v2.1.0 From 906feb5ba626c617daa692b7a4439fd571041ed2 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 19:38:16 -0600 Subject: [PATCH 009/119] Updating pull request to perform build and deploy as two separate steps --- .github/workflows/pull-request.yml | 87 ++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index b4fcd7e..2166fd0 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -92,11 +92,9 @@ jobs: body: output }) - eventPublisher: - name: Event Publisher Lambda Build & Deploy + eventPublisherBuild: + name: Event Publisher Lambda Build runs-on: ubuntu-latest - environment: - name: Development defaults: run: working-directory: ./functions/clash-bot/event-publisher @@ -123,6 +121,35 @@ jobs: - name: Test run: npm test + eventPublisherDeploy: + name: Event Publisher Lambda Deploy + runs-on: ubuntu-latest + environment: + name: Development + defaults: + run: + working-directory: ./functions/clash-bot/event-publisher + permissions: + id-token: write + contents: read + needs: + - terraformPreReqs + - eventPublisherBuild + + steps: + - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install depedencies + run: npm install --prod + - name: Build run: npm run build @@ -134,27 +161,24 @@ jobs: - name: Archive and Publish run: | - zip -r ./event-publisher-${{ github.run_number }}.zip ./prod ./node_modules ./package.json - aws s3 cp ./event-publisher-${{ github.run_number }}.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip + zip -r ./event-publisher.zip ./prod ./node_modules ./package.json + aws s3 cp ./event-publisher.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip - name: Upload Artifacts uses: actions/upload-artifact@v2 with: name: event-publisher-${{ vars.ENVIRONMENT}}-${{ github.run_number }} - path: ./functions/clash-bot/event-publisher/event-publisher-${{ github.run_number }}.tar.gz + path: ./event-publisher.tar.gz if-no-files-found: error - eventHandler: - name: Event Handler Lambda Build & Deploy + eventHandlerBuild: + name: Event Handler Lambda Build runs-on: ubuntu-latest environment: name: Development defaults: run: working-directory: ./functions/clash-bot/event-handler - permissions: - id-token: write - contents: read needs: - terraformPreReqs @@ -175,6 +199,35 @@ jobs: - name: Test run: npm test + eventHandlerDeploy: + name: Event Handler Lambda Deploy + runs-on: ubuntu-latest + environment: + name: Development + defaults: + run: + working-directory: ./functions/clash-bot/event-handler + permissions: + id-token: write + contents: read + needs: + - terraformPreReqs + - eventHandlerBuild + + steps: + - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install dependencies + run: npm install --production + - name: Build run: npm run build @@ -186,14 +239,14 @@ jobs: - name: Archive and Publish run: | - zip -r ./event-handler-${{ github.run_number }}.zip ./prod ./node_modules ./package.json - aws s3 cp ./event-handler-${{ github.run_number }}.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip + zip -r ./event-handler.zip ./prod ./node_modules ./package.json + aws s3 cp ./event-handler.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip - name: Upload Artifacts uses: actions/upload-artifact@v2 with: name: event-handler-${{ vars.ENVIRONMENT}}-${{ github.run_number }} - path: ./functions/clash-bot/event-handler/event-handler-${{ github.run_number }}.tar.gz + path: ./event-handler.tar.gz if-no-files-found: error terraformWorkflow: @@ -210,8 +263,8 @@ jobs: pull-requests: write needs: - terraformPreReqs - - eventPublisher - - eventHandler + - eventPublisherDeploy + - eventHandlerDeploy steps: - uses: FranzDiebold/github-env-vars-action@v2.1.0 From 3d69193f3b439ed6f9a75c0a104677a9998cc190 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 19:47:58 -0600 Subject: [PATCH 010/119] Updating packaging to optimize dist --- .github/workflows/pull-request.yml | 9 ++------- functions/clash-bot/event-handler/package.json | 3 ++- functions/clash-bot/event-publisher/package.json | 3 ++- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 2166fd0..3f22b39 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -98,9 +98,6 @@ jobs: defaults: run: working-directory: ./functions/clash-bot/event-publisher - permissions: - id-token: write - contents: read needs: - terraformPreReqs @@ -148,7 +145,7 @@ jobs: uses: actions/checkout@v2 - name: Install depedencies - run: npm install --prod + run: npm install - name: Build run: npm run build @@ -174,8 +171,6 @@ jobs: eventHandlerBuild: name: Event Handler Lambda Build runs-on: ubuntu-latest - environment: - name: Development defaults: run: working-directory: ./functions/clash-bot/event-handler @@ -226,7 +221,7 @@ jobs: uses: actions/checkout@v2 - name: Install dependencies - run: npm install --production + run: npm install - name: Build run: npm run build diff --git a/functions/clash-bot/event-handler/package.json b/functions/clash-bot/event-handler/package.json index 585231b..9cffc29 100644 --- a/functions/clash-bot/event-handler/package.json +++ b/functions/clash-bot/event-handler/package.json @@ -5,7 +5,8 @@ "main": "index.js", "scripts": { "test": "jest", - "build": "tsc" + "build": "tsc", + "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" }, "keywords": [ "clash-bot", diff --git a/functions/clash-bot/event-publisher/package.json b/functions/clash-bot/event-publisher/package.json index 475559b..e0a80ff 100644 --- a/functions/clash-bot/event-publisher/package.json +++ b/functions/clash-bot/event-publisher/package.json @@ -5,7 +5,8 @@ "main": "index.js", "scripts": { "test": "jest", - "build": "tsc" + "build": "tsc", + "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" }, "keywords": [ "clash-bot" From 1ae95a4d7fdee8a0d35edcc7c4b3fcc3c2025f97 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 19:51:26 -0600 Subject: [PATCH 011/119] Fixing artifact suffix --- .github/workflows/pull-request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 3f22b39..6018559 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -165,7 +165,7 @@ jobs: uses: actions/upload-artifact@v2 with: name: event-publisher-${{ vars.ENVIRONMENT}}-${{ github.run_number }} - path: ./event-publisher.tar.gz + path: ./event-publisher.zip if-no-files-found: error eventHandlerBuild: @@ -241,7 +241,7 @@ jobs: uses: actions/upload-artifact@v2 with: name: event-handler-${{ vars.ENVIRONMENT}}-${{ github.run_number }} - path: ./event-handler.tar.gz + path: ./event-handler.zip if-no-files-found: error terraformWorkflow: From 104ea334d5dc74354af507c2662b695aa4f1d941 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 19:54:38 -0600 Subject: [PATCH 012/119] Fixing path to artifacts --- .github/workflows/pull-request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 6018559..3c7d53b 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -165,7 +165,7 @@ jobs: uses: actions/upload-artifact@v2 with: name: event-publisher-${{ vars.ENVIRONMENT}}-${{ github.run_number }} - path: ./event-publisher.zip + path: ./functions/clash-bot/event-publisher/event-publisher.zip if-no-files-found: error eventHandlerBuild: @@ -241,7 +241,7 @@ jobs: uses: actions/upload-artifact@v2 with: name: event-handler-${{ vars.ENVIRONMENT}}-${{ github.run_number }} - path: ./event-handler.zip + path: ./functions/clash-bot/event-handler/event-handler.zip if-no-files-found: error terraformWorkflow: From 0ecbcf1f9542ffd9172047eebcf888512c6c24de Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 20:05:55 -0600 Subject: [PATCH 013/119] Moving iac back to be deployed --- terraform/backup/main.tf | 71 ------------------ terraform/backup/output.tf | 0 .../event-handler-lambda.tf | 0 .../event-publisher-lambda.tf | 0 terraform/workflow/main.tf | 74 +++++++++++++++++-- terraform/workflow/output.tf | 34 +++++++++ terraform/{backup => workflow}/sqs.tf | 0 .../step-functions.lambda.tf | 0 terraform/{backup => workflow}/variables.tf | 5 -- 9 files changed, 102 insertions(+), 82 deletions(-) delete mode 100644 terraform/backup/main.tf delete mode 100644 terraform/backup/output.tf rename terraform/{backup => workflow}/event-handler-lambda.tf (100%) rename terraform/{backup => workflow}/event-publisher-lambda.tf (100%) create mode 100644 terraform/workflow/output.tf rename terraform/{backup => workflow}/sqs.tf (100%) rename terraform/{backup => workflow}/step-functions.lambda.tf (100%) rename terraform/{backup => workflow}/variables.tf (88%) diff --git a/terraform/backup/main.tf b/terraform/backup/main.tf deleted file mode 100644 index 7dbbd4b..0000000 --- a/terraform/backup/main.tf +++ /dev/null @@ -1,71 +0,0 @@ -provider "aws" { - region = var.region - - default_tags { - tags = { - Application = "ClashBot-Workflow" - Environment = var.environment - } - } -} - -data "aws_acm_certificate" "issued" { - domain = "clash-bot.ninja" - statuses = ["ISSUED"] -} - -resource "aws_cloudwatch_log_group" "api_gateway_default_log_group" { - name = "api_gateway_default_log_group" -} - -module "api_gateway" { - source = "terraform-aws-modules/apigateway-v2/aws" - - name = "clash-bot-workflow-${var.environment}" - description = "Clash Bot Workflow API Gateway for the ${var.environment} environment" - protocol_type = "HTTP" - - cors_configuration = { - allow_headers = ["content-type", "x-amz-date", "authorization", "x-api-key", "x-amz-security-token", "x-amz-user-agent"] - allow_methods = ["*"] - allow_origins = ["*"] - } - - # Custom domain - domain_name = data.aws_acm_certificate.issued.domain - domain_name_certificate_arn = data.aws_acm_certificate.issued.arn - - # Access logs - default_stage_access_log_destination_arn = aws_cloudwatch_log_group.api_gateway_default_log_group.arn - default_stage_access_log_format = "$context.identity.sourceIp - - [$context.requestTime] \"$context.httpMethod $context.routeKey $context.protocol\" $context.status $context.responseLength $context.requestId $context.integrationErrorMessage" - - # Routes and integrations - integrations = { - "POST /api/v2" = { - lambda_arn = aws_lambda_function.event_publisher_lambda.arn, - integration_type = "AWS_PROXY" - } - } -} - -module "dynamodb_table" { - source = "terraform-aws-modules/dynamodb-table/aws" - - name = "clash-bot-workflow-${var.environment}" - hash_key = "type" - range_key = "id" - billing_mode = "PROVISIONED" - write_capacity = 5 - read_capacity = 1 - - attributes = [ - { - name = "type" - type = "S" - }, - { - name = "id" - type = "S" - } - ] -} diff --git a/terraform/backup/output.tf b/terraform/backup/output.tf deleted file mode 100644 index e69de29..0000000 diff --git a/terraform/backup/event-handler-lambda.tf b/terraform/workflow/event-handler-lambda.tf similarity index 100% rename from terraform/backup/event-handler-lambda.tf rename to terraform/workflow/event-handler-lambda.tf diff --git a/terraform/backup/event-publisher-lambda.tf b/terraform/workflow/event-publisher-lambda.tf similarity index 100% rename from terraform/backup/event-publisher-lambda.tf rename to terraform/workflow/event-publisher-lambda.tf diff --git a/terraform/workflow/main.tf b/terraform/workflow/main.tf index 167cfda..843827e 100644 --- a/terraform/workflow/main.tf +++ b/terraform/workflow/main.tf @@ -1,9 +1,71 @@ -terraform { - backend "remote" { - organization = "ClashBot" +provider "aws" { + region = var.region - workspaces { - name = "ClashBotWorkflow" + default_tags { + tags = { + Application = "ClashBot-Workflow" + Environment = var.environment } } -} \ No newline at end of file +} + +data "aws_acm_certificate" "issued" { + domain = "clash-bot.ninja" + statuses = ["ISSUED"] +} + +resource "aws_cloudwatch_log_group" "api_gateway_default_log_group" { + name = "api_gateway_default_log_group-${var.environment}" +} + +module "api_gateway" { + source = "terraform-aws-modules/apigateway-v2/aws" + + name = "clash-bot-workflow-${var.environment}" + description = "Clash Bot Workflow API Gateway for the ${var.environment} environment" + protocol_type = "HTTP" + + cors_configuration = { + allow_headers = ["content-type", "x-amz-date", "authorization", "x-api-key", "x-amz-security-token", "x-amz-user-agent"] + allow_methods = ["*"] + allow_origins = ["*"] + } + + # Custom domain + domain_name = data.aws_acm_certificate.issued.domain + domain_name_certificate_arn = data.aws_acm_certificate.issued.arn + + # Access logs + default_stage_access_log_destination_arn = aws_cloudwatch_log_group.api_gateway_default_log_group.arn + default_stage_access_log_format = "$context.identity.sourceIp - - [$context.requestTime] \"$context.httpMethod $context.routeKey $context.protocol\" $context.status $context.responseLength $context.requestId $context.integrationErrorMessage" + + # Routes and integrations + integrations = { + "POST /api/v2" = { + lambda_arn = aws_lambda_function.event_publisher_lambda.arn, + integration_type = "AWS_PROXY" + } + } +} + +module "dynamodb_table" { + source = "terraform-aws-modules/dynamodb-table/aws" + + name = "clash-bot-workflow-${var.environment}" + hash_key = "type" + range_key = "id" + billing_mode = "PROVISIONED" + write_capacity = 5 + read_capacity = 1 + + attributes = [ + { + name = "type" + type = "S" + }, + { + name = "id" + type = "S" + } + ] +} diff --git a/terraform/workflow/output.tf b/terraform/workflow/output.tf new file mode 100644 index 0000000..2dce47f --- /dev/null +++ b/terraform/workflow/output.tf @@ -0,0 +1,34 @@ +output "api-gateway-endpoint" { + value = module.api_gateway.apigatewayv2_api_endpoint + description = "The endpoint for the API Gateway." +} + +output "event-publisher-lambda-arn" { + value = aws_lambda_function.event_publisher_lambda.arn + description = "The ARN for the event publisher lambda function." +} + +output "event-handler-lambda-arn" { + value = aws_lambda_function.event_handler_lambda.arn + description = "The ARN for the event handler lambda function." +} + +output "event-publisher-lambda-version" { + value = aws_lambda_function.event_publisher_lambda.version + description = "The version for the event publisher lambda function." +} + +output "event-handler-lambda-version" { + value = aws_lambda_function.event_handler_lambda.version + description = "The version for the event handler lambda function." +} + +output "event-sqs" { + value = module.clash_bot_event_sqs.queue_arn + description = "The ARN for the event SQS queue." +} + +output "step-function-arn" { + value = module.create_team_step_function.state_machine_arn + description = "The ARN for the step function." +} \ No newline at end of file diff --git a/terraform/backup/sqs.tf b/terraform/workflow/sqs.tf similarity index 100% rename from terraform/backup/sqs.tf rename to terraform/workflow/sqs.tf diff --git a/terraform/backup/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf similarity index 100% rename from terraform/backup/step-functions.lambda.tf rename to terraform/workflow/step-functions.lambda.tf diff --git a/terraform/backup/variables.tf b/terraform/workflow/variables.tf similarity index 88% rename from terraform/backup/variables.tf rename to terraform/workflow/variables.tf index f6ea7c5..80ad18e 100644 --- a/terraform/backup/variables.tf +++ b/terraform/workflow/variables.tf @@ -29,9 +29,4 @@ variable "sqs_batch_size" { type = number default = 1 description = "value of the batch size for the sqs trigger." -} - -variable "workspace" { - type = string - description = "The name of the workspace to use." } \ No newline at end of file From a256715591fd6144fd1bfb844a1e0e752337d438 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 20:11:27 -0600 Subject: [PATCH 014/119] Adding in workspace select --- .github/workflows/pull-request.yml | 4 ++++ terraform/workflow/output.tf | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 3c7d53b..26b9ffe 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -286,6 +286,10 @@ jobs: id: init run: terraform init + - name: Select Workspace + id: workspace + run: terraform workspace select ${{ vars.WORKSPACE }} || terraform workspace new ${{ vars.WORKSPACE }} + - name: Terraform Validate id: validate run: terraform validate -no-color diff --git a/terraform/workflow/output.tf b/terraform/workflow/output.tf index 2dce47f..f501eaf 100644 --- a/terraform/workflow/output.tf +++ b/terraform/workflow/output.tf @@ -1,5 +1,5 @@ output "api-gateway-endpoint" { - value = module.api_gateway.apigatewayv2_api_endpoint + value = module.api_gateway.apigatewayv2_api_endpoint.apigatewayv2_api_api_endpoint description = "The endpoint for the API Gateway." } From cdccedeb898330ec4e139b739fc39b0e82ba605b Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 20:15:48 -0600 Subject: [PATCH 015/119] Fixing output --- terraform/workflow/output.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/output.tf b/terraform/workflow/output.tf index f501eaf..1b3d35d 100644 --- a/terraform/workflow/output.tf +++ b/terraform/workflow/output.tf @@ -1,5 +1,5 @@ output "api-gateway-endpoint" { - value = module.api_gateway.apigatewayv2_api_endpoint.apigatewayv2_api_api_endpoint + value = module.api_gateway.apigatewayv2_api_api_endpoint description = "The endpoint for the API Gateway." } From d11fd39227596f616ddef1d17f8fa72edaba3c9b Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 20:41:22 -0600 Subject: [PATCH 016/119] Updating to include a direct path for apigateway --- .github/workflows/release.yml | 156 ++++++++++++++++++ .../workflow/{main.tf => api-gateway.tf} | 31 ++-- terraform/workflow/providers.tf | 10 ++ 3 files changed, 176 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/release.yml rename terraform/workflow/{main.tf => api-gateway.tf} (90%) create mode 100644 terraform/workflow/providers.tf diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a0574ad --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,156 @@ +name: Relase Clash Bot Workflow + +on: + push: + tags: + - 'v*.*.*' + paths-ignore: + - '**/README.md' + - '**/.gitignore' + +jobs: + eventPublisherDeploy: + name: Event Publisher Lambda Deploy + runs-on: ubuntu-latest + environment: + name: Production + defaults: + run: + working-directory: ./functions/clash-bot/event-publisher + permissions: + id-token: write + contents: read + + steps: + - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install depedencies + run: npm install + + - name: Build + run: npm run build + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser + aws-region: us-east-1 + + - name: Archive and Publish + run: | + zip -r ./event-publisher.zip ./prod ./node_modules ./package.json + aws s3 cp ./event-publisher.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip + + - name: Upload Artifacts + uses: actions/upload-artifact@v2 + with: + name: event-publisher-${{ vars.ENVIRONMENT}}-${{ github.run_number }} + path: ./functions/clash-bot/event-publisher/event-publisher.zip + if-no-files-found: error + + eventHandlerDeploy: + name: Event Handler Lambda Deploy + runs-on: ubuntu-latest + environment: + name: Production + defaults: + run: + working-directory: ./functions/clash-bot/event-handler + permissions: + id-token: write + contents: read + + steps: + - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install dependencies + run: npm install + + - name: Build + run: npm run build + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser + aws-region: us-east-1 + + - name: Archive and Publish + run: | + zip -r ./event-handler.zip ./prod ./node_modules ./package.json + aws s3 cp ./event-handler.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip + + - name: Upload Artifacts + uses: actions/upload-artifact@v2 + with: + name: event-handler-${{ vars.ENVIRONMENT}}-${{ github.run_number }} + path: ./functions/clash-bot/event-handler/event-handler.zip + if-no-files-found: error + + terraformWorkflow: + name: Workflow Apply + runs-on: ubuntu-latest + environment: + name: Development + defaults: + run: + working-directory: ./terraform/workflow + permissions: + id-token: write + contents: read + pull-requests: write + needs: + - eventPublisherDeploy + - eventHandlerDeploy + + steps: + - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Checkout + uses: actions/checkout@v3 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser + aws-region: us-east-1 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v1 + with: + cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} + + - name: Terraform Init + id: init + run: terraform init + + - name: Select Workspace + id: workspace + run: terraform workspace select ${{ vars.WORKSPACE }} || terraform workspace new ${{ vars.WORKSPACE }} + + - name: Apply + id: apply + env: + TF_VAR_region: us-east-1 + TF_VAR_environment: ${{ vars.ENVIRONMENT }} + TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} + TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip + TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip + TF_VAR_sqs_batch_size: "1" + run: terraform apply -no-color -input=false --auto-approve diff --git a/terraform/workflow/main.tf b/terraform/workflow/api-gateway.tf similarity index 90% rename from terraform/workflow/main.tf rename to terraform/workflow/api-gateway.tf index 843827e..d28a533 100644 --- a/terraform/workflow/main.tf +++ b/terraform/workflow/api-gateway.tf @@ -1,23 +1,3 @@ -provider "aws" { - region = var.region - - default_tags { - tags = { - Application = "ClashBot-Workflow" - Environment = var.environment - } - } -} - -data "aws_acm_certificate" "issued" { - domain = "clash-bot.ninja" - statuses = ["ISSUED"] -} - -resource "aws_cloudwatch_log_group" "api_gateway_default_log_group" { - name = "api_gateway_default_log_group-${var.environment}" -} - module "api_gateway" { source = "terraform-aws-modules/apigateway-v2/aws" @@ -41,13 +21,22 @@ module "api_gateway" { # Routes and integrations integrations = { - "POST /api/v2" = { + "POST /api/v2/teams" = { lambda_arn = aws_lambda_function.event_publisher_lambda.arn, integration_type = "AWS_PROXY" } } } +data "aws_acm_certificate" "issued" { + domain = "clash-bot.ninja" + statuses = ["ISSUED"] +} + +resource "aws_cloudwatch_log_group" "api_gateway_default_log_group" { + name = "api_gateway_default_log_group-${var.environment}" +} + module "dynamodb_table" { source = "terraform-aws-modules/dynamodb-table/aws" diff --git a/terraform/workflow/providers.tf b/terraform/workflow/providers.tf new file mode 100644 index 0000000..a0edddc --- /dev/null +++ b/terraform/workflow/providers.tf @@ -0,0 +1,10 @@ +provider "aws" { + region = var.region + + default_tags { + tags = { + Application = "ClashBot-Workflow" + Environment = var.environment + } + } +} \ No newline at end of file From bdde9f9bbc0f45a0e63336a5e72c5343e8f50179 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 21:04:47 -0600 Subject: [PATCH 017/119] Swapping up the way the backend configuration is provided --- .github/workflows/pull-request.yml | 6 +----- .github/workflows/release.yml | 8 ++------ terraform/{workflow => backup}/api-gateway.tf | 0 terraform/{workflow => backup}/event-handler-lambda.tf | 0 terraform/{workflow => backup}/event-publisher-lambda.tf | 0 terraform/{workflow => backup}/output.tf | 0 terraform/{workflow => backup}/providers.tf | 0 terraform/{workflow => backup}/sqs.tf | 0 terraform/{workflow => backup}/step-functions.lambda.tf | 0 terraform/{workflow => backup}/variables.tf | 0 terraform/workflow/backend-configs/development.hcl | 4 ++++ terraform/workflow/backend-configs/production.hcl | 4 ++++ terraform/workflow/main.tf | 9 +++++++++ 13 files changed, 20 insertions(+), 11 deletions(-) rename terraform/{workflow => backup}/api-gateway.tf (100%) rename terraform/{workflow => backup}/event-handler-lambda.tf (100%) rename terraform/{workflow => backup}/event-publisher-lambda.tf (100%) rename terraform/{workflow => backup}/output.tf (100%) rename terraform/{workflow => backup}/providers.tf (100%) rename terraform/{workflow => backup}/sqs.tf (100%) rename terraform/{workflow => backup}/step-functions.lambda.tf (100%) rename terraform/{workflow => backup}/variables.tf (100%) create mode 100644 terraform/workflow/backend-configs/development.hcl create mode 100644 terraform/workflow/backend-configs/production.hcl create mode 100644 terraform/workflow/main.tf diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 26b9ffe..d08237f 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -284,11 +284,7 @@ jobs: - name: Terraform Init id: init - run: terraform init - - - name: Select Workspace - id: workspace - run: terraform workspace select ${{ vars.WORKSPACE }} || terraform workspace new ${{ vars.WORKSPACE }} + run: terraform init -backend-config=/backend-configs/${{ vars.ENVIRONMENT }}.hcl - name: Terraform Validate id: validate diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0574ad..99cc4ed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -138,12 +138,8 @@ jobs: - name: Terraform Init id: init - run: terraform init - - - name: Select Workspace - id: workspace - run: terraform workspace select ${{ vars.WORKSPACE }} || terraform workspace new ${{ vars.WORKSPACE }} - + run: terraform init -backend-config=/backend-configs/${{ vars.ENVIRONMENT }}.hcl + - name: Apply id: apply env: diff --git a/terraform/workflow/api-gateway.tf b/terraform/backup/api-gateway.tf similarity index 100% rename from terraform/workflow/api-gateway.tf rename to terraform/backup/api-gateway.tf diff --git a/terraform/workflow/event-handler-lambda.tf b/terraform/backup/event-handler-lambda.tf similarity index 100% rename from terraform/workflow/event-handler-lambda.tf rename to terraform/backup/event-handler-lambda.tf diff --git a/terraform/workflow/event-publisher-lambda.tf b/terraform/backup/event-publisher-lambda.tf similarity index 100% rename from terraform/workflow/event-publisher-lambda.tf rename to terraform/backup/event-publisher-lambda.tf diff --git a/terraform/workflow/output.tf b/terraform/backup/output.tf similarity index 100% rename from terraform/workflow/output.tf rename to terraform/backup/output.tf diff --git a/terraform/workflow/providers.tf b/terraform/backup/providers.tf similarity index 100% rename from terraform/workflow/providers.tf rename to terraform/backup/providers.tf diff --git a/terraform/workflow/sqs.tf b/terraform/backup/sqs.tf similarity index 100% rename from terraform/workflow/sqs.tf rename to terraform/backup/sqs.tf diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/backup/step-functions.lambda.tf similarity index 100% rename from terraform/workflow/step-functions.lambda.tf rename to terraform/backup/step-functions.lambda.tf diff --git a/terraform/workflow/variables.tf b/terraform/backup/variables.tf similarity index 100% rename from terraform/workflow/variables.tf rename to terraform/backup/variables.tf diff --git a/terraform/workflow/backend-configs/development.hcl b/terraform/workflow/backend-configs/development.hcl new file mode 100644 index 0000000..2029aed --- /dev/null +++ b/terraform/workflow/backend-configs/development.hcl @@ -0,0 +1,4 @@ +# config.remote.tfbackend +workspaces { name = "ClashBotWorkflow" } +hostname = "app.terraform.io" +organization = "ClashBot" diff --git a/terraform/workflow/backend-configs/production.hcl b/terraform/workflow/backend-configs/production.hcl new file mode 100644 index 0000000..b031dce --- /dev/null +++ b/terraform/workflow/backend-configs/production.hcl @@ -0,0 +1,4 @@ +# config.remote.tfbackend +workspaces { name = "ClashBotWorkflowProduction" } +hostname = "app.terraform.io" +organization = "ClashBot" diff --git a/terraform/workflow/main.tf b/terraform/workflow/main.tf new file mode 100644 index 0000000..d1a3c8a --- /dev/null +++ b/terraform/workflow/main.tf @@ -0,0 +1,9 @@ +provider "aws" { + region = "us-east-1" + + default_tags { + tags = { + Application = "ClashBot-Workflow" + } + } +} \ No newline at end of file From fe40807beb91b2f6bfa4a57722222038deb9f9a2 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 21:18:27 -0600 Subject: [PATCH 018/119] Attempting to appropriately dynamically attach a remote backend for terraform --- .github/workflows/pull-request.yml | 2 +- .github/workflows/release.yml | 2 +- .../{development.hcl => development.remote.tfbackend} | 2 +- .../{production.hcl => production.remote.tfbackend} | 0 terraform/workflow/main.tf | 4 ++++ 5 files changed, 7 insertions(+), 3 deletions(-) rename terraform/workflow/backend-configs/{development.hcl => development.remote.tfbackend} (79%) rename terraform/workflow/backend-configs/{production.hcl => production.remote.tfbackend} (100%) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index d08237f..5bd0087 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -284,7 +284,7 @@ jobs: - name: Terraform Init id: init - run: terraform init -backend-config=/backend-configs/${{ vars.ENVIRONMENT }}.hcl + run: terraform init -backend-config=backend-configs/${{ vars.ENVIRONMENT }}.remote.tfbackend - name: Terraform Validate id: validate diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 99cc4ed..9178f19 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -138,7 +138,7 @@ jobs: - name: Terraform Init id: init - run: terraform init -backend-config=/backend-configs/${{ vars.ENVIRONMENT }}.hcl + run: terraform init -backend-config=/backend-configs/${{ vars.ENVIRONMENT }}.remote.tfbackend - name: Apply id: apply diff --git a/terraform/workflow/backend-configs/development.hcl b/terraform/workflow/backend-configs/development.remote.tfbackend similarity index 79% rename from terraform/workflow/backend-configs/development.hcl rename to terraform/workflow/backend-configs/development.remote.tfbackend index 2029aed..89c447b 100644 --- a/terraform/workflow/backend-configs/development.hcl +++ b/terraform/workflow/backend-configs/development.remote.tfbackend @@ -1,4 +1,4 @@ # config.remote.tfbackend workspaces { name = "ClashBotWorkflow" } hostname = "app.terraform.io" -organization = "ClashBot" +organization = "ClashBot" \ No newline at end of file diff --git a/terraform/workflow/backend-configs/production.hcl b/terraform/workflow/backend-configs/production.remote.tfbackend similarity index 100% rename from terraform/workflow/backend-configs/production.hcl rename to terraform/workflow/backend-configs/production.remote.tfbackend diff --git a/terraform/workflow/main.tf b/terraform/workflow/main.tf index d1a3c8a..071461c 100644 --- a/terraform/workflow/main.tf +++ b/terraform/workflow/main.tf @@ -6,4 +6,8 @@ provider "aws" { Application = "ClashBot-Workflow" } } +} + +terraform { + backend "remote" {} } \ No newline at end of file From 495360e39e5ab91712c77b68b3d3fd097efbcfd4 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 21:22:37 -0600 Subject: [PATCH 019/119] Replacing workflow iac --- terraform/{backup => workflow}/api-gateway.tf | 0 .../{backup => workflow}/event-handler-lambda.tf | 0 .../{backup => workflow}/event-publisher-lambda.tf | 0 terraform/workflow/main.tf | 13 ------------- terraform/{backup => workflow}/output.tf | 0 terraform/{backup => workflow}/providers.tf | 4 ++++ terraform/{backup => workflow}/sqs.tf | 0 .../{backup => workflow}/step-functions.lambda.tf | 0 terraform/{backup => workflow}/variables.tf | 0 9 files changed, 4 insertions(+), 13 deletions(-) rename terraform/{backup => workflow}/api-gateway.tf (100%) rename terraform/{backup => workflow}/event-handler-lambda.tf (100%) rename terraform/{backup => workflow}/event-publisher-lambda.tf (100%) delete mode 100644 terraform/workflow/main.tf rename terraform/{backup => workflow}/output.tf (100%) rename terraform/{backup => workflow}/providers.tf (80%) rename terraform/{backup => workflow}/sqs.tf (100%) rename terraform/{backup => workflow}/step-functions.lambda.tf (100%) rename terraform/{backup => workflow}/variables.tf (100%) diff --git a/terraform/backup/api-gateway.tf b/terraform/workflow/api-gateway.tf similarity index 100% rename from terraform/backup/api-gateway.tf rename to terraform/workflow/api-gateway.tf diff --git a/terraform/backup/event-handler-lambda.tf b/terraform/workflow/event-handler-lambda.tf similarity index 100% rename from terraform/backup/event-handler-lambda.tf rename to terraform/workflow/event-handler-lambda.tf diff --git a/terraform/backup/event-publisher-lambda.tf b/terraform/workflow/event-publisher-lambda.tf similarity index 100% rename from terraform/backup/event-publisher-lambda.tf rename to terraform/workflow/event-publisher-lambda.tf diff --git a/terraform/workflow/main.tf b/terraform/workflow/main.tf deleted file mode 100644 index 071461c..0000000 --- a/terraform/workflow/main.tf +++ /dev/null @@ -1,13 +0,0 @@ -provider "aws" { - region = "us-east-1" - - default_tags { - tags = { - Application = "ClashBot-Workflow" - } - } -} - -terraform { - backend "remote" {} -} \ No newline at end of file diff --git a/terraform/backup/output.tf b/terraform/workflow/output.tf similarity index 100% rename from terraform/backup/output.tf rename to terraform/workflow/output.tf diff --git a/terraform/backup/providers.tf b/terraform/workflow/providers.tf similarity index 80% rename from terraform/backup/providers.tf rename to terraform/workflow/providers.tf index a0edddc..9d17f83 100644 --- a/terraform/backup/providers.tf +++ b/terraform/workflow/providers.tf @@ -7,4 +7,8 @@ provider "aws" { Environment = var.environment } } +} + +terraform { + backend "remote" {} } \ No newline at end of file diff --git a/terraform/backup/sqs.tf b/terraform/workflow/sqs.tf similarity index 100% rename from terraform/backup/sqs.tf rename to terraform/workflow/sqs.tf diff --git a/terraform/backup/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf similarity index 100% rename from terraform/backup/step-functions.lambda.tf rename to terraform/workflow/step-functions.lambda.tf diff --git a/terraform/backup/variables.tf b/terraform/workflow/variables.tf similarity index 100% rename from terraform/backup/variables.tf rename to terraform/workflow/variables.tf From ce8fadf71cb1b1e74ad076570f5f6e347177c13d Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 21:47:46 -0600 Subject: [PATCH 020/119] Swapping to use queue url instead of queue arn --- terraform/workflow/event-publisher-lambda.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/event-publisher-lambda.tf b/terraform/workflow/event-publisher-lambda.tf index 4728975..588c621 100644 --- a/terraform/workflow/event-publisher-lambda.tf +++ b/terraform/workflow/event-publisher-lambda.tf @@ -9,7 +9,7 @@ resource "aws_lambda_function" "event_publisher_lambda" { environment { variables = { - QUEUE_URL = module.clash_bot_event_sqs.queue_arn + QUEUE_URL = module.clash_bot_event_sqs.queue_url } } } From a41d7879e38c3f79b98811513bad08be88f729f3 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 21:55:56 -0600 Subject: [PATCH 021/119] Adding in policy to publish to sqs --- terraform/workflow/event-handler-lambda.tf | 4 +++- terraform/workflow/event-publisher-lambda.tf | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/terraform/workflow/event-handler-lambda.tf b/terraform/workflow/event-handler-lambda.tf index 70ed282..184b6a5 100644 --- a/terraform/workflow/event-handler-lambda.tf +++ b/terraform/workflow/event-handler-lambda.tf @@ -65,7 +65,9 @@ data "aws_iam_policy_document" "event_handler_policy_document" { "sqs:GetQueueAttributes", "sqs:ReceiveMessage", ] - resources = ["*"] + resources = [ + module.clash_bot_event_sqs.queue_arn + ] } } diff --git a/terraform/workflow/event-publisher-lambda.tf b/terraform/workflow/event-publisher-lambda.tf index 588c621..7e619da 100644 --- a/terraform/workflow/event-publisher-lambda.tf +++ b/terraform/workflow/event-publisher-lambda.tf @@ -61,11 +61,11 @@ data "aws_iam_policy_document" "event_publisher_policy_document" { statement { effect = "Allow" actions = [ - "sqs:DeleteMessage", - "sqs:GetQueueAttributes", - "sqs:ReceiveMessage", + "sqs:SendMessage" + ] + resources = [ + module.clash_bot_event_sqs.queue_arn ] - resources = ["*"] } } From 1eba6a5203d9f1393e0fc94f2441c9e94124c337 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 22:03:34 -0600 Subject: [PATCH 022/119] Adding MessageGroupid to event-publisher --- functions/clash-bot/event-publisher/src/handler.ts | 3 ++- .../clash-bot/event-publisher/tests/event-publisher.test.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/functions/clash-bot/event-publisher/src/handler.ts b/functions/clash-bot/event-publisher/src/handler.ts index a072657..b075569 100644 --- a/functions/clash-bot/event-publisher/src/handler.ts +++ b/functions/clash-bot/event-publisher/src/handler.ts @@ -34,7 +34,8 @@ export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEven }; const input: SendMessageCommandInput = { QueueUrl: process.env.QUEUE_URL, - MessageBody: JSON.stringify(eventToBeSent) + MessageBody: JSON.stringify(eventToBeSent), + MessageGroupId: 'event', }; const message = new SendMessageCommand(input); const response = await sqsClient diff --git a/functions/clash-bot/event-publisher/tests/event-publisher.test.ts b/functions/clash-bot/event-publisher/tests/event-publisher.test.ts index 02480af..9f63769 100644 --- a/functions/clash-bot/event-publisher/tests/event-publisher.test.ts +++ b/functions/clash-bot/event-publisher/tests/event-publisher.test.ts @@ -34,7 +34,8 @@ describe('Publish an event to SQS with an event type.', () => { await handler(eventTwo, setupContext(), {} as any); await expect(snsMock).toHaveReceivedCommandWith(SendMessageCommand, { QueueUrl: process.env.QUEUE_URL, - MessageBody: JSON.stringify(expectedEventToBeSent) + MessageBody: JSON.stringify(expectedEventToBeSent), + MessageGroupId: 'event', }); }); From 75a990d28e91ef6ec6794819b464fe3670bef93f Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 22:07:54 -0600 Subject: [PATCH 023/119] Adding deduplication id --- functions/clash-bot/event-publisher/src/handler.ts | 1 + .../clash-bot/event-publisher/tests/event-publisher.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/functions/clash-bot/event-publisher/src/handler.ts b/functions/clash-bot/event-publisher/src/handler.ts index b075569..eb64f39 100644 --- a/functions/clash-bot/event-publisher/src/handler.ts +++ b/functions/clash-bot/event-publisher/src/handler.ts @@ -36,6 +36,7 @@ export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEven QueueUrl: process.env.QUEUE_URL, MessageBody: JSON.stringify(eventToBeSent), MessageGroupId: 'event', + MessageDeduplicationId: event.requestContext.requestId }; const message = new SendMessageCommand(input); const response = await sqsClient diff --git a/functions/clash-bot/event-publisher/tests/event-publisher.test.ts b/functions/clash-bot/event-publisher/tests/event-publisher.test.ts index 9f63769..db98a3b 100644 --- a/functions/clash-bot/event-publisher/tests/event-publisher.test.ts +++ b/functions/clash-bot/event-publisher/tests/event-publisher.test.ts @@ -36,6 +36,7 @@ describe('Publish an event to SQS with an event type.', () => { QueueUrl: process.env.QUEUE_URL, MessageBody: JSON.stringify(expectedEventToBeSent), MessageGroupId: 'event', + MessageDeduplicationId: eventTwo.requestContext.requestId }); }); From 707c30d19b6ed2cce2bd323f1c1a2927d94148be Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 22:33:04 -0600 Subject: [PATCH 024/119] Setting up throttling as well as updating handler to invoke state machine --- functions/clash-bot/event-handler/src/handler.ts | 16 +++++++++++++--- terraform/workflow/api-gateway.tf | 7 ++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/functions/clash-bot/event-handler/src/handler.ts b/functions/clash-bot/event-handler/src/handler.ts index 8e6b05f..bd260b6 100644 --- a/functions/clash-bot/event-handler/src/handler.ts +++ b/functions/clash-bot/event-handler/src/handler.ts @@ -9,7 +9,7 @@ export const handler: SQSHandler = async (event: SQSEvent) => { const eventMap = setupSFMap(); logger.info(`Recieved ${event.Records.length} events...`); - event.Records.forEach(record => { + let promises = event.Records.map(record => { logger.info(`Processing event ${record.messageId}...`); const client = new SFNClient({}); @@ -18,16 +18,26 @@ export const handler: SQSHandler = async (event: SQSEvent) => { if (!sfArn) { logger.error(`Unmapped event found event=${parsedEvent.event}`); - throw new Error('Failed to map event.'); + return null; } - client.send(new StartExecutionCommand({ + logger.info(`Setting up payload to trigger SFN ${sfArn}...`); + + return client.send(new StartExecutionCommand({ stateMachineArn: sfArn, name: `${parsedEvent.event}-${record.messageId}`, input: JSON.stringify(parsedEvent.payload), traceHeader: record.messageId })); }); + + promises = promises.filter(p => p !== null); + + logger.info("Waiting for all promises to resolve..."); + + await Promise.all(promises); + + logger.info("All promises resolved."); }; function setupSFMap(): Map { diff --git a/terraform/workflow/api-gateway.tf b/terraform/workflow/api-gateway.tf index d28a533..763976f 100644 --- a/terraform/workflow/api-gateway.tf +++ b/terraform/workflow/api-gateway.tf @@ -23,9 +23,14 @@ module "api_gateway" { integrations = { "POST /api/v2/teams" = { lambda_arn = aws_lambda_function.event_publisher_lambda.arn, - integration_type = "AWS_PROXY" + integration_type = "AWS_PROXY", } } + + default_route_settings = { + throttling_burst_limit = 5 + throttling_rate_limit = 5.0 + } } data "aws_acm_certificate" "issued" { From b737a90d1fb351f7faa20350c87fe25fc6406a9f Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 13 Nov 2023 22:38:57 -0600 Subject: [PATCH 025/119] Adding appropriate permission to start state function --- terraform/workflow/event-handler-lambda.tf | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/terraform/workflow/event-handler-lambda.tf b/terraform/workflow/event-handler-lambda.tf index 184b6a5..348974d 100644 --- a/terraform/workflow/event-handler-lambda.tf +++ b/terraform/workflow/event-handler-lambda.tf @@ -69,6 +69,17 @@ data "aws_iam_policy_document" "event_handler_policy_document" { module.clash_bot_event_sqs.queue_arn ] } + + statement { + effect = "Allow" + actions = [ + "states:StartExecution" + ] + resources = [ + module.create_team_step_function.state_machine_arn + ] + + } } resource "aws_iam_policy" "event_handler_policy" { From cd66ceb31d99bc77bea59c192093658f6154f6b8 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 00:38:54 -0600 Subject: [PATCH 026/119] Adding in create team lambda for step function to leverage --- .github/workflows/pull-request.yml | 76 + .../clash-bot/team/create/jest.config.js | 6 + functions/clash-bot/team/create/package.json | 37 + .../clash-bot/team/create/src/handler.ts | 40 + .../team/create/tests/handler.test.ts | 54 + functions/clash-bot/team/create/tsconfig.json | 22 + terraform/workflow/create-team-lambda.tf | 71 + .../openapi-spec/clashbot-service.yml | 1361 ----------------- terraform/workflow/step-functions.lambda.tf | 22 +- .../create-team-step-function.asl.json | 16 + terraform/workflow/variables.tf | 7 +- 11 files changed, 332 insertions(+), 1380 deletions(-) create mode 100644 functions/clash-bot/team/create/jest.config.js create mode 100644 functions/clash-bot/team/create/package.json create mode 100644 functions/clash-bot/team/create/src/handler.ts create mode 100644 functions/clash-bot/team/create/tests/handler.test.ts create mode 100644 functions/clash-bot/team/create/tsconfig.json create mode 100644 terraform/workflow/create-team-lambda.tf delete mode 100644 terraform/workflow/openapi-spec/clashbot-service.yml create mode 100644 terraform/workflow/step-functions/create-team-step-function.asl.json diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 5bd0087..2be11c7 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -244,6 +244,82 @@ jobs: path: ./functions/clash-bot/event-handler/event-handler.zip if-no-files-found: error + createTeamHandlerBuild: + name: Create Team Handler Lambda Build + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./functions/clash-bot/team/create + needs: + - terraformPreReqs + + steps: + - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install dependencies + run: npm install + + - name: Test + run: npm test + + createTeamHandlerDeploy: + name: Create Team Handler Lambda Deploy + runs-on: ubuntu-latest + environment: + name: Development + defaults: + run: + working-directory: ./functions/clash-bot/event-handler + permissions: + id-token: write + contents: read + needs: + - terraformPreReqs + - eventHandlerBuild + + steps: + - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install dependencies + run: npm install + + - name: Build + run: npm run build + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser + aws-region: us-east-1 + + - name: Archive and Publish + run: | + zip -r ./create-team.zip ./prod ./node_modules ./package.json + aws s3 cp ./create-team.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip + + - name: Upload Artifacts + uses: actions/upload-artifact@v2 + with: + name: create-team-${{ vars.ENVIRONMENT}}-${{ github.run_number }} + path: ./functions/clash-bot/team/create/create-team.zip + if-no-files-found: error + terraformWorkflow: name: Workflow Apply runs-on: ubuntu-latest diff --git a/functions/clash-bot/team/create/jest.config.js b/functions/clash-bot/team/create/jest.config.js new file mode 100644 index 0000000..85c664c --- /dev/null +++ b/functions/clash-bot/team/create/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + transform: {'^.+\\.ts?$': 'ts-jest'}, + testEnvironment: 'node', + testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] + }; \ No newline at end of file diff --git a/functions/clash-bot/team/create/package.json b/functions/clash-bot/team/create/package.json new file mode 100644 index 0000000..95ad8c1 --- /dev/null +++ b/functions/clash-bot/team/create/package.json @@ -0,0 +1,37 @@ +{ + "name": "clash-bot-create-team", + "version": "1.0.0", + "description": "A typescript/nodejs lambda function to create a team in Clash Bot", + "main": "index.js", + "scripts": { + "test": "jest", + "build": "tsc", + "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + }, + "keywords": [ + "clash-bot" + ], + "author": "Poss111", + "license": "ISC", + "devDependencies": { + "@aws-sdk/types": "^3.398.0", + "@types/aws-lambda": "^8.10.119", + "@types/jest": "^29.5.4", + "@types/pino": "^7.0.5", + "@types/uuid": "^9.0.7", + "aws-sdk-client-mock": "^3.0.0", + "aws-sdk-client-mock-jest": "^3.0.0", + "jest": "^29.6.4", + "pino-pretty": "^10.2.0", + "ts-jest": "^29.1.1", + "ts-mockito": "^2.6.1", + "typescript": "^5.2.2" + }, + "dependencies": { + "@aws-sdk/client-dynamodb": "^3.405.0", + "clash-bot-shared": "github:Poss111/ClashBotWorkflowShared#main", + "dotenv": "^16.3.1", + "pino": "^8.15.0", + "uuid": "^9.0.1" + } +} diff --git a/functions/clash-bot/team/create/src/handler.ts b/functions/clash-bot/team/create/src/handler.ts new file mode 100644 index 0000000..19cd4e1 --- /dev/null +++ b/functions/clash-bot/team/create/src/handler.ts @@ -0,0 +1,40 @@ +import { Handler } from 'aws-lambda'; +import pino, { P } from "pino"; +import { DynamoDBClient, PutItemCommand, PutItemCommandInput } from '@aws-sdk/client-dynamodb'; +import { v4 as uuidv4 } from 'uuid'; +import { Team, TeamFromJSON } from 'clash-bot-shared'; + +export const handler: Handler = async (event, context) => { + const level = process.env.LOGGER_LEVEL === undefined ? "info" : process.env.LOGGER_LEVEL; + const logger = pino({ level }); + + logger.info({ eventRecieved: event }, 'Recieved event...'); + logger.info({ contextRecieved: context } , 'Recieved context...'); + + const dynamoDbClient = new DynamoDBClient({}); + + const teamEvent = event as Team; + + // Define the item to add to the table + const item: PutItemCommandInput = { + TableName: process.env.TABLE_NAME, + Item: { + "type": { S: "Team" }, + "id": { S: uuidv4() }, + "playerDetails": { S: JSON.stringify(teamEvent.playerDetails) ?? '' }, + "serverId": { S: teamEvent.serverId ?? ''}, + "tournament": { S: JSON.stringify(teamEvent.tournament) ?? '' }, + "lastUpdatedAt": { S: new Date().toISOString() } + } + }; + + // Create a new PutItemCommand + const command = new PutItemCommand(item); + await dynamoDbClient.send(command); + + return { + status: 'Done', + updatedRecord: event, + originalRecord: event + }; +}; \ No newline at end of file diff --git a/functions/clash-bot/team/create/tests/handler.test.ts b/functions/clash-bot/team/create/tests/handler.test.ts new file mode 100644 index 0000000..17f1c14 --- /dev/null +++ b/functions/clash-bot/team/create/tests/handler.test.ts @@ -0,0 +1,54 @@ +import { handler } from '../src/handler'; +import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb'; +import { v4 as uuidv4 } from 'uuid'; +import { Team, TeamFromJSON } from 'clash-bot-shared'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; + +describe('handler', () => { + it('should return a status of "Done"', async () => { + // Mock the event and context objects + const event: Team = { + playerDetails: { + top: { + discordId: '1', + name: 'someUser' + } + }, + serverId: 'serverId', + tournament: { + tournamentName: 'tournamentName', + tournamentDay: '1' + }, + }; + const context = { /* mock context object */ }; + + const dynamodbMock = mockClient(DynamoDBClient) + dynamodbMock + .on(PutItemCommand) + .resolvesOnce({}); + + // Call the handler function + const result = await handler(event, context as any, {} as any); + + // Assert the result + expect(result).toEqual({ + status: 'Done', + updatedRecord: event, + originalRecord: event + }); + + // Assert that the DynamoDBClient and PutItemCommand were called with the correct arguments + expect(dynamodbMock).toHaveReceivedCommandWith(PutItemCommand, { + TableName: process.env.TABLE_NAME, + Item: { + "type": { S: "Team" }, + "id": { S: expect.any(String) }, + "playerDetails": { S: JSON.stringify(event.playerDetails) }, + "serverId": { S: expect.any(String) }, + "tournament": { S: JSON.stringify(event.tournament) }, + "lastUpdatedAt": { S: expect.any(String) } + } + }); + }); +}); \ No newline at end of file diff --git a/functions/clash-bot/team/create/tsconfig.json b/functions/clash-bot/team/create/tsconfig.json new file mode 100644 index 0000000..8050ecd --- /dev/null +++ b/functions/clash-bot/team/create/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "es2020", + "strict": true, + "preserveConstEnums": true, + "noEmit": false, + "sourceMap": false, + "module":"commonjs", + "moduleResolution":"node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "rootDir": "./src", + "outDir": "prod", + "baseUrl": ".", + "paths": { + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.test.ts"], + } \ No newline at end of file diff --git a/terraform/workflow/create-team-lambda.tf b/terraform/workflow/create-team-lambda.tf new file mode 100644 index 0000000..3cc4152 --- /dev/null +++ b/terraform/workflow/create-team-lambda.tf @@ -0,0 +1,71 @@ +resource "aws_lambda_function" "create_team_lambda" { + function_name = "clash-bot-create-team-${lower(var.environment)}" + handler = "prod/handler.handler" + runtime = "nodejs16.x" + role = aws_iam_role.create_team_lambda_exec.arn + + s3_bucket = var.s3_bucket_name + s3_key = var.create_team_artifact_path + + environment { + variables = { + TABLE_NAME = module.dynamodb_table.dynamodb_table_id + } + } +} + +resource "aws_iam_role" "create_team_lambda_exec" { + name = "clash_bot_create_team_exec_role-${lower(var.environment)}" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" + } + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "create_team_lambda_exec_policy" { + role = aws_iam_role.create_team_lambda_exec.name + policy_arn = aws_iam_policy.create_team_policy.arn +} + +data "aws_iam_policy_document" "create_team_policy_document" { + statement { + effect = "Allow" + actions = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + resources = ["*"] + } + + statement { + effect = "Allow" + actions = [ + "dynamodb:GetItem", + "dynamodb:BatchGetItem", + "dynamodb:Query", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem" + ] + resources = [ + moduel.dynamodb_table.dynamodb_table_arn + ] + } +} + +resource "aws_iam_policy" "create_team_policy" { + name = "ClashBotWorkflowCreateTeamPolicy-${lower(var.environment)}" + description = "Policy for the create team lambda function." + policy = data.aws_iam_policy_document.create_team_policy_document.json +} \ No newline at end of file diff --git a/terraform/workflow/openapi-spec/clashbot-service.yml b/terraform/workflow/openapi-spec/clashbot-service.yml deleted file mode 100644 index c63dad0..0000000 --- a/terraform/workflow/openapi-spec/clashbot-service.yml +++ /dev/null @@ -1,1361 +0,0 @@ -openapi: '3.0.1' -info: - title: Clash Bot Service - description: | - # Welcome to Clash Bot! - - Where all of your League of Legends Clash scheduling needs are met! - - ## Purpose - - Clash Bot Webapp Service to support League of Legends Clash tournament scheduling with Discord. - - ## Disclaimer - - Clash-Bot is not endorsed by Riot Games and does not reflect the views or opinions of Riot Games or - anyone officially involved in producing or managing League of Legends. League of Legends and Riot Games - are trademarks or registered trademarks of Riot Games, Inc. League of Legends © Riot Games, Inc. - contact: - name: ClashBot-API-Support - email: rixxroid@gmail.com - license: - name: Apache 2.0 - url: https://www.apache.org/licenses/LICENSE-2.0.html - version: 2.0.0 -servers: - - url: http://localhost:{port}/{basePath} - description: The local API server. - variables: - port: - default: '8080' - basePath: - default: api/v2 -tags: - - name: Champions - description: Dealing with User's League of Legends champions choices. - - name: Server - description: A Discord server that is leveraging the Clash Bot. - - name: Subscription - description: Subscriptions to certain notifications that Clash Bot produces. - - name: Team - description: A Clash Bot team for a League of Legends Clash tournament. - - name: Tentative - description: A tentative queue for upcoming League of Legends Clash tournaments. - - name: Tournament - description: A League of Legends Clash Tournament. - - name: Maintenance - description: Endpoints related to maintenance of the Service. - - name: User - description: A Discord user that is leveraging the Clash Bot. -components: - schemas: - Server: - description: A Discord Server - type: object - properties: - id: - description: The unique identifier for a Discord Server. - type: string - name: - description: The Discord Server name associated to the id. - type: string - Event: - description: A websocket event to be published. - type: object - required: - - id - - teamEvent - - serverId - - causedBy - properties: - id: - description: The unique identifier for the event. - type: string - teamEvent: - $ref: '#/components/schemas/TeamEvent' - summary: - description: A message to describe the event. - type: string - serverId: - description: The Discord server id attached to the event. - type: string - causedBy: - description: Who the event was caused by. - type: string - TeamEvent: - type: object - required: - - eventType - properties: - team: - $ref: '#/components/schemas/Team' - tentative: - $ref: '#/components/schemas/Tentative' - eventType: - $ref: '#/components/schemas/EventType' - EventType: - description: The type of event. - type: string - enum: - - CREATED - - JOINED - - REMOVED - - UPDATED - - DELETED - Servers: - description: A list of the Player's selected Server - properties: - servers: - type: array - items: - $ref: '#/components/schemas/Server' - Team: - description: A League of Legends Clash Team - type: object - properties: - id: - description: Unique identifier for a Team. - type: string - name: - description: The name of the Team. - type: string - playerDetails: - description: The available positions a Player can be assigned to for a Team. - type: object - properties: - Top: - $ref: '#/components/schemas/TeamPlayer' - Mid: - $ref: '#/components/schemas/TeamPlayer' - Jg: - $ref: '#/components/schemas/TeamPlayer' - Bot: - $ref: '#/components/schemas/TeamPlayer' - Supp: - $ref: '#/components/schemas/TeamPlayer' - serverId: - description: The Discord server id that the Team belongs to. - type: string - tournament: - $ref: '#/components/schemas/BaseTournament' - lastUpdatedAt: - description: The timestamp that the object was updated at - type: string - format: date-time - TeamRequired: - allOf: - - $ref: '#/components/schemas/Team' - - type: object - required: - - serverId - - tournament - - playerDetails - TeamUpdate: - description: Allowed properties to be updated on a Clash Team - required: - - teamName - properties: - teamName: - type: string - description: The name of the team - Teams: - description: A list of League of Legend's Clash Teams - properties: - count: - type: integer - teams: - type: array - items: - $ref: '#/components/schemas/Team' - BaseTournament: - description: The base necessary Tournament details - type: object - properties: - tournamentName: - description: The name of the Tournament. - type: string - tournamentDay: - description: The day number of the Tournament. [1-4] - example: 1 - type: string - DetailedTournament: - description: A League of Legends Clash Tournament - type: object - properties: - tournamentName: - description: The name of the Tournament. - type: string - tournamentDay: - description: The day number of the Tournament. [1-4] - example: 1 - type: string - startTime: - description: When the Tournament starts. - type: string - format: date-time - registrationTime: - description: When you can register for the Tournament. - type: string - format: date-time - Tournaments: - description: A list of Tournaments - type: object - properties: - count: - type: integer - tournaments: - type: array - items: - $ref: '#/components/schemas/DetailedTournament' - Tentative: - description: A queue for Players unsure if they will play Clash for a given Tournament. - type: object - properties: - id: - description: Unique identifier for the Tentative Queue. - type: string - serverId: - description: The default Discord Server id for the player to use. - type: string - tournamentDetails: - $ref: '#/components/schemas/BaseTournament' - tentativePlayers: - items: - $ref: '#/components/schemas/TentativePlayer' - lastUpdatedAt: - description: The timestamp that the object was updated at - type: string - format: date-time - TentativeRequired: - allOf: - - $ref: '#/components/schemas/Tentative' - - type: object - required: - - serverId - - tournamentDetails - - tentativePlayers - Tentatives: - description: A list of queues for Players unsure if they will play Clash for a given Tournament. - type: object - properties: - count: - type: integer - queues: - type: array - items: - $ref: '#/components/schemas/Tentative' - Player: - description: A Clash Bot Player - type: object - properties: - discordId: - description: Discord Id for the Player - type: string - name: - description: The Players discord name - type: string - role: - $ref: '#/components/schemas/Role' - champions: - description: A list of the Users preferred champions. - type: array - items: - $ref: '#/components/schemas/Champion' - subscriptions: - type: array - items: - $ref: '#/components/schemas/Subscription' - serverId: - description: The Discord Server id that the User is defaulted to. - type: string - selectedServers: - description: The list of available Discord Servers for the player to filter by. - type: array - items: - type: string - TeamPlayer: - description: A Player record with a subset of Player information for usage with Teams. - type: object - properties: - discordId: - description: Discord Id for the Player - type: string - name: - description: The Players discord name - type: string - champions: - description: A list of the Users preferred champions. - type: array - items: - $ref: '#/components/schemas/Champion' - TentativePlayer: - description: A Player record with a subset of Player information for usage with Tentative queues. - type: object - properties: - discordId: - description: Discord Id for the Player - type: string - name: - description: The Players discord name - type: string - champions: - description: A list of the Users preferred champions. - type: array - items: - $ref: '#/components/schemas/Champion' - role: - $ref: '#/components/schemas/Role' - Subscription: - description: A map of subscriptions a player has for Clash Bot - type: object - properties: - key: - $ref: '#/components/schemas/SubscriptionType' - isOn: - type: boolean - Champion: - description: A record listing details on a League of Legends champion - type: object - properties: - name: - type: string - Champions: - description: A list of League of Legends champions and their details - properties: - champions: - type: array - items: - $ref: '#/components/schemas/Champion' - Role: - description: A League of Legends role. - type: string - enum: - - TOP - - MID - - JG - - BOT - - SUPP - SubscriptionType: - description: The type of User subscription. - type: string - enum: - - DISCORD_MONDAY_NOTIFICATION - Error: - description: The base error object. - type: object - properties: - code: - type: integer - format: int32 - message: - type: string - PositionDetails: - description: Details necessary to update a User's position on a Team with. - type: object - properties: - champions: - $ref: "#/components/schemas/Champions" - role: - $ref: "#/components/schemas/Role" - TeamTournamentDetails: - description: Details necessary to update a User's position on a Team based on fluid details. - type: object - properties: - discordId: - description: The user's Discord Id. - type: string - tournamentName: - description: The Clash Tournament's name. - tournamentDay: - description: The Clash Tournament's day. - role: - $ref: "#/components/schemas/Role" - ArchiveMetadata: - description: Metadata regarding the archive process. - type: object - properties: - teamsMoved: - description: The number of Teams moved into the archive table. - type: integer - tentativeQueuesMoved: - description: The number of Tentative Queues moved into the archive table. - type: integer - totalTime: - description: The total time the process took in milliseconds - type: string - parameters: - AuditHeaderParam: - name: x-caused-by - in: header - schema: - type: string - default: Not Found - required: true - InactiveQueryParam: - name: archived - in: query - description: Will retrieve records that are from past Tournaments - schema: - type: boolean - default: false - DiscordIdPathParam: - name: discordId - in: path - description: The Discord id of the Player - required: true - schema: - type: string - DiscordIdQueryParam: - name: discordId - in: query - description: The Discord id of the Player - schema: - type: string - ServerIdPathParam: - name: serverId - in: path - description: The Discord id of the Discord Server - required: true - schema: - type: string - ServerIdQueryParam: - name: serverId - in: query - description: The Discord id of the Discord Server - schema: - type: string - SubscriptionPathParam: - name: subscription - description: The subscription type. - in: path - required: true - schema: - $ref: '#/components/schemas/SubscriptionType' - TeamIdPathParam: - name: teamId - in: path - description: The unique identifier of the Clash Bot Team - required: true - schema: - type: string - TentativeQueueIdPathParam: - name: tentativeId - in: path - description: The unique identifier of the Clash Bot Tentative queue - required: true - schema: - type: string - TournamentNamePathParam: - name: tournamentName - in: path - description: The LoL Clash Tournament name - required: true - schema: - type: string - TournamentNameQueryParam: - name: tournamentName - in: query - description: The LoL Clash Tournament name - schema: - type: string - TournamentDayPathParam: - name: tournamentDay - in: path - description: The LoL Clash Tournament day - required: true - schema: - type: string - TournamentDayQueryParam: - name: tournamentDay - in: query - description: The LoL Clash Tournament day - schema: - type: string - requestBodies: - PostTeamRequestBody: - description: Details to create a Clash Bot Team with. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/TeamRequired' - examples: - baseCreateTeam: - $ref: '#/components/examples/TeamCreateExample' - PatchTeamRequestBody: - description: Details to update a Clash Bot Team's metadata with. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/TeamUpdate' - examples: - baseCreateTeam: - $ref: '#/components/examples/TeamMetadataExample' - PatchAssignUserToTeamRequestBody: - description: Details to assign a user to a Team with. - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PositionDetails' - examples: - assignTop: - $ref: '#/components/examples/AssignUserToTeamAsTopExample' - assignJg: - $ref: '#/components/examples/AssignUserToTeamAsJgExample' - assignMid: - $ref: '#/components/examples/AssignUserToTeamAsMidExample' - assignBot: - $ref: '#/components/examples/AssignUserToTeamAsBotExample' - assignSupp: - $ref: '#/components/examples/AssignUserToTeamAsSuppExample' - PostTentativeQueueRequestBody: - description: Details to create a Clash Bot Tentative Queue with. - content: - application/json: - schema: - $ref: '#/components/schemas/TentativeRequired' - examples: - baseCreateTeam: - $ref: '#/components/examples/TentativeQueueCreateExample' - CreatePlayerBody: - description: All necessary parameters to create a new Player - content: - application/json: - schema: - type: object - required: - - discordId - - name - - serverId - properties: - discordId: - description: The Discord id of the player - type: string - name: - description: The Clash Bot User's name - type: string - serverId: - description: The Discord Server that the player is using by default. - type: string - selectedGuilds: - description: The list of available Discord Servers for the player to filter by. - type: array - items: - type: string - UpdatePlayerBody: - description: All necessary parameters to update an existing Player - content: - application/json: - schema: - type: object - required: - - serverId - properties: - serverId: - description: The Discord Server that the player is using by default. - type: string - ChampionRequestBody: - description: A list of champions. - content: - application/json: - schema: - $ref: '#/components/schemas/Champions' - ServerRequestBody: - description: A list of Discord Servers. - content: - application/json: - schema: - $ref: '#/components/schemas/Servers' - - responses: - ArchiveResponse: - description: The metadata regarding the archive process. - content: - application/json: - schema: - $ref: "#/components/schemas/ArchiveMetadata" - TentativeResponse: - description: A Tentative queue. - content: - application/json: - schema: - $ref: "#/components/schemas/Tentative" - TentativeListResponse: - description: The retrieved list of Tentative queues for a Discord Server. - content: - application/json: - schema: - $ref: "#/components/schemas/Tentatives" - TeamResponse: - description: A Clash Bot Team. - content: - application/json: - schema: - $ref: "#/components/schemas/Team" - examples: - success: - $ref: '#/components/examples/TeamExample' - TeamListResponse: - description: The retrieved list of Teams for a Discord Server. - content: - application/json: - schema: - $ref: "#/components/schemas/Teams" - TeamInteractionResponse: - description: The updated team details. - content: - application/json: - schema: - $ref: "#/components/schemas/Team" - ChampionListResponse: - description: List of champions for a Player - content: - application/json: - schema: - $ref: "#/components/schemas/Champions" - SelectedServersResponse: - description: List of selected Servers for a Player - content: - application/json: - schema: - $ref: "#/components/schemas/Servers" - NotFound: - description: Unable to find requested resource - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - NoneFound: - description: No results found matching the criteria. - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - BadInput: - description: Input given is invalid. - ClashBotException: - description: Default error. - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - examples: - TeamCreateExample: - description: | - The minimum required payload to create a Team with. If you do not provide a name, it will be created for you. - summary: A base Team post body. - value: - { - "playerDetails": { - "Top": { - "discordId": "1", - } - }, - "serverId": 123, - "tournament": { - "tournamentName": "awesome_sauce", - "tournamentDay": "1" - } - } - AssignUserToTeamAsTopExample: - description: | - The details to assign a Player to a Clash Bot Team as Top. - summary: Assign Player to a Team as Top. - value: - { - "role": "TOP" - } - AssignUserToTeamAsJgExample: - description: | - The details to assign a Player to a Clash Bot Team as Jungle. - summary: Assign Player to a Team as Jungle. - value: - { - "role": "JG" - } - AssignUserToTeamAsMidExample: - description: | - The details to assign a Player to a Clash Bot Team as Mid. - summary: Assign Player to a Team as Mid. - value: - { - "role": "MID" - } - AssignUserToTeamAsBotExample: - description: | - The details to assign a Player to a Clash Bot Team as Bot. - summary: Assign Player to a Team as Bot. - value: - { - "role": "BOT" - } - AssignUserToTeamAsSuppExample: - description: | - The details to assign a Player to a Clash Bot Team as Support. - summary: Assign Player to a Team as Support. - value: - { - "role": "SUPP" - } - TeamMetadataExample: - description: | - An example of a payload to update the Clash Bot Team's metadata. - summary: A base Team patch body. - value: - { - "teamName": "Some new name", - } - TentativeQueueCreateExample: - description: | - The minimum required payload to create a Tentative Queue with. - summary: A base Tentative queue post body. - value: - { - "serverId": 1234, - "tournamentDetails": { - "tournamentName": "awesome_sauce", - "tournamentDay": "1" - }, - "tentativePlayers": [ - { - "discordId": "1", - "role": "Top" - } - ] - } - TeamExample: - description: | - A Team object. - summary: A Team - value: - { - "id": "123abcde", - "name": "A simple team", - "playerDetails": { - "Top": { - "discordId": "1234", - "name": "Player 1", - "champions": [ - { - "name": "Aatrox" - } - ] - }, - "Mid": { - "discordId": "1235", - "name": "Player 2", - "champions": [ - { - "name": "Anivia" - } - ] - } - }, - "serverId": 12345, - "tournament": { - "tournamentName": "awesome_sauce", - "tournamentDay": "1" - } - } - TentativeExample: - description: | - A Tentative queue object. - summary: A Tentative queue - value: - { - "id": "1234asdf", - "serverId": 1234, - "tournamentDetails": { - "tournamentName": "awesome_sauce", - "tournamentDay": "1" - }, - "tentativePlayers": [ - { - "discordId": "1", - "name": "Player One", - "champions": [ - { - "name": "Azir" - } - ], - "role": "Mid" - } - ] - } -paths: - /teams: - summary: Used to interact with Teams that are active - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - get: - description: Returns a list of Teams. - tags: - - Team - parameters: - - $ref: '#/components/parameters/InactiveQueryParam' - - $ref: '#/components/parameters/DiscordIdQueryParam' - - $ref: '#/components/parameters/ServerIdQueryParam' - - $ref: '#/components/parameters/TournamentNameQueryParam' - - $ref: '#/components/parameters/TournamentDayQueryParam' - operationId: retrieveTeams - responses: - 200: - $ref: '#/components/responses/TeamListResponse' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NoneFound' - 500: - $ref: '#/components/responses/ClashBotException' - post: - description: Creates a Team with the defined details - tags: - - Team - operationId: createTeam - requestBody: - $ref: '#/components/requestBodies/PostTeamRequestBody' - responses: - 200: - $ref: '#/components/responses/TeamResponse' - 400: - $ref: '#/components/responses/BadInput' - 500: - $ref: '#/components/responses/ClashBotException' - /teams/{teamId}: - summary: To interact with a created Team. - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - - $ref: '#/components/parameters/TeamIdPathParam' - get: - description: Returns a single Clash Bot Team based on the id provided. - operationId: retrieveTeamBasedOnId - tags: - - Team - responses: - 200: - $ref: '#/components/responses/TeamResponse' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NoneFound' - 500: - $ref: '#/components/responses/ClashBotException' - patch: - description: Updates an existing Clash Bot Team's metadata. - operationId: updateTeam - tags: - - Team - requestBody: - $ref: '#/components/requestBodies/PatchTeamRequestBody' - responses: - 200: - $ref: '#/components/responses/TeamResponse' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NoneFound' - 500: - $ref: '#/components/responses/ClashBotException' - /teams/{teamId}/users/{discordId}: - summary: To interact with a Team on the behalf of a Player. - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - - $ref: '#/components/parameters/TeamIdPathParam' - - $ref: '#/components/parameters/DiscordIdPathParam' - patch: - description: Assign's a User to the specified Team based on the role provided. - operationId: assignUserToTeam - tags: - - Team - requestBody: - $ref: '#/components/requestBodies/PatchAssignUserToTeamRequestBody' - responses: - 200: - $ref: '#/components/responses/TeamInteractionResponse' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NoneFound' - 500: - $ref: '#/components/responses/ClashBotException' - delete: - description: Removes a User from the specified Team. - operationId: removeUserFromTeam - tags: - - Team - responses: - 200: - $ref: '#/components/responses/TeamInteractionResponse' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NoneFound' - 500: - $ref: '#/components/responses/ClashBotException' - /tentatives: - summary: Interacts with Tentative queues. - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - get: - description: Retrieves a list of Tentative queues. - operationId: retrieveTentativeQueues - parameters: - - $ref: '#/components/parameters/InactiveQueryParam' - - $ref: '#/components/parameters/DiscordIdQueryParam' - - $ref: '#/components/parameters/ServerIdQueryParam' - - $ref: '#/components/parameters/TournamentNameQueryParam' - - $ref: '#/components/parameters/TournamentDayQueryParam' - tags: - - Tentative - responses: - 200: - $ref: '#/components/responses/TentativeListResponse' - 404: - $ref: '#/components/responses/NoneFound' - 500: - $ref: '#/components/schemas/Error' - post: - description: Creates a Tentative queue. - operationId: createTentativeQueue - requestBody: - $ref: '#/components/requestBodies/PostTentativeQueueRequestBody' - tags: - - Tentative - responses: - 200: - $ref: '#/components/responses/TentativeResponse' - 204: - $ref: '#/components/responses/NoneFound' - 500: - $ref: '#/components/schemas/Error' - /tentatives/{tentativeId}: - summary: Interacts with a Tentative queue. - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - - $ref: '#/components/parameters/TentativeQueueIdPathParam' - get: - description: Retrieves a Tentative queues. - operationId: retrieveTentativeQueue - tags: - - Tentative - responses: - 200: - $ref: '#/components/responses/TentativeResponse' - 404: - $ref: '#/components/responses/NoneFound' - 500: - $ref: '#/components/schemas/Error' - /tentatives/{tentativeId}/users/{discordId}: - summary: Interacts with a specific Tentative queue. - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - - $ref: '#/components/parameters/TentativeQueueIdPathParam' - - $ref: '#/components/parameters/DiscordIdPathParam' - patch: - description: Updates an existing Tentative queue. - operationId: assignUserToATentativeQueue - tags: - - Tentative - responses: - 200: - $ref: '#/components/responses/TentativeResponse' - 404: - $ref: '#/components/responses/NoneFound' - 500: - $ref: '#/components/schemas/Error' - delete: - description: Removes a User from the specified Tentative Queue. - operationId: removeUserFromTentativeQueue - tags: - - Tentative - responses: - 200: - $ref: '#/components/responses/TentativeResponse' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NoneFound' - 500: - $ref: '#/components/responses/ClashBotException' - /tournaments: - summary: APIs to interact with Clash Tournaments. - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - get: - operationId: getTournaments - parameters: - - name: tournament - description: The Tournament name to filter by. - in: query - style: form - required: false - schema: - type: string - - name: day - description: The tournament day to filter by. - in: query - style: form - required: false - schema: - type: string - - name: upcomingOnly - description: Whether to return only upcoming tournaments or not? - in: query - style: form - required: false - schema: - type: boolean - tags: - - Tournament - responses: - 200: - description: return a tournament or Tournaments - content: - application/json: - schema: - $ref: '#/components/schemas/Tournaments' - 400: - description: If no Tournaments can be found with a name. - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - post: - description: To create a Tournament - operationId: createTournament - tags: - - Tournament - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/DetailedTournament' - responses: - 200: - description: Create Tournament record - content: - application/json: - schema: - $ref: '#/components/schemas/DetailedTournament' - /tournaments/riot: - summary: Used to interact with Riot's Clash Tournaments api for League of Legends - patch: - description: Will retrieve Clash Tournaments from Riot's League of Legends API - operationId: retrieveRiotClashTournaments - tags: - - Tournament - responses: - 200: - description: Successfully retrieved and persisted Clash Tournaments - content: - application/json: - schema: - $ref: '#/components/schemas/Tournaments' - /users: - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - get: - description: Retrieve a Clash Bot Player Details - operationId: getUser - parameters: - - $ref: '#/components/parameters/DiscordIdQueryParam' - tags: - - User - responses: - 200: - description: The Clash Bot Player details. - content: - application/json: - schema: - $ref: '#/components/schemas/Player' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 500: - $ref: '#/components/responses/ClashBotException' - default: - $ref: "#/components/responses/ClashBotException" - post: - description: Create a new Clash Bot Player. - operationId: createUser - tags: - - User - requestBody: - $ref: '#/components/requestBodies/CreatePlayerBody' - responses: - 200: - description: Created a new Clash Bot Player - content: - application/json: - schema: - $ref: '#/components/schemas/Player' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 500: - $ref: '#/components/responses/ClashBotException' - default: - $ref: "#/components/responses/ClashBotException" - /users/{discordId}: - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - - $ref: '#/components/parameters/DiscordIdPathParam' - patch: - description: Update an existing Clash Bot Player. - operationId: updateUser - tags: - - User - requestBody: - $ref: '#/components/requestBodies/UpdatePlayerBody' - responses: - 200: - description: Updated an existing Player - content: - application/json: - schema: - $ref: '#/components/schemas/Player' - 404: - $ref: '#/components/responses/NotFound' - 500: - $ref: '#/components/responses/ClashBotException' - default: - $ref: "#/components/responses/ClashBotException" - /users/{discordId}/subscriptions/{subscription}: - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - - $ref: '#/components/parameters/DiscordIdPathParam' - - $ref: '#/components/parameters/SubscriptionPathParam' - get: - description: Retrieve details on a user's subscription. - operationId: isUserSubscribed - tags: - - Subscription - responses: - 200: - description: The User's subscription details. - content: - application/json: - schema: - $ref: '#/components/schemas/Subscription' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 500: - $ref: '#/components/responses/ClashBotException' - default: - $ref: "#/components/responses/ClashBotException" - post: - description: Subscribes the User to the specified subscription. - operationId: subscribeUser - tags: - - Subscription - responses: - 200: - description: The User's subscription details after a successful subscription. - content: - application/json: - schema: - $ref: '#/components/schemas/Subscription' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 500: - $ref: '#/components/responses/ClashBotException' - default: - $ref: "#/components/responses/ClashBotException" - delete: - description: Unsubscribes the User from the specified subscription. - operationId: unsubscribeUser - tags: - - Subscription - responses: - 200: - description: The User's subscription details after they have successfully unsubscribed. - content: - application/json: - schema: - $ref: '#/components/schemas/Subscription' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 500: - $ref: '#/components/responses/ClashBotException' - default: - $ref: "#/components/responses/ClashBotException" - /users/{discordId}/champions: - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - - $ref: '#/components/parameters/DiscordIdPathParam' - get: - description: Returns a list of preferred champions that the User has. - operationId: retrieveUsersPreferredChampions - tags: - - Champions - responses: - 200: - $ref: '#/components/responses/ChampionListResponse' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 5XX: - $ref: '#/components/schemas/Error' - post: - description: Updates the users preferred champions with an entirely new list. Cannot be greater than a length of 5. - operationId: createListOfPreferredChampionsForUser - tags: - - Champions - requestBody: - $ref: '#/components/requestBodies/ChampionRequestBody' - responses: - 200: - $ref: '#/components/responses/ChampionListResponse' - 204: - $ref: '#/components/responses/NoneFound' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 5XX: - $ref: '#/components/schemas/Error' - patch: - description: Adds the requested champion to the users preferred champions. Cannot be greater than a length of 5. - operationId: addToPreferredChampionsForUser - tags: - - Champions - requestBody: - $ref: '#/components/requestBodies/ChampionRequestBody' - responses: - 200: - $ref: '#/components/responses/ChampionListResponse' - 204: - $ref: '#/components/responses/NoneFound' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 5XX: - $ref: '#/components/schemas/Error' - delete: - description: Removes the requested champion to the users preferred champions. - operationId: removePreferredChampionForUser - tags: - - Champions - parameters: - - name: champions - description: The list of champion's names to remove from the user's list of champions - in: query - required: true - schema: - type: array - items: - type: string - responses: - 200: - $ref: '#/components/responses/ChampionListResponse' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 5XX: - $ref: '#/components/schemas/Error' - /users/{discordId}/servers: - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - - $ref: '#/components/parameters/DiscordIdPathParam' - get: - description: Returns a list of selected servers that the User has. - operationId: retrieveUsersSelectedServers - tags: - - User - responses: - 200: - $ref: '#/components/responses/SelectedServersResponse' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 5XX: - $ref: '#/components/schemas/Error' - post: - description: Updates the users selected servers with an entirely new list. Cannot be greater than a length of 5. - operationId: createUsersSelectedServers - tags: - - User - requestBody: - $ref: '#/components/requestBodies/ServerRequestBody' - responses: - 200: - $ref: '#/components/responses/SelectedServersResponse' - 204: - $ref: '#/components/responses/NoneFound' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 5XX: - $ref: '#/components/schemas/Error' - patch: - description: Adds the selected server to the users selected servers. Cannot be greater than a length of 5. - operationId: addUsersSelectedServers - tags: - - User - requestBody: - $ref: '#/components/requestBodies/ServerRequestBody' - responses: - 200: - $ref: '#/components/responses/SelectedServersResponse' - 204: - $ref: '#/components/responses/NoneFound' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 5XX: - $ref: '#/components/schemas/Error' - delete: - description: Removes the selected server to the users selected servers. - operationId: removeUsersSelectedServers - tags: - - User - parameters: - - name: champions - description: The list of selected servers to remove from the user's list of champions - in: query - required: true - schema: - type: array - items: - type: string - responses: - 200: - $ref: '#/components/responses/SelectedServersResponse' - 400: - $ref: '#/components/responses/BadInput' - 404: - $ref: '#/components/responses/NotFound' - 5XX: - $ref: '#/components/schemas/Error' - /archive: - parameters: - - $ref: '#/components/parameters/AuditHeaderParam' - post: - operationId: archiveTeamsAndTentativeQueues - description: | - Will move all Teams and Tentative Queues that are now inactive into an archive table. - This will help keep current operations clean and quick. - tags: - - Maintenance - responses: - 200: - $ref: '#/components/responses/ArchiveResponse' - 5XX: - $ref: '#/components/schemas/Error' - diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index db54a87..24301f1 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -1,25 +1,11 @@ module "create_team_step_function" { source = "terraform-aws-modules/step-functions/aws" - name = "retrieve-teams-${var.environment}" - definition = < Date: Fri, 17 Nov 2023 00:44:59 -0600 Subject: [PATCH 027/119] Adding a check for the json payload and fixing depedency in deployment --- .github/workflows/pull-request.yml | 2 +- .../clash-bot/team/create/src/handler.ts | 52 +++++++++++-------- .../team/create/tests/handler.test.ts | 22 ++++++++ functions/clash-bot/team/create/tsconfig.json | 2 + 4 files changed, 55 insertions(+), 23 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 2be11c7..3e2c767 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -283,7 +283,7 @@ jobs: contents: read needs: - terraformPreReqs - - eventHandlerBuild + - createTeamHandlerBuild steps: - uses: FranzDiebold/github-env-vars-action@v2.1.0 diff --git a/functions/clash-bot/team/create/src/handler.ts b/functions/clash-bot/team/create/src/handler.ts index 19cd4e1..720c17f 100644 --- a/functions/clash-bot/team/create/src/handler.ts +++ b/functions/clash-bot/team/create/src/handler.ts @@ -14,27 +14,35 @@ export const handler: Handler = async (event, context) => { const dynamoDbClient = new DynamoDBClient({}); const teamEvent = event as Team; + + if (teamEvent.playerDetails === undefined) { + return { + status: 'Failed', + details: 'Player details are required to create a team.', + originalRecord: event + }; + } else { + // Define the item to add to the table + const item: PutItemCommandInput = { + TableName: process.env.TABLE_NAME, + Item: { + "type": { S: "Team" }, + "id": { S: uuidv4() }, + "playerDetails": { S: JSON.stringify(teamEvent.playerDetails) ?? '' }, + "serverId": { S: teamEvent.serverId ?? ''}, + "tournament": { S: JSON.stringify(teamEvent.tournament) ?? '' }, + "lastUpdatedAt": { S: new Date().toISOString() } + } + }; + + // Create a new PutItemCommand + const command = new PutItemCommand(item); + await dynamoDbClient.send(command); - // Define the item to add to the table - const item: PutItemCommandInput = { - TableName: process.env.TABLE_NAME, - Item: { - "type": { S: "Team" }, - "id": { S: uuidv4() }, - "playerDetails": { S: JSON.stringify(teamEvent.playerDetails) ?? '' }, - "serverId": { S: teamEvent.serverId ?? ''}, - "tournament": { S: JSON.stringify(teamEvent.tournament) ?? '' }, - "lastUpdatedAt": { S: new Date().toISOString() } - } - }; - - // Create a new PutItemCommand - const command = new PutItemCommand(item); - await dynamoDbClient.send(command); - - return { - status: 'Done', - updatedRecord: event, - originalRecord: event - }; + return { + status: 'Done', + updatedRecord: event, + originalRecord: event + }; + } }; \ No newline at end of file diff --git a/functions/clash-bot/team/create/tests/handler.test.ts b/functions/clash-bot/team/create/tests/handler.test.ts index 17f1c14..0885377 100644 --- a/functions/clash-bot/team/create/tests/handler.test.ts +++ b/functions/clash-bot/team/create/tests/handler.test.ts @@ -51,4 +51,26 @@ describe('handler', () => { } }); }); + + it('Should return a failed status if the event does not include player details', async () => { + // Mock the event and context objects + const event: Team = { + serverId: 'serverId', + tournament: { + tournamentName: 'tournamentName', + tournamentDay: '1' + }, + }; + const context = { /* mock context object */ }; + + // Call the handler function + const result = await handler(event, context as any, {} as any); + + // Assert the result + expect(result).toEqual({ + status: 'Failed', + details: 'Player details are required to create a team.', + originalRecord: event + }); + }); }); \ No newline at end of file diff --git a/functions/clash-bot/team/create/tsconfig.json b/functions/clash-bot/team/create/tsconfig.json index 8050ecd..62859a2 100644 --- a/functions/clash-bot/team/create/tsconfig.json +++ b/functions/clash-bot/team/create/tsconfig.json @@ -15,6 +15,8 @@ "outDir": "prod", "baseUrl": ".", "paths": { + "clash-bot-shared": [ "node_modules/clash-bot-shared/src"], + "clash-bot-shared/*": [ "node_modules/clash-bot-shared/src/*"] } }, "include": ["src/**/*"], From dc6a4bb628164237dae44c5ec2b91c2a5ef68691 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 00:50:24 -0600 Subject: [PATCH 028/119] Fixing working directory --- .github/workflows/pull-request.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 3e2c767..d5edcda 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -277,7 +277,7 @@ jobs: name: Development defaults: run: - working-directory: ./functions/clash-bot/event-handler + working-directory: ./functions/clash-bot/team/create permissions: id-token: write contents: read @@ -374,6 +374,7 @@ jobs: TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip + TF_VAR_create_team_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip TF_VAR_sqs_batch_size: "1" run: terraform apply -no-color -input=false --auto-approve From 3ef0696506b0442c02606ee427373f475b053a49 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 00:55:31 -0600 Subject: [PATCH 029/119] Fixing typo --- terraform/workflow/create-team-lambda.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/create-team-lambda.tf b/terraform/workflow/create-team-lambda.tf index 3cc4152..afec580 100644 --- a/terraform/workflow/create-team-lambda.tf +++ b/terraform/workflow/create-team-lambda.tf @@ -59,7 +59,7 @@ data "aws_iam_policy_document" "create_team_policy_document" { "dynamodb:BatchWriteItem" ] resources = [ - moduel.dynamodb_table.dynamodb_table_arn + module.dynamodb_table.dynamodb_table_arn ] } } From 56fc30d62b97e42aae0df6b18bda3dfb44caf946 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 00:59:38 -0600 Subject: [PATCH 030/119] Fixing reference to create team lambda --- .github/workflows/pull-request.yml | 13 +++++-------- terraform/workflow/step-functions.lambda.tf | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index d5edcda..548d079 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -129,8 +129,7 @@ jobs: permissions: id-token: write contents: read - needs: - - terraformPreReqs + needs: - eventPublisherBuild steps: @@ -205,8 +204,7 @@ jobs: permissions: id-token: write contents: read - needs: - - terraformPreReqs + needs: - eventHandlerBuild steps: @@ -281,8 +279,7 @@ jobs: permissions: id-token: write contents: read - needs: - - terraformPreReqs + needs: - createTeamHandlerBuild steps: @@ -332,10 +329,10 @@ jobs: id-token: write contents: read pull-requests: write - needs: - - terraformPreReqs + needs: - eventPublisherDeploy - eventHandlerDeploy + - createTeamHandlerDeploy steps: - uses: FranzDiebold/github-env-vars-action@v2.1.0 diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 24301f1..bbce68e 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -3,7 +3,7 @@ module "create_team_step_function" { name = "create-team-${var.environment}" definition = templatefile("${path.module}/step-functions/create-team-step-function.asl.json", { - CreateTeamLambdaFunctionArn = aws_lambda_function.test_lambda.arn + CreateTeamLambdaFunctionArn = aws_lambda_function.create_team_lambda.arn } ) From 9a74e791af63684e8df945aeb7b2236ed1c654be Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 14:28:13 -0600 Subject: [PATCH 031/119] Adding in retrieve teams and refactoring workflow --- .github/workflows/build_and_deploy_lambda.yml | 72 +++++ .github/workflows/pull-request.yml | 267 +++------------ .../retrieve/ClashBotWorkflow.code-workspace | 37 +++ .../clash-bot/team/retrieve/jest.config.js | 6 + .../clash-bot/team/retrieve/package.json | 38 +++ .../clash-bot/team/retrieve/src/handler.ts | 97 ++++++ .../team/retrieve/tests/handler.test.ts | 304 ++++++++++++++++++ .../clash-bot/team/retrieve/tsconfig.json | 24 ++ terraform/workflow/retrieve-teams-lambda.tf | 71 ++++ terraform/workflow/step-functions.lambda.tf | 3 +- .../create-team-step-function.asl.json | 48 ++- terraform/workflow/variables.tf | 5 + 12 files changed, 743 insertions(+), 229 deletions(-) create mode 100644 .github/workflows/build_and_deploy_lambda.yml create mode 100644 functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace create mode 100644 functions/clash-bot/team/retrieve/jest.config.js create mode 100644 functions/clash-bot/team/retrieve/package.json create mode 100644 functions/clash-bot/team/retrieve/src/handler.ts create mode 100644 functions/clash-bot/team/retrieve/tests/handler.test.ts create mode 100644 functions/clash-bot/team/retrieve/tsconfig.json create mode 100644 terraform/workflow/retrieve-teams-lambda.tf diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml new file mode 100644 index 0000000..0b12208 --- /dev/null +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -0,0 +1,72 @@ +on: + workflow_call: + inputs: + working-directory: + description: 'The working directory to run the commands in' + required: true + type: string + artifact-name: + description: 'The name of the artifact to upload' + required: true + type: string + environment-name: + description: 'The name of the environment to deploy to' + required: true + type: string + s3-bucket-name: + description: 'The name of the S3 bucket to upload the artifact to' + required: true + type: string + region: + description: 'The AWS region to deploy to' + required: true + type: string + default: 'us-east-1' + +jobs: + buildAndDeployLambda: + name: Lambda Build and Deploy to S3 + runs-on: ubuntu-latest + defaults: + run: + working-directory: ${{ inputs.working-directory }} + environment: + name: ${{ inputs.environment-name }} + permissions: + id-token: write + contents: read + + steps: + - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install depedencies + run: npm install + + - name: Test + run: npm test + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser + aws-region: ${{ inputs.region }} + + - name: Archive and Publish + run: | + zip -r ./${{ inputs.artifact-name}}.zip ./prod ./node_modules ./package.json + aws s3 cp ./event-publisher.zip s3://${{ inputs.s3-bucket-name }}/artifacts/${{ toLower(inputs.environment-name) }}/${{ github.run_number }}/${{ inputs.artifact-name}}.zip + + - name: Upload Artifacts + uses: actions/upload-artifact@v2 + with: + name: event-publisher-${{ toLower(inputs.environment-name) }}-${{ github.run_number }} + path: ${{ inputs.working-directory}}/${{ inputs.artifact-name }}.zip + if-no-files-found: error \ No newline at end of file diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 548d079..9be37b1 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -92,230 +92,41 @@ jobs: body: output }) - eventPublisherBuild: - name: Event Publisher Lambda Build - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./functions/clash-bot/event-publisher - needs: - - terraformPreReqs - - steps: - - uses: FranzDiebold/github-env-vars-action@v2.1.0 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: '16' - - - name: Checkout - uses: actions/checkout@v2 - - - name: Install depedencies - run: npm install - - - name: Test - run: npm test - - eventPublisherDeploy: - name: Event Publisher Lambda Deploy - runs-on: ubuntu-latest - environment: - name: Development - defaults: - run: - working-directory: ./functions/clash-bot/event-publisher - permissions: - id-token: write - contents: read - needs: - - eventPublisherBuild - - steps: - - uses: FranzDiebold/github-env-vars-action@v2.1.0 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: '16' - - - name: Checkout - uses: actions/checkout@v2 - - - name: Install depedencies - run: npm install - - - name: Build - run: npm run build - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v3 - with: - role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser - aws-region: us-east-1 - - - name: Archive and Publish - run: | - zip -r ./event-publisher.zip ./prod ./node_modules ./package.json - aws s3 cp ./event-publisher.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip - - - name: Upload Artifacts - uses: actions/upload-artifact@v2 - with: - name: event-publisher-${{ vars.ENVIRONMENT}}-${{ github.run_number }} - path: ./functions/clash-bot/event-publisher/event-publisher.zip - if-no-files-found: error - - eventHandlerBuild: - name: Event Handler Lambda Build - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./functions/clash-bot/event-handler - needs: - - terraformPreReqs - - steps: - - uses: FranzDiebold/github-env-vars-action@v2.1.0 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: '16' - - - name: Checkout - uses: actions/checkout@v2 - - - name: Install dependencies - run: npm install - - - name: Test - run: npm test - - eventHandlerDeploy: - name: Event Handler Lambda Deploy - runs-on: ubuntu-latest - environment: - name: Development - defaults: - run: - working-directory: ./functions/clash-bot/event-handler - permissions: - id-token: write - contents: read - needs: - - eventHandlerBuild - - steps: - - uses: FranzDiebold/github-env-vars-action@v2.1.0 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: '16' - - - name: Checkout - uses: actions/checkout@v2 - - - name: Install dependencies - run: npm install - - - name: Build - run: npm run build - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v3 - with: - role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser - aws-region: us-east-1 - - - name: Archive and Publish - run: | - zip -r ./event-handler.zip ./prod ./node_modules ./package.json - aws s3 cp ./event-handler.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip - - - name: Upload Artifacts - uses: actions/upload-artifact@v2 - with: - name: event-handler-${{ vars.ENVIRONMENT}}-${{ github.run_number }} - path: ./functions/clash-bot/event-handler/event-handler.zip - if-no-files-found: error - - createTeamHandlerBuild: - name: Create Team Handler Lambda Build - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./functions/clash-bot/team/create - needs: - - terraformPreReqs - - steps: - - uses: FranzDiebold/github-env-vars-action@v2.1.0 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: '16' - - - name: Checkout - uses: actions/checkout@v2 - - - name: Install dependencies - run: npm install - - - name: Test - run: npm test - - createTeamHandlerDeploy: - name: Create Team Handler Lambda Deploy - runs-on: ubuntu-latest - environment: - name: Development - defaults: - run: - working-directory: ./functions/clash-bot/team/create - permissions: - id-token: write - contents: read - needs: - - createTeamHandlerBuild - - steps: - - uses: FranzDiebold/github-env-vars-action@v2.1.0 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: '16' - - - name: Checkout - uses: actions/checkout@v2 - - - name: Install dependencies - run: npm install - - - name: Build - run: npm run build - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v3 - with: - role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser - aws-region: us-east-1 - - - name: Archive and Publish - run: | - zip -r ./create-team.zip ./prod ./node_modules ./package.json - aws s3 cp ./create-team.zip s3://${{ vars.S3_BUCKET_NAME }}/artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip - - - name: Upload Artifacts - uses: actions/upload-artifact@v2 - with: - name: create-team-${{ vars.ENVIRONMENT}}-${{ github.run_number }} - path: ./functions/clash-bot/team/create/create-team.zip - if-no-files-found: error + eventPublisher: + uses: ./.github/workflows/build_and_deploy_lambda.yml@eventPublisher + with: + working-directory: ./functions/clash-bot/event-publisher + artifact-name: event-publisher + s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} + environment: Development + aws-region: us-east-1 + + eventHandler: + uses: ./.github/workflows/build_and_deploy_lambda.yml@eventPublisher + with: + working-directory: ./functions/clash-bot/event-handler + artifact-name: event-handler + s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} + environment: Development + aws-region: us-east-1 + + createTeam: + uses: ./.github/workflows/build_and_deploy_lambda.yml@eventPublisher + with: + working-directory: ./functions/clash-bot/team/create + artifact-name: create-team + s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} + environment: Development + aws-region: us-east-1 + + retrieveTeams: + uses: ./.github/workflows/build_and_deploy_lambda.yml@eventPublisher + with: + working-directory: ./functions/clash-bot/team/retrieve + artifact-name: retrieve-teams + s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} + environment: Development + aws-region: us-east-1 terraformWorkflow: name: Workflow Apply @@ -330,9 +141,10 @@ jobs: contents: read pull-requests: write needs: - - eventPublisherDeploy - - eventHandlerDeploy - - createTeamHandlerDeploy + - eventHandler + - eventPublisher + - createTeam + - retrieveTeams steps: - uses: FranzDiebold/github-env-vars-action@v2.1.0 @@ -372,6 +184,7 @@ jobs: TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip TF_VAR_create_team_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip + TF_VAR_retrieve_teams_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/retrieve-teams.zip TF_VAR_sqs_batch_size: "1" run: terraform apply -no-color -input=false --auto-approve diff --git a/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace new file mode 100644 index 0000000..e21d5fe --- /dev/null +++ b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace @@ -0,0 +1,37 @@ +{ + "folders": [ + { + "path": "../../../.." + }, + { + "name": "clash-bot-shared", + "path": "../../clash-bot-shared" + }, + { + "name": "create", + "path": "../create" + }, + { + "name": "retrieve", + "path": "." + }, + { + "name": "event-publisher", + "path": "../../event-publisher" + }, + { + "name": "prod", + "path": "../../event-publisher/prod" + }, + { + "name": "event-handler", + "path": "../../event-handler" + } + ], + "settings": { + "jest.disabledWorkspaceFolders": [ + "ClashBotWorkflow", + "prod" + ] + } +} \ No newline at end of file diff --git a/functions/clash-bot/team/retrieve/jest.config.js b/functions/clash-bot/team/retrieve/jest.config.js new file mode 100644 index 0000000..85c664c --- /dev/null +++ b/functions/clash-bot/team/retrieve/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + transform: {'^.+\\.ts?$': 'ts-jest'}, + testEnvironment: 'node', + testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] + }; \ No newline at end of file diff --git a/functions/clash-bot/team/retrieve/package.json b/functions/clash-bot/team/retrieve/package.json new file mode 100644 index 0000000..680ee04 --- /dev/null +++ b/functions/clash-bot/team/retrieve/package.json @@ -0,0 +1,38 @@ +{ + "name": "clash-bot-retrieve-teams", + "version": "1.0.0", + "description": "A Lambda function to retrieve teams from DynamoDB for Clash Bot", + "main": "index.js", + "scripts": { + "test": "jest", + "build": "tsc", + "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + }, + "keywords": [ + "clash-bot" + ], + "author": "Poss111", + "license": "ISC", + "devDependencies": { + "@aws-sdk/types": "^3.398.0", + "@types/aws-lambda": "^8.10.119", + "@types/jest": "^29.5.4", + "@types/pino": "^7.0.5", + "@types/uuid": "^9.0.7", + "aws-sdk-client-mock": "^3.0.0", + "aws-sdk-client-mock-jest": "^3.0.0", + "jest": "^29.6.4", + "pino-pretty": "^10.2.0", + "ts-jest": "^29.1.1", + "ts-mockito": "^2.6.1", + "typescript": "^5.2.2" + }, + "dependencies": { + "@aws-sdk/client-dynamodb": "^3.405.0", + "@aws-sdk/util-dynamodb": "^3.451.0", + "clash-bot-shared": "github:Poss111/ClashBotWorkflowShared#main", + "dotenv": "^16.3.1", + "pino": "^8.15.0", + "uuid": "^9.0.1" + } +} diff --git a/functions/clash-bot/team/retrieve/src/handler.ts b/functions/clash-bot/team/retrieve/src/handler.ts new file mode 100644 index 0000000..c4e60d4 --- /dev/null +++ b/functions/clash-bot/team/retrieve/src/handler.ts @@ -0,0 +1,97 @@ +import { Handler } from 'aws-lambda'; +import pino, { P } from "pino"; +import { DynamoDBClient, QueryCommand, QueryCommandInput } from '@aws-sdk/client-dynamodb'; +import { v4 as uuidv4 } from 'uuid'; +import { Team, TeamFromJSON } from 'clash-bot-shared'; +import { unmarshall } from '@aws-sdk/util-dynamodb'; + +export const handler: Handler = async (event, context) => { + const level = process.env.LOGGER_LEVEL === undefined ? "info" : process.env.LOGGER_LEVEL; + const logger = pino({ level }); + + logger.info({ eventRecieved: event }, 'Recieved event...'); + logger.info({ contextRecieved: context } , 'Recieved context...'); + + let queryConditions: QueryCommandInput = { + TableName: process.env.TABLE_NAME, + KeyConditionExpression: 'type = :type', + ExpressionAttributeNames: { + "#type": "type" + }, + ExpressionAttributeValues: { + ":type": { + S: "Team" + }, + } + }; + + if (event.tournamentName && event.tournamentDay && event.serverId) { + logger.info("Filtering by tournament name '%s', tournament day '%s', and server id '%s'...", event.tournamentName, event.tournamentDay, event.serverId); + queryConditions = { + ...queryConditions, + FilterExpression: '#tournament.#tournamentName = :tournamentName AND #tournament.#tournamentDay = :tournamentDay AND #serverId = :serverId', + ExpressionAttributeNames: { + ...queryConditions.ExpressionAttributeNames, + "#tournament": "tournament", + "#tournamentName": "tournamentName", + "#tournamentDay": "tournamentDay", + "#serverId": "serverId" + }, + ExpressionAttributeValues: { + ...queryConditions.ExpressionAttributeValues, + ":tournamentName": { S: event.tournamentName }, + ":tournamentDay": { S: event.tournamentDay }, + ":serverId": { S: event.serverId } + } + } + } else if (event.tournamentName && event.tournamentDay) { + logger.info("Filtering by tournament name '%s' and tournament day '%s'...", event.tournamentName, event.tournamentDay); + queryConditions = { + ...queryConditions, + FilterExpression: '#tournament.#tournamentName = :tournamentName AND #tournament.#tournamentDay = :tournamentDay', + ExpressionAttributeNames: { + ...queryConditions.ExpressionAttributeNames, + "#tournament": "tournament", + "#tournamentName": "tournamentName", + "#tournamentDay": "tournamentDay" + }, + ExpressionAttributeValues: { + ...queryConditions.ExpressionAttributeValues, + ":tournamentName": { S: event.tournamentName }, + ":tournamentDay": { S: event.tournamentDay } + } + } + } else if (event.tournamentName) { + logger.info("Filtering by tournament name '%s'...", event.tournamentName); + queryConditions = { + ...queryConditions, + FilterExpression: '#tournament.#tournamentName = :tournamentName', + ExpressionAttributeNames: { + ...queryConditions.ExpressionAttributeNames, + "#tournament": "tournament", + "#tournamentName": "tournamentName" + }, + ExpressionAttributeValues: { + ...queryConditions.ExpressionAttributeValues, + ":tournamentName": { S: event.tournamentName } + } + } + } + + const dynamoDbClient = new DynamoDBClient({}); + const query = new QueryCommand(queryConditions); + + const results = await dynamoDbClient.send(query); + const items = results.Items ?? []; + + logger.info("Returned %d records from DynamoDb...", items.length); + logger.debug({ items }, "Retrieved items from DynamoDB..."); + + return items.map((item) => { + const unmarshalledItem = unmarshall(item); + return { + ...unmarshalledItem, + lastUpdatedAt: new Date(unmarshalledItem.lastUpdatedAt) + }; + }); +}; \ No newline at end of file diff --git a/functions/clash-bot/team/retrieve/tests/handler.test.ts b/functions/clash-bot/team/retrieve/tests/handler.test.ts new file mode 100644 index 0000000..ae044e1 --- /dev/null +++ b/functions/clash-bot/team/retrieve/tests/handler.test.ts @@ -0,0 +1,304 @@ +import { handler } from '../src/handler'; +import { DynamoDBClient, QueryCommand } from '@aws-sdk/client-dynamodb'; +import { Team, TeamFromJSON } from 'clash-bot-shared'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; +import { marshall } from '@aws-sdk/util-dynamodb'; + +describe('handler', () => { + test('If filtering ctiteria is not passed, should return a list of clash bot teams', async () => { + + const listOfExcpectedTeams: Team[] = [ + { + id: '1', + name: 'team1', + playerDetails: { + top: { + discordId: '1', + name: 'someUser' + } + }, + serverId: 'serverId', + tournament: { + tournamentName: 'tournamentName', + tournamentDay: '1' + }, + lastUpdatedAt: new Date() + }, + { + id: '2', + name: 'team2', + playerDetails: { + bot: { + discordId: '1', + name: 'someUser' + } + }, + serverId: 'serverId', + tournament: { + tournamentName: 'tournamentName', + tournamentDay: '1' + }, + lastUpdatedAt: new Date() + } + ]; + + const dynamodbMock = mockClient(DynamoDBClient); + + const items = listOfExcpectedTeams.map((team) => { + let marshalledTeam = { + ...team, + lastUpdatedAt: team.lastUpdatedAt?.toISOString() + } + return marshall(marshalledTeam); + }); + + dynamodbMock.on(QueryCommand).resolvesOnce({ + Items: items + }); + const results = await handler({}, {} as any, {} as any); + + expect(dynamodbMock).toHaveReceivedCommandWith(QueryCommand, { + TableName: process.env.TABLE_NAME, + KeyConditionExpression: 'type = :type', + ExpressionAttributeNames: { + "#type": "type" + }, + ExpressionAttributeValues: { + ":type": { + S: "Team" + } + } + }); + expect(results).toEqual(listOfExcpectedTeams); + }); + + test('If filtering ctiteria for tournament is passed, should return a list of clash bot teams for a tournament', async () => { + const tournamentName = 'tournamentName'; + const listOfExcpectedTeams: Team[] = [ + { + id: '1', + name: 'team1', + playerDetails: { + top: { + discordId: '1', + name: 'someUser' + } + }, + serverId: 'serverId', + tournament: { + tournamentName: tournamentName, + tournamentDay: '1' + }, + lastUpdatedAt: new Date() + }, + { + id: '2', + name: 'team2', + playerDetails: { + bot: { + discordId: '1', + name: 'someUser' + } + }, + serverId: 'serverId', + tournament: { + tournamentName: tournamentName, + tournamentDay: '1' + }, + lastUpdatedAt: new Date() + } + ]; + + const dynamodbMock = mockClient(DynamoDBClient); + + const items = listOfExcpectedTeams.map((team) => { + let marshalledTeam = { + ...team, + lastUpdatedAt: team.lastUpdatedAt?.toISOString() + } + return marshall(marshalledTeam); + }); + + dynamodbMock.on(QueryCommand).resolvesOnce({ + Items: items + }); + const results = await handler({ + tournamentName + }, {} as any, {} as any); + + expect(dynamodbMock).toHaveReceivedCommandWith(QueryCommand, { + TableName: process.env.TABLE_NAME, + KeyConditionExpression: 'type = :type', + FilterExpression: '#tournament.#tournamentName = :tournamentName', + ExpressionAttributeNames: { + "#type": "type", + "#tournament": "tournament", + "#tournamentName": "tournamentName" + }, + ExpressionAttributeValues: { + ":type": { + S: "Team" + }, + ":tournamentName": { S: tournamentName } + } + }); + expect(results).toEqual(listOfExcpectedTeams); + }); + + test('If filtering ctiteria for tournament and tournament day are passed, should return a list of clash bot teams for a tournament', async () => { + const tournamentName = 'tournamentName'; + const tournamentDay = '1'; + const listOfExcpectedTeams: Team[] = [ + { + id: '1', + name: 'team1', + playerDetails: { + top: { + discordId: '1', + name: 'someUser' + } + }, + serverId: 'serverId', + tournament: { + tournamentName: tournamentName, + tournamentDay: tournamentDay + }, + lastUpdatedAt: new Date() + }, + { + id: '2', + name: 'team2', + playerDetails: { + bot: { + discordId: '1', + name: 'someUser' + } + }, + serverId: 'serverId', + tournament: { + tournamentName: tournamentName, + tournamentDay: tournamentDay + }, + lastUpdatedAt: new Date() + } + ]; + + const dynamodbMock = mockClient(DynamoDBClient); + + const items = listOfExcpectedTeams.map((team) => { + let marshalledTeam = { + ...team, + lastUpdatedAt: team.lastUpdatedAt?.toISOString() + } + return marshall(marshalledTeam); + }); + + dynamodbMock.on(QueryCommand).resolvesOnce({ + Items: items + }); + const results = await handler({ + tournamentName, + tournamentDay + }, {} as any, {} as any); + + expect(dynamodbMock).toHaveReceivedCommandWith(QueryCommand, { + TableName: process.env.TABLE_NAME, + KeyConditionExpression: 'type = :type', + FilterExpression: '#tournament.#tournamentName = :tournamentName AND #tournament.#tournamentDay = :tournamentDay', + ExpressionAttributeNames: { + "#type": "type", + "#tournament": "tournament", + "#tournamentName": "tournamentName", + "#tournamentDay": "tournamentDay" + }, + ExpressionAttributeValues: { + ":type": { + S: "Team" + }, + ":tournamentName": { S: tournamentName }, + ":tournamentDay": { S: tournamentDay } + }}); + expect(results).toEqual(listOfExcpectedTeams); + }); + + test('If filtering ctiteria for tournament, tournament day, and server id are passed, should return a list of clash bot teams for a tournament, tournament id and server id', async () => { + const tournamentName = 'tournamentName'; + const tournamentDay = '1'; + const serverId = 'serverId'; + const listOfExcpectedTeams: Team[] = [ + { + id: '1', + name: 'team1', + playerDetails: { + top: { + discordId: '1', + name: 'someUser' + } + }, + serverId: serverId, + tournament: { + tournamentName: tournamentName, + tournamentDay: tournamentDay + }, + lastUpdatedAt: new Date() + }, + { + id: '2', + name: 'team2', + playerDetails: { + bot: { + discordId: '1', + name: 'someUser' + } + }, + serverId: serverId, + tournament: { + tournamentName: tournamentName, + tournamentDay: tournamentDay + }, + lastUpdatedAt: new Date() + } + ]; + + const dynamodbMock = mockClient(DynamoDBClient); + + const items = listOfExcpectedTeams.map((team) => { + let marshalledTeam = { + ...team, + lastUpdatedAt: team.lastUpdatedAt?.toISOString() + } + return marshall(marshalledTeam); + }); + + dynamodbMock.on(QueryCommand).resolvesOnce({ + Items: items + }); + const results = await handler({ + tournamentName, + tournamentDay, + serverId + }, {} as any, {} as any); + expect(dynamodbMock).toHaveReceivedCommandWith(QueryCommand, { + TableName: process.env.TABLE_NAME, + KeyConditionExpression: 'type = :type', + FilterExpression: '#tournament.#tournamentName = :tournamentName AND #tournament.#tournamentDay = :tournamentDay AND #serverId = :serverId', + ExpressionAttributeNames: { + "#type": "type", + "#tournament": "tournament", + "#tournamentName": "tournamentName", + "#tournamentDay": "tournamentDay", + "#serverId": "serverId" + }, + ExpressionAttributeValues: { + ":type": { + S: "Team" + }, + ":tournamentName": { S: tournamentName }, + ":tournamentDay": { S: tournamentDay }, + ":serverId": { S: serverId } + } + }); + expect(results).toEqual(listOfExcpectedTeams); + }); +}); \ No newline at end of file diff --git a/functions/clash-bot/team/retrieve/tsconfig.json b/functions/clash-bot/team/retrieve/tsconfig.json new file mode 100644 index 0000000..62859a2 --- /dev/null +++ b/functions/clash-bot/team/retrieve/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es2020", + "strict": true, + "preserveConstEnums": true, + "noEmit": false, + "sourceMap": false, + "module":"commonjs", + "moduleResolution":"node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "rootDir": "./src", + "outDir": "prod", + "baseUrl": ".", + "paths": { + "clash-bot-shared": [ "node_modules/clash-bot-shared/src"], + "clash-bot-shared/*": [ "node_modules/clash-bot-shared/src/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.test.ts"], + } \ No newline at end of file diff --git a/terraform/workflow/retrieve-teams-lambda.tf b/terraform/workflow/retrieve-teams-lambda.tf new file mode 100644 index 0000000..8fbbdb4 --- /dev/null +++ b/terraform/workflow/retrieve-teams-lambda.tf @@ -0,0 +1,71 @@ +resource "aws_lambda_function" "retrieve_team_lambda" { + function_name = "clash-bot-retrieve-team-${lower(var.environment)}" + handler = "prod/handler.handler" + runtime = "nodejs16.x" + role = aws_iam_role.retrieve_team_lambda_exec.arn + + s3_bucket = var.s3_bucket_name + s3_key = var.retrieve_team_artifact_path + + environment { + variables = { + TABLE_NAME = module.dynamodb_table.dynamodb_table_id + } + } +} + +resource "aws_iam_role" "retrieve_team_lambda_exec" { + name = "clash_bot_retrieve_team_exec_role-${lower(var.environment)}" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" + } + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "retrieve_team_lambda_exec_policy" { + role = aws_iam_role.retrieve_team_lambda_exec.name + policy_arn = aws_iam_policy.retrieve_team_policy.arn +} + +data "aws_iam_policy_document" "retrieve_team_policy_document" { + statement { + effect = "Allow" + actions = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + resources = ["*"] + } + + statement { + effect = "Allow" + actions = [ + "dynamodb:GetItem", + "dynamodb:BatchGetItem", + "dynamodb:Query", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem" + ] + resources = [ + module.dynamodb_table.dynamodb_table_arn + ] + } +} + +resource "aws_iam_policy" "retrieve_team_policy" { + name = "ClashBotWorkflowRetrieveTeamPolicy-${lower(var.environment)}" + description = "Policy for the retrieve team lambda function." + policy = data.aws_iam_policy_document.retrieve_team_policy_document.json +} \ No newline at end of file diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index bbce68e..088b8ba 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -3,7 +3,8 @@ module "create_team_step_function" { name = "create-team-${var.environment}" definition = templatefile("${path.module}/step-functions/create-team-step-function.asl.json", { - CreateTeamLambdaFunctionArn = aws_lambda_function.create_team_lambda.arn + CreateTeamLambdaFunctionArn = aws_lambda_function.create_team_lambda.arn, + RetrieveTeamLambdaFunctionArn = aws_lambda_function.retrieve_team_lambda.arn, } ) diff --git a/terraform/workflow/step-functions/create-team-step-function.asl.json b/terraform/workflow/step-functions/create-team-step-function.asl.json index aa2be08..1a4d77b 100644 --- a/terraform/workflow/step-functions/create-team-step-function.asl.json +++ b/terraform/workflow/step-functions/create-team-step-function.asl.json @@ -1,7 +1,42 @@ { "Comment": "Creates a Clash Bot team for a Discord Service if the user is able to.", - "StartAt": "CreateTeam", + "StartAt": "RetrieveTeamsForTournament", "States": { + "RetrieveTeamsForTournament": { + "Type": "Task", + "Resource": "arn:aws:states:::lambda:invoke", + "Parameters": { + "Payload": { + "tournamantName": "$.Payload.tournament.tournamentName", + "tournamentDay": "$.Payload.tournament.tournamentDay" + }, + "FunctionName": "${RetrieveTeamsForTournamentLambdaFunctionArn}" + }, + "OutputPath": "$.Payload", + "Next": "CanTeamBeCreated", + "Catch": [ + { + "ErrorEquals": ["States.ALL"], + "Next": "DefaultErrorHandler" + } + ] + }, + "CanTeamBeCreated": { + "Type": "Choice", + "Choices": [ + { + "Variable": "$.teamCreated", + "BooleanEquals": true, + "Next": "TeamAlreadyCreated" + } + ], + "Default": "CreateTeam" + }, + "TeamAlreadyCreated": { + "Type": "Pass", + "Result": "Team already created", + "End": true + }, "CreateTeam": { "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", @@ -10,6 +45,17 @@ "Payload.$": "$", "FunctionName": "${CreateTeamLambdaFunctionArn}" }, + "Catch": [ + { + "ErrorEquals": ["States.ALL"], + "Next": "DefaultErrorHandler" + } + ], + "End": true + }, + "DefaultErrorHandler": { + "Type": "Pass", + "Result": "An error occurred", "End": true } } diff --git a/terraform/workflow/variables.tf b/terraform/workflow/variables.tf index 644e85e..f4e27f3 100644 --- a/terraform/workflow/variables.tf +++ b/terraform/workflow/variables.tf @@ -30,6 +30,11 @@ variable "create_team_artifact_path" { description = "Path to the artifact for the create team lambda function." } +variable "retrieve_team_artifact_path" { + type = string + description = "Path to the artifact for the retrieve team lambda function." +} + variable "sqs_batch_size" { type = number default = 1 From ac8c966727f25ac084a5b3a895a345bed0354610 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 14:37:23 -0600 Subject: [PATCH 032/119] Removing version for build and deploy lambda workflow --- .github/workflows/pull-request.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 9be37b1..6fef12e 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -93,7 +93,7 @@ jobs: }) eventPublisher: - uses: ./.github/workflows/build_and_deploy_lambda.yml@eventPublisher + uses: ./.github/workflows/build_and_deploy_lambda.yml with: working-directory: ./functions/clash-bot/event-publisher artifact-name: event-publisher @@ -102,7 +102,7 @@ jobs: aws-region: us-east-1 eventHandler: - uses: ./.github/workflows/build_and_deploy_lambda.yml@eventPublisher + uses: ./.github/workflows/build_and_deploy_lambda.yml with: working-directory: ./functions/clash-bot/event-handler artifact-name: event-handler @@ -111,7 +111,7 @@ jobs: aws-region: us-east-1 createTeam: - uses: ./.github/workflows/build_and_deploy_lambda.yml@eventPublisher + uses: ./.github/workflows/build_and_deploy_lambda.yml with: working-directory: ./functions/clash-bot/team/create artifact-name: create-team @@ -120,7 +120,7 @@ jobs: aws-region: us-east-1 retrieveTeams: - uses: ./.github/workflows/build_and_deploy_lambda.yml@eventPublisher + uses: ./.github/workflows/build_and_deploy_lambda.yml with: working-directory: ./functions/clash-bot/team/retrieve artifact-name: retrieve-teams From 215969983e659dccfd908b94bc2bd29ce99f4af6 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 14:49:32 -0600 Subject: [PATCH 033/119] Hopefully fixing input of reusable workflow --- .github/workflows/pull-request.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 6fef12e..ccad056 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -98,8 +98,10 @@ jobs: working-directory: ./functions/clash-bot/event-publisher artifact-name: event-publisher s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} - environment: Development + environment-name: Development aws-region: us-east-1 + needs: + - terraformPreReqs eventHandler: uses: ./.github/workflows/build_and_deploy_lambda.yml @@ -107,8 +109,10 @@ jobs: working-directory: ./functions/clash-bot/event-handler artifact-name: event-handler s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} - environment: Development + environment-name: Development aws-region: us-east-1 + needs: + - terraformPreReqs createTeam: uses: ./.github/workflows/build_and_deploy_lambda.yml @@ -116,8 +120,10 @@ jobs: working-directory: ./functions/clash-bot/team/create artifact-name: create-team s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} - environment: Development + environment-name: Development aws-region: us-east-1 + needs: + - terraformPreReqs retrieveTeams: uses: ./.github/workflows/build_and_deploy_lambda.yml @@ -125,8 +131,10 @@ jobs: working-directory: ./functions/clash-bot/team/retrieve artifact-name: retrieve-teams s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} - environment: Development + environment-name: Development aws-region: us-east-1 + needs: + - terraformPreReqs terraformWorkflow: name: Workflow Apply From 89d4deb65795693eb9b251c1fbd7ecee9ef2da85 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 14:51:36 -0600 Subject: [PATCH 034/119] Fixing again... --- .github/workflows/pull-request.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index ccad056..7b17386 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -99,7 +99,7 @@ jobs: artifact-name: event-publisher s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} environment-name: Development - aws-region: us-east-1 + region: us-east-1 needs: - terraformPreReqs @@ -110,7 +110,7 @@ jobs: artifact-name: event-handler s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} environment-name: Development - aws-region: us-east-1 + region: us-east-1 needs: - terraformPreReqs @@ -121,7 +121,7 @@ jobs: artifact-name: create-team s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} environment-name: Development - aws-region: us-east-1 + region: us-east-1 needs: - terraformPreReqs @@ -132,7 +132,7 @@ jobs: artifact-name: retrieve-teams s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} environment-name: Development - aws-region: us-east-1 + region: us-east-1 needs: - terraformPreReqs From a32617d2226c8c6c06d04c4f1a5d85d9f6d02251 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 14:54:07 -0600 Subject: [PATCH 035/119] Fixing path to functions --- .github/workflows/pull-request.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 7b17386..afda3ad 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -95,7 +95,7 @@ jobs: eventPublisher: uses: ./.github/workflows/build_and_deploy_lambda.yml with: - working-directory: ./functions/clash-bot/event-publisher + working-directory: functions/clash-bot/event-publisher artifact-name: event-publisher s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} environment-name: Development @@ -106,7 +106,7 @@ jobs: eventHandler: uses: ./.github/workflows/build_and_deploy_lambda.yml with: - working-directory: ./functions/clash-bot/event-handler + working-directory: functions/clash-bot/event-handler artifact-name: event-handler s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} environment-name: Development @@ -117,7 +117,7 @@ jobs: createTeam: uses: ./.github/workflows/build_and_deploy_lambda.yml with: - working-directory: ./functions/clash-bot/team/create + working-directory: functions/clash-bot/team/create artifact-name: create-team s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} environment-name: Development @@ -128,7 +128,7 @@ jobs: retrieveTeams: uses: ./.github/workflows/build_and_deploy_lambda.yml with: - working-directory: ./functions/clash-bot/team/retrieve + working-directory: functions/clash-bot/team/retrieve artifact-name: retrieve-teams s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} environment-name: Development From 714324539644fb1370ddd32d1d69d16d1cc496de Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 15:03:20 -0600 Subject: [PATCH 036/119] Updating reusable lambda build --- .github/workflows/build_and_deploy_lambda.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 979d5b8..349566c 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -51,6 +51,9 @@ jobs: - name: Test run: npm test + - name: Build Artifact + run: npm run build + - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v3 with: @@ -59,7 +62,8 @@ jobs: - name: Archive and Publish run: | - zip -r ./${{ inputs.artifact-name}}.zip ./prod ./node_modules ./package.json + zip -r ./${{ inputs.artifact-name}}.zip ./prod ./package.json + ls -lha aws s3 cp ./event-publisher.zip s3://${{ inputs.s3-bucket-name }}/artifacts/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/${{ github.run_number }}/${{ inputs.artifact-name}}.zip - name: Upload Artifacts From 9293b2d2bfb336ebb615012ab24584c4756c165f Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 15:06:13 -0600 Subject: [PATCH 037/119] Fixing parameterized artifact name --- .github/workflows/build_and_deploy_lambda.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 349566c..4f20db2 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -63,8 +63,7 @@ jobs: - name: Archive and Publish run: | zip -r ./${{ inputs.artifact-name}}.zip ./prod ./package.json - ls -lha - aws s3 cp ./event-publisher.zip s3://${{ inputs.s3-bucket-name }}/artifacts/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/${{ github.run_number }}/${{ inputs.artifact-name}}.zip + aws s3 cp ./${{ inputs.artifact-name}}.zip s3://${{ inputs.s3-bucket-name }}/artifacts/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/${{ github.run_number }}/${{ inputs.artifact-name}}.zip - name: Upload Artifacts uses: actions/upload-artifact@v2 From 0f6da7d586f1a6d604ceec076754d5655338dc36 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 15:09:55 -0600 Subject: [PATCH 038/119] Formatting terraform code --- .../retrieve/ClashBotWorkflow.code-workspace | 24 ------------------- terraform/workflow/step-functions.lambda.tf | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace index e21d5fe..1dd8d29 100644 --- a/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace +++ b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace @@ -2,30 +2,6 @@ "folders": [ { "path": "../../../.." - }, - { - "name": "clash-bot-shared", - "path": "../../clash-bot-shared" - }, - { - "name": "create", - "path": "../create" - }, - { - "name": "retrieve", - "path": "." - }, - { - "name": "event-publisher", - "path": "../../event-publisher" - }, - { - "name": "prod", - "path": "../../event-publisher/prod" - }, - { - "name": "event-handler", - "path": "../../event-handler" } ], "settings": { diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 088b8ba..957f64e 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -3,7 +3,7 @@ module "create_team_step_function" { name = "create-team-${var.environment}" definition = templatefile("${path.module}/step-functions/create-team-step-function.asl.json", { - CreateTeamLambdaFunctionArn = aws_lambda_function.create_team_lambda.arn, + CreateTeamLambdaFunctionArn = aws_lambda_function.create_team_lambda.arn, RetrieveTeamLambdaFunctionArn = aws_lambda_function.retrieve_team_lambda.arn, } ) From ec4fa81a63b1f46e5a03df700dafa8f5b46c011d Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 15:14:11 -0600 Subject: [PATCH 039/119] Fixing step function definition --- .../workflow/step-functions/create-team-step-function.asl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/step-functions/create-team-step-function.asl.json b/terraform/workflow/step-functions/create-team-step-function.asl.json index 1a4d77b..b3afa56 100644 --- a/terraform/workflow/step-functions/create-team-step-function.asl.json +++ b/terraform/workflow/step-functions/create-team-step-function.asl.json @@ -10,7 +10,7 @@ "tournamantName": "$.Payload.tournament.tournamentName", "tournamentDay": "$.Payload.tournament.tournamentDay" }, - "FunctionName": "${RetrieveTeamsForTournamentLambdaFunctionArn}" + "FunctionName": "${RetrieveTeamLambdaFunctionArn}" }, "OutputPath": "$.Payload", "Next": "CanTeamBeCreated", From 43eb7e0388ad54a33bdc796b03f46fd8129044e9 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 15:17:55 -0600 Subject: [PATCH 040/119] Adding variables --- .github/workflows/build_and_deploy_lambda.yml | 2 +- terraform/workflow/variables.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 4f20db2..cc671e5 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -68,6 +68,6 @@ jobs: - name: Upload Artifacts uses: actions/upload-artifact@v2 with: - name: event-publisher-${{ inputs.environment-name }}-${{ github.run_number }} + name: ${{ inputs.artifact-name }}-${{ inputs.environment-name }}-${{ github.run_number }} path: ${{ inputs.working-directory}}/${{ inputs.artifact-name }}.zip if-no-files-found: error diff --git a/terraform/workflow/variables.tf b/terraform/workflow/variables.tf index f4e27f3..7c8e98a 100644 --- a/terraform/workflow/variables.tf +++ b/terraform/workflow/variables.tf @@ -30,7 +30,7 @@ variable "create_team_artifact_path" { description = "Path to the artifact for the create team lambda function." } -variable "retrieve_team_artifact_path" { +variable "retrieve_teams_artifact_path" { type = string description = "Path to the artifact for the retrieve team lambda function." } From fd3db24ad3dfcc0fd8f9e92537efd9f6dda500b2 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 15:23:06 -0600 Subject: [PATCH 041/119] Fixing artifact path --- terraform/workflow/retrieve-teams-lambda.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/retrieve-teams-lambda.tf b/terraform/workflow/retrieve-teams-lambda.tf index 8fbbdb4..e82b344 100644 --- a/terraform/workflow/retrieve-teams-lambda.tf +++ b/terraform/workflow/retrieve-teams-lambda.tf @@ -5,7 +5,7 @@ resource "aws_lambda_function" "retrieve_team_lambda" { role = aws_iam_role.retrieve_team_lambda_exec.arn s3_bucket = var.s3_bucket_name - s3_key = var.retrieve_team_artifact_path + s3_key = var.retrieve_teams_artifact_path environment { variables = { From 8bed3e935ae24b7e4c611c6de5141bf8552ada37 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 17:45:18 -0600 Subject: [PATCH 042/119] Creating lambda module --- .github/workflows/pull-request.yml | 13 ++ .../is-tournament-eligible/jest.config.js | 6 + .../team/is-tournament-eligible/package.json | 36 +++++ .../is-tournament-eligible/src/handler.ts | 92 ++++++++++++ .../tests/handler.test.ts | 136 ++++++++++++++++++ .../team/is-tournament-eligible/tsconfig.json | 24 ++++ .../retrieve/ClashBotWorkflow.code-workspace | 28 ++++ terraform/workflow/create-team-lambda.tf | 71 --------- terraform/workflow/event-handler-lambda.tf | 89 ------------ terraform/workflow/event-publisher-lambda.tf | 76 ---------- terraform/workflow/modules/lambda/lambda.tf | 41 ++++++ terraform/workflow/modules/lambda/outputs.tf | 9 ++ .../workflow/modules/lambda/variables.tf | 29 ++++ .../policies/create-team-lambda-policy.json | 27 ++++ .../policies/event-handler-lambda-policy.json | 28 ++++ .../event-publisher-lambda-policy.json | 19 +++ .../retrieve-teams-lambda-policy.json | 27 ++++ .../tournament-eligibility-lambda-policy.json | 27 ++++ terraform/workflow/retrieve-teams-lambda.tf | 71 --------- terraform/workflow/step-functions.lambda.tf | 127 +++++++++++++++- terraform/workflow/variables.tf | 5 + 21 files changed, 672 insertions(+), 309 deletions(-) create mode 100644 functions/clash-bot/team/is-tournament-eligible/jest.config.js create mode 100644 functions/clash-bot/team/is-tournament-eligible/package.json create mode 100644 functions/clash-bot/team/is-tournament-eligible/src/handler.ts create mode 100644 functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts create mode 100644 functions/clash-bot/team/is-tournament-eligible/tsconfig.json delete mode 100644 terraform/workflow/create-team-lambda.tf delete mode 100644 terraform/workflow/event-handler-lambda.tf delete mode 100644 terraform/workflow/event-publisher-lambda.tf create mode 100644 terraform/workflow/modules/lambda/lambda.tf create mode 100644 terraform/workflow/modules/lambda/outputs.tf create mode 100644 terraform/workflow/modules/lambda/variables.tf create mode 100644 terraform/workflow/policies/create-team-lambda-policy.json create mode 100644 terraform/workflow/policies/event-handler-lambda-policy.json create mode 100644 terraform/workflow/policies/event-publisher-lambda-policy.json create mode 100644 terraform/workflow/policies/retrieve-teams-lambda-policy.json create mode 100644 terraform/workflow/policies/tournament-eligibility-lambda-policy.json delete mode 100644 terraform/workflow/retrieve-teams-lambda.tf diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index afda3ad..98955cd 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -136,6 +136,17 @@ jobs: needs: - terraformPreReqs + isTournamentEligible: + uses: ./.github/workflows/build_and_deploy_lambda.yml + with: + working-directory: functions/clash-bot/team/is-tournament-eligible + artifact-name: is-tournament-eligible + s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} + environment-name: Development + region: us-east-1 + needs: + - terraformPreReqs + terraformWorkflow: name: Workflow Apply runs-on: ubuntu-latest @@ -153,6 +164,7 @@ jobs: - eventPublisher - createTeam - retrieveTeams + - isTournamentEligible steps: - uses: FranzDiebold/github-env-vars-action@v2.1.0 @@ -193,6 +205,7 @@ jobs: TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip TF_VAR_create_team_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip TF_VAR_retrieve_teams_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/retrieve-teams.zip + TF_VAR_tournament_eligibility_lambda_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/is-tournament-eligible.zip TF_VAR_sqs_batch_size: "1" run: terraform apply -no-color -input=false --auto-approve diff --git a/functions/clash-bot/team/is-tournament-eligible/jest.config.js b/functions/clash-bot/team/is-tournament-eligible/jest.config.js new file mode 100644 index 0000000..85c664c --- /dev/null +++ b/functions/clash-bot/team/is-tournament-eligible/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + transform: {'^.+\\.ts?$': 'ts-jest'}, + testEnvironment: 'node', + testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] + }; \ No newline at end of file diff --git a/functions/clash-bot/team/is-tournament-eligible/package.json b/functions/clash-bot/team/is-tournament-eligible/package.json new file mode 100644 index 0000000..31f7789 --- /dev/null +++ b/functions/clash-bot/team/is-tournament-eligible/package.json @@ -0,0 +1,36 @@ +{ + "name": "clash-bot-is-tournament-eligible", + "version": "1.0.0", + "description": "Checks if a player is eligible to participate in a tournament", + "main": "index.js", + "scripts": { + "test": "jest", + "build": "tsc", + "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + }, + "keywords": [ + "clash-bot" + ], + "author": "Poss111", + "license": "ISC", + "devDependencies": { + "@aws-sdk/types": "^3.398.0", + "@types/aws-lambda": "^8.10.119", + "@types/jest": "^29.5.4", + "@types/pino": "^7.0.5", + "aws-sdk-client-mock": "^3.0.0", + "aws-sdk-client-mock-jest": "^3.0.0", + "jest": "^29.6.4", + "pino-pretty": "^10.2.0", + "ts-jest": "^29.1.1", + "ts-mockito": "^2.6.1", + "typescript": "^5.2.2" + }, + "dependencies": { + "@aws-sdk/client-dynamodb": "^3.405.0", + "@aws-sdk/util-dynamodb": "^3.451.0", + "clash-bot-shared": "github:Poss111/ClashBotWorkflowShared#main", + "dotenv": "^16.3.1", + "pino": "^8.15.0" + } +} diff --git a/functions/clash-bot/team/is-tournament-eligible/src/handler.ts b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts new file mode 100644 index 0000000..748e3c8 --- /dev/null +++ b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts @@ -0,0 +1,92 @@ +import { Handler } from 'aws-lambda'; +import pino from "pino"; +import { DynamoDBClient, QueryCommand, QueryCommandInput } from '@aws-sdk/client-dynamodb'; +import { unmarshall } from '@aws-sdk/util-dynamodb'; + +export const handler: Handler = async (event, context) => { + const level = process.env.LOGGER_LEVEL === undefined ? "info" : process.env.LOGGER_LEVEL; + const logger = pino({ level }); + + logger.info({ eventRecieved: event }, 'Recieved event...'); + logger.info({ contextRecieved: context } , 'Recieved context...'); + + if (event.tournament === undefined && event.tournamentDay === undefined) { + logger.info('No tournament or tournament day provided.'); + return false; + } + + const dynamoDBClient = new DynamoDBClient({}); + + let queryConditions: QueryCommandInput = { + TableName: process.env.TABLE_NAME, + KeyConditionExpression: '#type = :type', + ExpressionAttributeNames: { + "#type": "type" + }, + ExpressionAttributeValues: { + ":type": { + S: "Tournament" + }, + } + }; + + if (event.tournament !== undefined && event.tournamentDay !== undefined) { + logger.info({ tournament: event.tournament, tournamentDay: event.tournamentDay }, 'Tournament and tournament day provided...'); + queryConditions = { + ...queryConditions, + FilterExpression: '#tournament = :tournament AND #tournamentDay = :tournamentDay', + ExpressionAttributeNames: { + ...queryConditions.ExpressionAttributeNames, + "#tournament": "tournament", + "#tournamentDay": "tournamentDay" + }, + ExpressionAttributeValues: { + ...queryConditions.ExpressionAttributeValues, + ":tournament": { + S: event.tournament + }, + ":tournamentDay": { + S: event.tournamentDay + } + } + }; + } else if (event.tournament !== undefined) { + logger.info({ tournament: event.tournament }, 'Tournament provided...') + queryConditions = { + ...queryConditions, + FilterExpression: '#tournament = :tournament', + ExpressionAttributeNames: { + ...queryConditions.ExpressionAttributeNames, + "#tournament": "tournament" + }, + ExpressionAttributeValues: { + ...queryConditions.ExpressionAttributeValues, + ":tournament": { + S: event.tournament + } + } + }; + } + + const queryCommand = new QueryCommand(queryConditions); + + const queryCommandOutput = await dynamoDBClient.send(queryCommand); + + if (queryCommandOutput.Items === undefined || queryCommandOutput.Items.length === 0) { + return false; + } else { + const currentDate = new Date(); + currentDate.setHours(0, 0, 0, 0); + const tournaments = queryCommandOutput.Items + .map(item => unmarshall(item)) + .filter(tournament => { + const dayOfDateOfTournament = new Date(tournament.date); + dayOfDateOfTournament.setHours(0, 0, 0, 0); + const tournamentIsEligible = dayOfDateOfTournament >= currentDate; + logger.info({ ...tournament, tournamentIsEligible, currentDate }, 'Tournament eligibility...'); + return tournamentIsEligible; + }); + + return tournaments.length > 0; + } +}; \ No newline at end of file diff --git a/functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts b/functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts new file mode 100644 index 0000000..64cad8d --- /dev/null +++ b/functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts @@ -0,0 +1,136 @@ +import { handler } from '../src/handler'; +import { DynamoDBClient, QueryCommand } from '@aws-sdk/client-dynamodb'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; +import { marshall } from '@aws-sdk/util-dynamodb'; + +describe('handler', () => { + test('If the tournament is after the current date, then respond with true', async () => { + const tournamentName = 'Clash Tournament'; + const date = new Date(); + date.setHours(1, 0, 0, 0); + const mock = mockClient(DynamoDBClient); + const mockQueryCommand = jest.fn(); + mockQueryCommand.mockResolvedValue({ + Items: [ + marshall({ + type: 'Tournament', + tournament: tournamentName, + date: date.toISOString() + }) + ] + }); + mock.on(QueryCommand).resolves(mockQueryCommand()); + + const result = await handler({ + tournament: tournamentName + }, {} as any, {} as any); + + expect(result).toBe(true); + expect(mock).toHaveReceivedCommandWith(QueryCommand, { + TableName: process.env.TABLE_NAME, + KeyConditionExpression: '#type = :type', + FilterExpression: '#tournament = :tournament', + ExpressionAttributeNames: { + '#type': 'type', + '#tournament': 'tournament' + }, + ExpressionAttributeValues: { + ':type': { S: 'Tournament' }, + ':tournament': { S: tournamentName } + } + }); + }); + + test('If the tournament and tournament day are passed and is after the current date, then respond with true', async () => { + const tournamentName = 'Clash Tournament'; + const tournamentDay = '1'; + const date = new Date(); + date.setHours(1, 0, 0, 0); + const mock = mockClient(DynamoDBClient); + const mockQueryCommand = jest.fn(); + mockQueryCommand.mockResolvedValue({ + Items: [ + marshall({ + type: 'Tournament', + tournament: tournamentName, + tournamentDay, + date: date.toISOString() + }) + ] + }); + mock.on(QueryCommand).resolves(mockQueryCommand()); + + const result = await handler({ + tournament: tournamentName, + tournamentDay + }, {} as any, {} as any); + + expect(result).toBe(true); + expect(mock).toHaveReceivedCommandWith(QueryCommand, { + TableName: process.env.TABLE_NAME, + KeyConditionExpression: '#type = :type', + FilterExpression: '#tournament = :tournament AND #tournamentDay = :tournamentDay', + ExpressionAttributeNames: { + '#type': 'type', + '#tournament': 'tournament', + '#tournamentDay': 'tournamentDay' + }, + ExpressionAttributeValues: { + ':type': { S: 'Tournament' }, + ':tournament': { S: tournamentName }, + ':tournamentDay': { S: tournamentDay } + } + }); + }); + + test('If multiple days are returned, check both days', async () => { + const tournamentName = 'Clash Tournament'; + const tournamentDay = '1'; + const date = new Date(); + date.setHours(1, 0, 0, 0); + const priorDate = new Date(); + priorDate.setHours(-1, 0, 0, 0); + const mock = mockClient(DynamoDBClient); + const mockQueryCommand = jest.fn(); + mockQueryCommand.mockResolvedValue({ + Items: [ + marshall({ + type: 'Tournament', + tournament: tournamentName, + tournamentDay, + date: priorDate.toISOString() + }), + marshall({ + type: 'Tournament', + tournament: tournamentName, + tournamentDay: '2', + date: date.toISOString() + }) + ] + }); + mock.on(QueryCommand).resolves(mockQueryCommand()); + + const result = await handler({ + tournament: tournamentName, + tournamentDay + }, {} as any, {} as any); + + expect(result).toBe(true); + expect(mock).toHaveReceivedCommandWith(QueryCommand, { + TableName: process.env.TABLE_NAME, + KeyConditionExpression: '#type = :type', + FilterExpression: '#tournament = :tournament AND #tournamentDay = :tournamentDay', + ExpressionAttributeNames: { + '#type': 'type', + '#tournament': 'tournament', + '#tournamentDay': 'tournamentDay' + }, + ExpressionAttributeValues: { + ':type': { S: 'Tournament' }, + ':tournament': { S: tournamentName }, + ':tournamentDay': { S: tournamentDay } + } + }); + }); +}); \ No newline at end of file diff --git a/functions/clash-bot/team/is-tournament-eligible/tsconfig.json b/functions/clash-bot/team/is-tournament-eligible/tsconfig.json new file mode 100644 index 0000000..62859a2 --- /dev/null +++ b/functions/clash-bot/team/is-tournament-eligible/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es2020", + "strict": true, + "preserveConstEnums": true, + "noEmit": false, + "sourceMap": false, + "module":"commonjs", + "moduleResolution":"node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "rootDir": "./src", + "outDir": "prod", + "baseUrl": ".", + "paths": { + "clash-bot-shared": [ "node_modules/clash-bot-shared/src"], + "clash-bot-shared/*": [ "node_modules/clash-bot-shared/src/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.test.ts"], + } \ No newline at end of file diff --git a/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace index 1dd8d29..c4595d3 100644 --- a/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace +++ b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace @@ -2,6 +2,34 @@ "folders": [ { "path": "../../../.." + }, + { + "name": "clash-bot-shared", + "path": "../../clash-bot-shared" + }, + { + "name": "event-publisher", + "path": "../../event-publisher" + }, + { + "name": "create", + "path": "../create" + }, + { + "name": "retrieve", + "path": "." + }, + { + "name": "can-create-team", + "path": "../can-create-team" + }, + { + "name": "event-handler", + "path": "../../event-handler" + }, + { + "name": "prod", + "path": "../../event-publisher/prod" } ], "settings": { diff --git a/terraform/workflow/create-team-lambda.tf b/terraform/workflow/create-team-lambda.tf deleted file mode 100644 index afec580..0000000 --- a/terraform/workflow/create-team-lambda.tf +++ /dev/null @@ -1,71 +0,0 @@ -resource "aws_lambda_function" "create_team_lambda" { - function_name = "clash-bot-create-team-${lower(var.environment)}" - handler = "prod/handler.handler" - runtime = "nodejs16.x" - role = aws_iam_role.create_team_lambda_exec.arn - - s3_bucket = var.s3_bucket_name - s3_key = var.create_team_artifact_path - - environment { - variables = { - TABLE_NAME = module.dynamodb_table.dynamodb_table_id - } - } -} - -resource "aws_iam_role" "create_team_lambda_exec" { - name = "clash_bot_create_team_exec_role-${lower(var.environment)}" - - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "lambda.amazonaws.com" - } - } - ] - }) -} - -resource "aws_iam_role_policy_attachment" "create_team_lambda_exec_policy" { - role = aws_iam_role.create_team_lambda_exec.name - policy_arn = aws_iam_policy.create_team_policy.arn -} - -data "aws_iam_policy_document" "create_team_policy_document" { - statement { - effect = "Allow" - actions = [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ] - resources = ["*"] - } - - statement { - effect = "Allow" - actions = [ - "dynamodb:GetItem", - "dynamodb:BatchGetItem", - "dynamodb:Query", - "dynamodb:PutItem", - "dynamodb:UpdateItem", - "dynamodb:DeleteItem", - "dynamodb:BatchWriteItem" - ] - resources = [ - module.dynamodb_table.dynamodb_table_arn - ] - } -} - -resource "aws_iam_policy" "create_team_policy" { - name = "ClashBotWorkflowCreateTeamPolicy-${lower(var.environment)}" - description = "Policy for the create team lambda function." - policy = data.aws_iam_policy_document.create_team_policy_document.json -} \ No newline at end of file diff --git a/terraform/workflow/event-handler-lambda.tf b/terraform/workflow/event-handler-lambda.tf deleted file mode 100644 index 348974d..0000000 --- a/terraform/workflow/event-handler-lambda.tf +++ /dev/null @@ -1,89 +0,0 @@ -resource "aws_lambda_function" "event_handler_lambda" { - function_name = "clash-bot-event-handler-${lower(var.environment)}" - handler = "prod/handler.handler" - runtime = "nodejs16.x" - role = aws_iam_role.lambda_handler_exec.arn - - s3_bucket = var.s3_bucket_name - s3_key = var.event_handler_artifact_path - - environment { - variables = { - CREATE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn - UPDATE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn - DELETE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn - CREATE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn - UPDATE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn - DELETE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn - } - } -} - -resource "aws_lambda_event_source_mapping" "sqs_trigger" { - event_source_arn = module.clash_bot_event_sqs.queue_arn - function_name = aws_lambda_function.event_handler_lambda.function_name - batch_size = var.sqs_batch_size -} - -resource "aws_iam_role" "lambda_handler_exec" { - name = "clash_bot_event_handler_exec_role-${lower(var.environment)}" - - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "lambda.amazonaws.com" - } - } - ] - }) -} - -resource "aws_iam_role_policy_attachment" "lambda_handler_exec_policy" { - role = aws_iam_role.lambda_handler_exec.name - policy_arn = aws_iam_policy.event_handler_policy.arn -} - -data "aws_iam_policy_document" "event_handler_policy_document" { - statement { - effect = "Allow" - actions = [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ] - resources = ["*"] - } - - statement { - effect = "Allow" - actions = [ - "sqs:DeleteMessage", - "sqs:GetQueueAttributes", - "sqs:ReceiveMessage", - ] - resources = [ - module.clash_bot_event_sqs.queue_arn - ] - } - - statement { - effect = "Allow" - actions = [ - "states:StartExecution" - ] - resources = [ - module.create_team_step_function.state_machine_arn - ] - - } -} - -resource "aws_iam_policy" "event_handler_policy" { - name = "ClashBotWorkflowEventHandlerPolicy-${lower(var.environment)}" - description = "Allows the event handler lambda to interact with SQS and CloudWatch Logs" - policy = data.aws_iam_policy_document.event_handler_policy_document.json -} \ No newline at end of file diff --git a/terraform/workflow/event-publisher-lambda.tf b/terraform/workflow/event-publisher-lambda.tf deleted file mode 100644 index 7e619da..0000000 --- a/terraform/workflow/event-publisher-lambda.tf +++ /dev/null @@ -1,76 +0,0 @@ -resource "aws_lambda_function" "event_publisher_lambda" { - function_name = "clash-bot-event-publisher-${lower(var.environment)}" - handler = "prod/handler.handler" - runtime = "nodejs16.x" - role = aws_iam_role.lambda_publisher_exec.arn - - s3_bucket = var.s3_bucket_name - s3_key = var.event_publisher_artifact_path - - environment { - variables = { - QUEUE_URL = module.clash_bot_event_sqs.queue_url - } - } -} - -resource "aws_iam_role" "lambda_publisher_exec" { - name = "clash_bot_event_publisher_exec_role-${lower(var.environment)}" - - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "lambda.amazonaws.com" - } - } - ] - }) -} - -resource "aws_lambda_permission" "apigw" { - statement_id = "AllowExecutionFromAPIGateway-${lower(var.environment)}" - action = "lambda:InvokeFunction" - function_name = aws_lambda_function.event_publisher_lambda.function_name - principal = "apigateway.amazonaws.com" - - # The /*/* portion grants access from any method on any resource - # within the API Gateway "REST API". - source_arn = "${module.api_gateway.apigatewayv2_api_execution_arn}/*/*" -} - -resource "aws_iam_role_policy_attachment" "lambda_publisher_exec_policy" { - role = aws_iam_role.lambda_publisher_exec.name - policy_arn = aws_iam_policy.event_publisher_policy.arn -} - -data "aws_iam_policy_document" "event_publisher_policy_document" { - statement { - effect = "Allow" - actions = [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ] - resources = ["*"] - } - - statement { - effect = "Allow" - actions = [ - "sqs:SendMessage" - ] - resources = [ - module.clash_bot_event_sqs.queue_arn - ] - } -} - -resource "aws_iam_policy" "event_publisher_policy" { - name = "ClashBotEventPublisherPolicy-${lower(var.environment)}" - description = "Allows the event publisher lambda to publish events to the event queue and log events to CloudWatch" - policy = data.aws_iam_policy_document.event_publisher_policy_document.json -} \ No newline at end of file diff --git a/terraform/workflow/modules/lambda/lambda.tf b/terraform/workflow/modules/lambda/lambda.tf new file mode 100644 index 0000000..c23fac8 --- /dev/null +++ b/terraform/workflow/modules/lambda/lambda.tf @@ -0,0 +1,41 @@ +resource "aws_lambda_function" "lambda" { + function_name = "${var.prefix}-${lower(var.environment)}" + handler = "prod/handler.handler" + runtime = "nodejs16.x" + role = aws_iam_role.lambda_exec_role.arn + + s3_bucket = var.s3_bucket_name + s3_key = var.artifact_path + + environment { + variables = var.environment_variables + } +} + +resource "aws_iam_role" "lambda_exec_role" { + name = "${replace(var.prefix, "-", "_")}_exec_role-${lower(var.environment)}" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" + } + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "lambda_exec_policy" { + role = aws_iam_role.lambda_exec_role.name + policy_arn = aws_iam_policy.policy.arn +} + +resource "aws_iam_policy" "policy" { + name = "${var.prefix}-Policy-${lower(var.environment)}" + description = "Policy for the ${var.prefix} lambda function." + policy = var.iam_policy_json +} \ No newline at end of file diff --git a/terraform/workflow/modules/lambda/outputs.tf b/terraform/workflow/modules/lambda/outputs.tf new file mode 100644 index 0000000..6406f25 --- /dev/null +++ b/terraform/workflow/modules/lambda/outputs.tf @@ -0,0 +1,9 @@ +output "arn" { + value = aws_lambda_function.lambda.arn + description = "The ARN for the lambda function." +} + +output "name" { + value = aws_lambda_function.lambda.function_name + description = "The name for the lambda function." +} \ No newline at end of file diff --git a/terraform/workflow/modules/lambda/variables.tf b/terraform/workflow/modules/lambda/variables.tf new file mode 100644 index 0000000..5f9a0cf --- /dev/null +++ b/terraform/workflow/modules/lambda/variables.tf @@ -0,0 +1,29 @@ +variable "prefix" { + type = string + description = "The prefix to use for all resources." +} + +variable "environment" { + type = string + description = "The environment to use." +} + +variable "s3_bucket_name" { + type = string + description = "The s3 bucket that stores the lambda function code." +} + +variable "artifact_path" { + type = string + description = "Path to the artifact for the lambda function." +} + +variable "environment_variables" { + type = map(string) + description = "Environment variables to set for the lambda function." +} + +variable "iam_policy_json" { + type = string + description = "The json for the iam policy." +} \ No newline at end of file diff --git a/terraform/workflow/policies/create-team-lambda-policy.json b/terraform/workflow/policies/create-team-lambda-policy.json new file mode 100644 index 0000000..6d4cc8e --- /dev/null +++ b/terraform/workflow/policies/create-team-lambda-policy.json @@ -0,0 +1,27 @@ +{ + "Statement": [ + { + "Action": [ + "logs:PutLogEvents", + "logs:CreateLogStream", + "logs:CreateLogGroup" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "dynamodb:UpdateItem", + "dynamodb:Query", + "dynamodb:PutItem", + "dynamodb:GetItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem", + "dynamodb:BatchGetItem" + ], + "Effect": "Allow", + "Resource": "${DYNAMODB_ARN}" + } + ], + "Version": "2012-10-17" +} \ No newline at end of file diff --git a/terraform/workflow/policies/event-handler-lambda-policy.json b/terraform/workflow/policies/event-handler-lambda-policy.json new file mode 100644 index 0000000..a9e46b5 --- /dev/null +++ b/terraform/workflow/policies/event-handler-lambda-policy.json @@ -0,0 +1,28 @@ +{ + "Statement": [ + { + "Action": [ + "logs:PutLogEvents", + "logs:CreateLogStream", + "logs:CreateLogGroup" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "sqs:ReceiveMessage", + "sqs:GetQueueAttributes", + "sqs:DeleteMessage" + ], + "Effect": "Allow", + "Resource": "${SQS_ARN}" + }, + { + "Action": "states:StartExecution", + "Effect": "Allow", + "Resource": "${CREATE_TEAM_STATE_MACHINE_ARN}" + } + ], + "Version": "2012-10-17" +} \ No newline at end of file diff --git a/terraform/workflow/policies/event-publisher-lambda-policy.json b/terraform/workflow/policies/event-publisher-lambda-policy.json new file mode 100644 index 0000000..9ce6671 --- /dev/null +++ b/terraform/workflow/policies/event-publisher-lambda-policy.json @@ -0,0 +1,19 @@ +{ + "Statement": [ + { + "Action": [ + "logs:PutLogEvents", + "logs:CreateLogStream", + "logs:CreateLogGroup" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": "${SQS_ARN}" + } + ], + "Version": "2012-10-17" +} \ No newline at end of file diff --git a/terraform/workflow/policies/retrieve-teams-lambda-policy.json b/terraform/workflow/policies/retrieve-teams-lambda-policy.json new file mode 100644 index 0000000..6d4cc8e --- /dev/null +++ b/terraform/workflow/policies/retrieve-teams-lambda-policy.json @@ -0,0 +1,27 @@ +{ + "Statement": [ + { + "Action": [ + "logs:PutLogEvents", + "logs:CreateLogStream", + "logs:CreateLogGroup" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "dynamodb:UpdateItem", + "dynamodb:Query", + "dynamodb:PutItem", + "dynamodb:GetItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem", + "dynamodb:BatchGetItem" + ], + "Effect": "Allow", + "Resource": "${DYNAMODB_ARN}" + } + ], + "Version": "2012-10-17" +} \ No newline at end of file diff --git a/terraform/workflow/policies/tournament-eligibility-lambda-policy.json b/terraform/workflow/policies/tournament-eligibility-lambda-policy.json new file mode 100644 index 0000000..6d4cc8e --- /dev/null +++ b/terraform/workflow/policies/tournament-eligibility-lambda-policy.json @@ -0,0 +1,27 @@ +{ + "Statement": [ + { + "Action": [ + "logs:PutLogEvents", + "logs:CreateLogStream", + "logs:CreateLogGroup" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "dynamodb:UpdateItem", + "dynamodb:Query", + "dynamodb:PutItem", + "dynamodb:GetItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem", + "dynamodb:BatchGetItem" + ], + "Effect": "Allow", + "Resource": "${DYNAMODB_ARN}" + } + ], + "Version": "2012-10-17" +} \ No newline at end of file diff --git a/terraform/workflow/retrieve-teams-lambda.tf b/terraform/workflow/retrieve-teams-lambda.tf deleted file mode 100644 index e82b344..0000000 --- a/terraform/workflow/retrieve-teams-lambda.tf +++ /dev/null @@ -1,71 +0,0 @@ -resource "aws_lambda_function" "retrieve_team_lambda" { - function_name = "clash-bot-retrieve-team-${lower(var.environment)}" - handler = "prod/handler.handler" - runtime = "nodejs16.x" - role = aws_iam_role.retrieve_team_lambda_exec.arn - - s3_bucket = var.s3_bucket_name - s3_key = var.retrieve_teams_artifact_path - - environment { - variables = { - TABLE_NAME = module.dynamodb_table.dynamodb_table_id - } - } -} - -resource "aws_iam_role" "retrieve_team_lambda_exec" { - name = "clash_bot_retrieve_team_exec_role-${lower(var.environment)}" - - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "lambda.amazonaws.com" - } - } - ] - }) -} - -resource "aws_iam_role_policy_attachment" "retrieve_team_lambda_exec_policy" { - role = aws_iam_role.retrieve_team_lambda_exec.name - policy_arn = aws_iam_policy.retrieve_team_policy.arn -} - -data "aws_iam_policy_document" "retrieve_team_policy_document" { - statement { - effect = "Allow" - actions = [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ] - resources = ["*"] - } - - statement { - effect = "Allow" - actions = [ - "dynamodb:GetItem", - "dynamodb:BatchGetItem", - "dynamodb:Query", - "dynamodb:PutItem", - "dynamodb:UpdateItem", - "dynamodb:DeleteItem", - "dynamodb:BatchWriteItem" - ] - resources = [ - module.dynamodb_table.dynamodb_table_arn - ] - } -} - -resource "aws_iam_policy" "retrieve_team_policy" { - name = "ClashBotWorkflowRetrieveTeamPolicy-${lower(var.environment)}" - description = "Policy for the retrieve team lambda function." - policy = data.aws_iam_policy_document.retrieve_team_policy_document.json -} \ No newline at end of file diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 957f64e..65925e6 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -3,8 +3,9 @@ module "create_team_step_function" { name = "create-team-${var.environment}" definition = templatefile("${path.module}/step-functions/create-team-step-function.asl.json", { - CreateTeamLambdaFunctionArn = aws_lambda_function.create_team_lambda.arn, - RetrieveTeamLambdaFunctionArn = aws_lambda_function.retrieve_team_lambda.arn, + CreateTeamLambdaFunctionArn = module.create_team_lambda.arn, + RetrieveTeamLambdaFunctionArn = module.retrieve_team_lambda.arn, + IsTournamentEligibleLambdaFunctionArn = module.tournament_eligibility_lambda.arn } ) @@ -15,4 +16,126 @@ module "create_team_step_function" { } type = "STANDARD" +} + +module "create_team_lambda" { + source = "./modules/lambda" + + prefix = "create-team" + s3_bucket_name = var.s3_bucket_name + environment = var.environment + + artifact_path = var.create_team_artifact_path + + environment_variables = { + TABLE_NAME = module.dynamodb_table.dynamodb_table_id + } + + iam_policy_json = templatefile( + "${path.module}/policies/create-team-lambda-policy.json", + { + DYNAMODB_TABLE_ARN = module.dynamodb_table.dynamodb_table_arn + } + ) +} + +module "event_handler_lambda" { + source = "./modules/lambda" + + prefix = "event-handler" + s3_bucket_name = var.s3_bucket_name + environment = var.environment + + artifact_path = var.event_handler_artifact_path + + environment_variables = { + CREATE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn + UPDATE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn + DELETE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn + CREATE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn + UPDATE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn + DELETE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn + } + + iam_policy_json = templatefile( + "${path.module}/policies/event-handler-lambda-policy.json", + { + CREATE_TEAM_STATE_MACHINE_ARN = module.create_team_step_function.state_machine_arn + SQS_ARN = module.clash_bot_event_sqs.queue_arn + } + ) +} + +module "event_publisher_lambda" { + source = "./modules/lambda" + + prefix = "event-publisher" + s3_bucket_name = var.s3_bucket_name + environment = var.environment + + artifact_path = var.event_publisher_artifact_path + + environment_variables = { + QUEUE_URL = module.clash_bot_event_sqs.queue_url + } + + iam_policy_json = templatefile( + "${path.module}/policies/event-publisher-lambda-policy.json", + { + SQS_ARN = module.clash_bot_event_sqs.queue_arn + } + ) +} + +module "retrieve_team_lambda" { + source = "./modules/lambda" + + prefix = "retrieve-team" + s3_bucket_name = var.s3_bucket_name + environment = var.environment + + artifact_path = var.retrieve_teams_artifact_path + + environment_variables = { + TABLE_NAME = module.dynamodb_table.dynamodb_table_id + } + + iam_policy_json = templatefile( + "${path.module}/policies/retrieve-teams-lambda-policy.json", + { + SQS_ARN = module.clash_bot_event_sqs.queue_arn + } + ) +} + +module "tournament_eligibility_lambda" { + source = "./modules/lambda" + + prefix = "tournament-eligibility" + s3_bucket_name = var.s3_bucket_name + environment = var.environment + + artifact_path = var.tournament_eligibility_lambda_artifact_path + + environment_variables = { + TABLE_NAME = module.dynamodb_table.dynamodb_table_id + } + + iam_policy_json = templatefile( + "${path.module}/policies/tournament-eligibility-lambda-policy.json", + { + SQS_ARN = module.clash_bot_event_sqs.queue_arn + } + ) +} + +resource "aws_lambda_permission" "apigw" { + statement_id = "AllowExecutionFromAPIGateway-${lower(var.environment)}" + action = "lambda:InvokeFunction" + function_name = module.event_publisher_lambda.name + principal = "apigateway.amazonaws.com" + + # The /*/* portion grants access from any method on any resource + # within the API Gateway "REST API". + source_arn = "${module.api_gateway.apigatewayv2_api_execution_arn}/*/*" } \ No newline at end of file diff --git a/terraform/workflow/variables.tf b/terraform/workflow/variables.tf index 7c8e98a..a5a599e 100644 --- a/terraform/workflow/variables.tf +++ b/terraform/workflow/variables.tf @@ -35,6 +35,11 @@ variable "retrieve_teams_artifact_path" { description = "Path to the artifact for the retrieve team lambda function." } +variable "tournament_eligibility_lambda_artifact_path" { + type = string + description = "Path to the artifact for the tournament eligibility lambda function." +} + variable "sqs_batch_size" { type = number default = 1 From e71adad23e9f96d853cb4b3f6b5711da1ba426f1 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 17:48:08 -0600 Subject: [PATCH 043/119] Formatting --- terraform/workflow/step-functions.lambda.tf | 4 ++-- terraform/workflow/variables.tf | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 65925e6..55dd238 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -3,8 +3,8 @@ module "create_team_step_function" { name = "create-team-${var.environment}" definition = templatefile("${path.module}/step-functions/create-team-step-function.asl.json", { - CreateTeamLambdaFunctionArn = module.create_team_lambda.arn, - RetrieveTeamLambdaFunctionArn = module.retrieve_team_lambda.arn, + CreateTeamLambdaFunctionArn = module.create_team_lambda.arn, + RetrieveTeamLambdaFunctionArn = module.retrieve_team_lambda.arn, IsTournamentEligibleLambdaFunctionArn = module.tournament_eligibility_lambda.arn } ) diff --git a/terraform/workflow/variables.tf b/terraform/workflow/variables.tf index a5a599e..a4e3d0b 100644 --- a/terraform/workflow/variables.tf +++ b/terraform/workflow/variables.tf @@ -36,7 +36,7 @@ variable "retrieve_teams_artifact_path" { } variable "tournament_eligibility_lambda_artifact_path" { - type = string + type = string description = "Path to the artifact for the tournament eligibility lambda function." } From 7f4b7bacea29b779629ee15bb919f6c30b5a4f36 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 17:52:49 -0600 Subject: [PATCH 044/119] Fixing step functions --- terraform/workflow/step-functions.lambda.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 55dd238..3e8619c 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -103,7 +103,7 @@ module "retrieve_team_lambda" { iam_policy_json = templatefile( "${path.module}/policies/retrieve-teams-lambda-policy.json", { - SQS_ARN = module.clash_bot_event_sqs.queue_arn + DYNAMODB_ARN = module.dynamodb_table.dynamodb_table_arn } ) } @@ -124,7 +124,7 @@ module "tournament_eligibility_lambda" { iam_policy_json = templatefile( "${path.module}/policies/tournament-eligibility-lambda-policy.json", { - SQS_ARN = module.clash_bot_event_sqs.queue_arn + DYNAMODB_ARN = module.dynamodb_table.dynamodb_table_arn } ) } From fa83ca9e85f0e1ad90bcbedf2c9be73d71fd3082 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 17:56:44 -0600 Subject: [PATCH 045/119] Hoping this is the last update --- terraform/workflow/step-functions.lambda.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 3e8619c..e66a40d 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -34,7 +34,7 @@ module "create_team_lambda" { iam_policy_json = templatefile( "${path.module}/policies/create-team-lambda-policy.json", { - DYNAMODB_TABLE_ARN = module.dynamodb_table.dynamodb_table_arn + DYNAMODB_ARN = module.dynamodb_table.dynamodb_table_arn } ) } From 8dc959f8c5dfdde54ca558517791e18c2fd26386 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 18:01:15 -0600 Subject: [PATCH 046/119] Fixing output.tf --- terraform/workflow/output.tf | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/terraform/workflow/output.tf b/terraform/workflow/output.tf index 1b3d35d..8999a40 100644 --- a/terraform/workflow/output.tf +++ b/terraform/workflow/output.tf @@ -4,23 +4,28 @@ output "api-gateway-endpoint" { } output "event-publisher-lambda-arn" { - value = aws_lambda_function.event_publisher_lambda.arn + value = module.event_publisher_lambda.arn description = "The ARN for the event publisher lambda function." } output "event-handler-lambda-arn" { - value = aws_lambda_function.event_handler_lambda.arn + value = module.event_handler_lambda.arn description = "The ARN for the event handler lambda function." } -output "event-publisher-lambda-version" { - value = aws_lambda_function.event_publisher_lambda.version - description = "The version for the event publisher lambda function." +output "create-team-lambda-arn" { + value = module.create_team_lambda.arn + description = "The ARN for the create team lambda function." } -output "event-handler-lambda-version" { - value = aws_lambda_function.event_handler_lambda.version - description = "The version for the event handler lambda function." +output "retrieve-team-lambda-arn" { + value = module.retrieve_team_lambda.arn + description = "The ARN for the retrieve team lambda function." +} + +output "tournament-eligibility-lambda-arn" { + value = module.tournament_eligibility_lambda.arn + description = "The ARN for the tournament eligibility lambda function." } output "event-sqs" { From 6e734eabbf7ce6d75edc2ae5218dab5579054424 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 18:07:17 -0600 Subject: [PATCH 047/119] Fixing arn used in lambda gateway --- terraform/workflow/api-gateway.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/api-gateway.tf b/terraform/workflow/api-gateway.tf index 763976f..f0af987 100644 --- a/terraform/workflow/api-gateway.tf +++ b/terraform/workflow/api-gateway.tf @@ -22,7 +22,7 @@ module "api_gateway" { # Routes and integrations integrations = { "POST /api/v2/teams" = { - lambda_arn = aws_lambda_function.event_publisher_lambda.arn, + lambda_arn = module.event_publisher_lambda.arn, integration_type = "AWS_PROXY", } } From b77ed7c207de5cb69eb28717ee352dee3debe818 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 18:15:27 -0600 Subject: [PATCH 048/119] Testing out caching of node modules --- .github/workflows/build_and_deploy_lambda.yml | 13 +- .github/workflows/pull-request.yml | 186 +++++++++--------- 2 files changed, 104 insertions(+), 95 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index cc671e5..5d669d6 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -37,13 +37,22 @@ jobs: contents: read steps: - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Checkout + uses: actions/checkout@v2 - name: Setup Node.js uses: actions/setup-node@v2 with: node-version: '16' - - name: Checkout - uses: actions/checkout@v2 + + - name: Cache Node.js modules + uses: actions/cache@v2 + with: + path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- - name: Install dependencies run: npm install diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 98955cd..07f3fd4 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -147,99 +147,99 @@ jobs: needs: - terraformPreReqs - terraformWorkflow: - name: Workflow Apply - runs-on: ubuntu-latest - environment: - name: Development - defaults: - run: - working-directory: ./terraform/workflow - permissions: - id-token: write - contents: read - pull-requests: write - needs: - - eventHandler - - eventPublisher - - createTeam - - retrieveTeams - - isTournamentEligible - - steps: - - uses: FranzDiebold/github-env-vars-action@v2.1.0 - - - name: Checkout - uses: actions/checkout@v3 - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v3 - with: - role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser - aws-region: us-east-1 - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v1 - with: - cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} - - - name: Terraform Format - id: fmt - run: terraform fmt -check - - - name: Terraform Init - id: init - run: terraform init -backend-config=backend-configs/${{ vars.ENVIRONMENT }}.remote.tfbackend - - - name: Terraform Validate - id: validate - run: terraform validate -no-color - - - name: Apply - id: apply - env: - TF_VAR_region: us-east-1 - TF_VAR_environment: ${{ vars.ENVIRONMENT }} - TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} - TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip - TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip - TF_VAR_create_team_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip - TF_VAR_retrieve_teams_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/retrieve-teams.zip - TF_VAR_tournament_eligibility_lambda_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/is-tournament-eligible.zip - TF_VAR_sqs_batch_size: "1" - run: terraform apply -no-color -input=false --auto-approve - - - name: Update Pull Request - uses: actions/github-script@v6.1.0 - if: github.event_name == 'pull_request' - env: - APPLY: "terraform\n${{ steps.apply.outputs.stdout }}" - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const output = `Clash Bot Workflow - Apply - #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` - #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` - #### Workflow Apply 📖\`${{ steps.apply.outcome }}\` - #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` - -
Show Run - - #### Workflow Apply - \`\`\`\n - ${process.env.APPLY} - \`\`\` - -
- - *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }) + # terraformWorkflow: + # name: Workflow Apply + # runs-on: ubuntu-latest + # environment: + # name: Development + # defaults: + # run: + # working-directory: ./terraform/workflow + # permissions: + # id-token: write + # contents: read + # pull-requests: write + # needs: + # - eventHandler + # - eventPublisher + # - createTeam + # - retrieveTeams + # - isTournamentEligible + + # steps: + # - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + # - name: Checkout + # uses: actions/checkout@v3 + + # - name: Configure AWS Credentials + # uses: aws-actions/configure-aws-credentials@v3 + # with: + # role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser + # aws-region: us-east-1 + + # - name: Setup Terraform + # uses: hashicorp/setup-terraform@v1 + # with: + # cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} + + # - name: Terraform Format + # id: fmt + # run: terraform fmt -check + + # - name: Terraform Init + # id: init + # run: terraform init -backend-config=backend-configs/${{ vars.ENVIRONMENT }}.remote.tfbackend + + # - name: Terraform Validate + # id: validate + # run: terraform validate -no-color + + # - name: Apply + # id: apply + # env: + # TF_VAR_region: us-east-1 + # TF_VAR_environment: ${{ vars.ENVIRONMENT }} + # TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} + # TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip + # TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip + # TF_VAR_create_team_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip + # TF_VAR_retrieve_teams_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/retrieve-teams.zip + # TF_VAR_tournament_eligibility_lambda_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/is-tournament-eligible.zip + # TF_VAR_sqs_batch_size: "1" + # run: terraform apply -no-color -input=false --auto-approve + + # - name: Update Pull Request + # uses: actions/github-script@v6.1.0 + # if: github.event_name == 'pull_request' + # env: + # APPLY: "terraform\n${{ steps.apply.outputs.stdout }}" + # with: + # github-token: ${{ secrets.GITHUB_TOKEN }} + # script: | + # const output = `Clash Bot Workflow - Apply + # #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` + # #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` + # #### Workflow Apply 📖\`${{ steps.apply.outcome }}\` + # #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` + + #
Show Run + + # #### Workflow Apply + # \`\`\`\n + # ${process.env.APPLY} + # \`\`\` + + #
+ + # *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; + + # github.rest.issues.createComment({ + # issue_number: context.issue.number, + # owner: context.repo.owner, + # repo: context.repo.repo, + # body: output + # }) # Commenting for now until I can cut down on graph size # - name: Install Graphviz From 3ca8021cc0808f9bd913068b57fa42efef51ec00 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 18:18:32 -0600 Subject: [PATCH 049/119] Triggering another build --- .github/workflows/pull-request.yml | 186 ++++++++++++++--------------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 07f3fd4..98955cd 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -147,99 +147,99 @@ jobs: needs: - terraformPreReqs - # terraformWorkflow: - # name: Workflow Apply - # runs-on: ubuntu-latest - # environment: - # name: Development - # defaults: - # run: - # working-directory: ./terraform/workflow - # permissions: - # id-token: write - # contents: read - # pull-requests: write - # needs: - # - eventHandler - # - eventPublisher - # - createTeam - # - retrieveTeams - # - isTournamentEligible - - # steps: - # - uses: FranzDiebold/github-env-vars-action@v2.1.0 - - # - name: Checkout - # uses: actions/checkout@v3 - - # - name: Configure AWS Credentials - # uses: aws-actions/configure-aws-credentials@v3 - # with: - # role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser - # aws-region: us-east-1 - - # - name: Setup Terraform - # uses: hashicorp/setup-terraform@v1 - # with: - # cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} - - # - name: Terraform Format - # id: fmt - # run: terraform fmt -check - - # - name: Terraform Init - # id: init - # run: terraform init -backend-config=backend-configs/${{ vars.ENVIRONMENT }}.remote.tfbackend - - # - name: Terraform Validate - # id: validate - # run: terraform validate -no-color - - # - name: Apply - # id: apply - # env: - # TF_VAR_region: us-east-1 - # TF_VAR_environment: ${{ vars.ENVIRONMENT }} - # TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} - # TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip - # TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip - # TF_VAR_create_team_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip - # TF_VAR_retrieve_teams_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/retrieve-teams.zip - # TF_VAR_tournament_eligibility_lambda_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/is-tournament-eligible.zip - # TF_VAR_sqs_batch_size: "1" - # run: terraform apply -no-color -input=false --auto-approve - - # - name: Update Pull Request - # uses: actions/github-script@v6.1.0 - # if: github.event_name == 'pull_request' - # env: - # APPLY: "terraform\n${{ steps.apply.outputs.stdout }}" - # with: - # github-token: ${{ secrets.GITHUB_TOKEN }} - # script: | - # const output = `Clash Bot Workflow - Apply - # #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` - # #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` - # #### Workflow Apply 📖\`${{ steps.apply.outcome }}\` - # #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` - - #
Show Run - - # #### Workflow Apply - # \`\`\`\n - # ${process.env.APPLY} - # \`\`\` - - #
- - # *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; - - # github.rest.issues.createComment({ - # issue_number: context.issue.number, - # owner: context.repo.owner, - # repo: context.repo.repo, - # body: output - # }) + terraformWorkflow: + name: Workflow Apply + runs-on: ubuntu-latest + environment: + name: Development + defaults: + run: + working-directory: ./terraform/workflow + permissions: + id-token: write + contents: read + pull-requests: write + needs: + - eventHandler + - eventPublisher + - createTeam + - retrieveTeams + - isTournamentEligible + + steps: + - uses: FranzDiebold/github-env-vars-action@v2.1.0 + + - name: Checkout + uses: actions/checkout@v3 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser + aws-region: us-east-1 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v1 + with: + cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} + + - name: Terraform Format + id: fmt + run: terraform fmt -check + + - name: Terraform Init + id: init + run: terraform init -backend-config=backend-configs/${{ vars.ENVIRONMENT }}.remote.tfbackend + + - name: Terraform Validate + id: validate + run: terraform validate -no-color + + - name: Apply + id: apply + env: + TF_VAR_region: us-east-1 + TF_VAR_environment: ${{ vars.ENVIRONMENT }} + TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} + TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip + TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip + TF_VAR_create_team_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip + TF_VAR_retrieve_teams_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/retrieve-teams.zip + TF_VAR_tournament_eligibility_lambda_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/is-tournament-eligible.zip + TF_VAR_sqs_batch_size: "1" + run: terraform apply -no-color -input=false --auto-approve + + - name: Update Pull Request + uses: actions/github-script@v6.1.0 + if: github.event_name == 'pull_request' + env: + APPLY: "terraform\n${{ steps.apply.outputs.stdout }}" + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const output = `Clash Bot Workflow - Apply + #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` + #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` + #### Workflow Apply 📖\`${{ steps.apply.outcome }}\` + #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` + +
Show Run + + #### Workflow Apply + \`\`\`\n + ${process.env.APPLY} + \`\`\` + +
+ + *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output + }) # Commenting for now until I can cut down on graph size # - name: Install Graphviz From d6a44ee5e5d54cc1df6e471c6b6d42a2f1c5a0b0 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 18:38:52 -0600 Subject: [PATCH 050/119] Testing out caching of lambda artifacts --- .github/workflows/build_and_deploy_lambda.yml | 21 ++- .github/workflows/pull-request.yml | 148 ++++++++++-------- 2 files changed, 97 insertions(+), 72 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 5d669d6..9c5b5d3 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -54,29 +54,36 @@ jobs: restore-keys: | ${{ runner.os }}-node- + - name: Cache lambda artifact + id: cache + uses: actions/cache@v2 + with: + path: ${{ inputs.working-directory}}/${{ inputs.artifact-name }}.zip + key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/${{ inputs.artifact-name }}.zip') }} + restore-keys: | + ${{ inputs.artifact-name }}-lambda- + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' run: npm install - name: Test + if: steps.cache.outputs.cache-hit != 'true' run: npm test - name: Build Artifact + if: steps.cache.outputs.cache-hit != 'true' run: npm run build - name: Configure AWS Credentials + if: steps.cache.outputs.cache-hit != 'true' uses: aws-actions/configure-aws-credentials@v3 with: role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser aws-region: ${{ inputs.region }} - name: Archive and Publish + if: steps.cache.outputs.cache-hit != 'true' run: | zip -r ./${{ inputs.artifact-name}}.zip ./prod ./package.json aws s3 cp ./${{ inputs.artifact-name}}.zip s3://${{ inputs.s3-bucket-name }}/artifacts/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/${{ github.run_number }}/${{ inputs.artifact-name}}.zip - - - name: Upload Artifacts - uses: actions/upload-artifact@v2 - with: - name: ${{ inputs.artifact-name }}-${{ inputs.environment-name }}-${{ github.run_number }} - path: ${{ inputs.working-directory}}/${{ inputs.artifact-name }}.zip - if-no-files-found: error diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 98955cd..c7946d7 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -172,74 +172,92 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v3 + - name: Restore Event Handler Artifact + uses: actions/cache@v2 with: - role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser - aws-region: us-east-1 + path: event-handler.zip + key: event-handler-lambda-${{ github.run_id }} + restore-keys: | + event-handler-lambda- - - name: Setup Terraform - uses: hashicorp/setup-terraform@v1 + - name: Restore Event Publisher Artifact + uses: actions/cache@v2 with: - cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} - - - name: Terraform Format - id: fmt - run: terraform fmt -check - - - name: Terraform Init - id: init - run: terraform init -backend-config=backend-configs/${{ vars.ENVIRONMENT }}.remote.tfbackend - - - name: Terraform Validate - id: validate - run: terraform validate -no-color - - - name: Apply - id: apply - env: - TF_VAR_region: us-east-1 - TF_VAR_environment: ${{ vars.ENVIRONMENT }} - TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} - TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip - TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip - TF_VAR_create_team_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip - TF_VAR_retrieve_teams_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/retrieve-teams.zip - TF_VAR_tournament_eligibility_lambda_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/is-tournament-eligible.zip - TF_VAR_sqs_batch_size: "1" - run: terraform apply -no-color -input=false --auto-approve - - - name: Update Pull Request - uses: actions/github-script@v6.1.0 - if: github.event_name == 'pull_request' - env: - APPLY: "terraform\n${{ steps.apply.outputs.stdout }}" - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const output = `Clash Bot Workflow - Apply - #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` - #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` - #### Workflow Apply 📖\`${{ steps.apply.outcome }}\` - #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` - -
Show Run - - #### Workflow Apply - \`\`\`\n - ${process.env.APPLY} - \`\`\` - -
- - *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }) + path: event-handler.zip + key: event-publisher-lambda-${{ github.run_id }} + restore-keys: | + event-publisher-lambda- + + + + # - name: Configure AWS Credentials + # uses: aws-actions/configure-aws-credentials@v3 + # with: + # role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser + # aws-region: us-east-1 + + # - name: Setup Terraform + # uses: hashicorp/setup-terraform@v1 + # with: + # cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} + + # - name: Terraform Format + # id: fmt + # run: terraform fmt -check + + # - name: Terraform Init + # id: init + # run: terraform init -backend-config=backend-configs/${{ vars.ENVIRONMENT }}.remote.tfbackend + + # - name: Terraform Validate + # id: validate + # run: terraform validate -no-color + + # - name: Apply + # id: apply + # env: + # TF_VAR_region: us-east-1 + # TF_VAR_environment: ${{ vars.ENVIRONMENT }} + # TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} + # TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip + # TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip + # TF_VAR_create_team_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip + # TF_VAR_retrieve_teams_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/retrieve-teams.zip + # TF_VAR_tournament_eligibility_lambda_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/is-tournament-eligible.zip + # TF_VAR_sqs_batch_size: "1" + # run: terraform apply -no-color -input=false --auto-approve + + # - name: Update Pull Request + # uses: actions/github-script@v6.1.0 + # if: github.event_name == 'pull_request' + # env: + # APPLY: "terraform\n${{ steps.apply.outputs.stdout }}" + # with: + # github-token: ${{ secrets.GITHUB_TOKEN }} + # script: | + # const output = `Clash Bot Workflow - Apply + # #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` + # #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` + # #### Workflow Apply 📖\`${{ steps.apply.outcome }}\` + # #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` + + #
Show Run + + # #### Workflow Apply + # \`\`\`\n + # ${process.env.APPLY} + # \`\`\` + + #
+ + # *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; + + # github.rest.issues.createComment({ + # issue_number: context.issue.number, + # owner: context.repo.owner, + # repo: context.repo.repo, + # body: output + # }) # Commenting for now until I can cut down on graph size # - name: Install Graphviz From 8613514ad9b330b4ea3984c7e6c0cd4868071ce5 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 19:07:51 -0600 Subject: [PATCH 051/119] Pushing out new version of all artifacts --- .github/workflows/build_and_deploy_lambda.yml | 28 +++++++++---------- .github/workflows/pull-request.yml | 24 +++++++++++++++- scripts/pull-latest-artifact.sh | 19 +++++++++++++ 3 files changed, 56 insertions(+), 15 deletions(-) create mode 100755 scripts/pull-latest-artifact.sh diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 9c5b5d3..4f27b9d 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -54,36 +54,36 @@ jobs: restore-keys: | ${{ runner.os }}-node- - - name: Cache lambda artifact - id: cache - uses: actions/cache@v2 - with: - path: ${{ inputs.working-directory}}/${{ inputs.artifact-name }}.zip - key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/${{ inputs.artifact-name }}.zip') }} - restore-keys: | - ${{ inputs.artifact-name }}-lambda- + # - name: Cache lambda artifact + # id: cache + # uses: actions/cache@v2 + # with: + # path: ${{ inputs.working-directory}}/${{ inputs.artifact-name }}.zip + # key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/${{ inputs.artifact-name }}.zip') }} + # restore-keys: | + # ${{ inputs.artifact-name }}-lambda- - name: Install dependencies - if: steps.cache.outputs.cache-hit != 'true' + # if: steps.cache.outputs.cache-hit != 'true' run: npm install - name: Test - if: steps.cache.outputs.cache-hit != 'true' + # if: steps.cache.outputs.cache-hit != 'true' run: npm test - name: Build Artifact - if: steps.cache.outputs.cache-hit != 'true' + # if: steps.cache.outputs.cache-hit != 'true' run: npm run build - name: Configure AWS Credentials - if: steps.cache.outputs.cache-hit != 'true' + # if: steps.cache.outputs.cache-hit != 'true' uses: aws-actions/configure-aws-credentials@v3 with: role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser aws-region: ${{ inputs.region }} - name: Archive and Publish - if: steps.cache.outputs.cache-hit != 'true' + # if: steps.cache.outputs.cache-hit != 'true' run: | zip -r ./${{ inputs.artifact-name}}.zip ./prod ./package.json - aws s3 cp ./${{ inputs.artifact-name}}.zip s3://${{ inputs.s3-bucket-name }}/artifacts/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/${{ github.run_number }}/${{ inputs.artifact-name}}.zip + aws s3 cp ./${{ inputs.artifact-name}}.zip s3://${{ inputs.s3-bucket-name }}/artifacts/${{ inputs.artifact-name}}/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/artifact-${{ github.run_number }}.zip diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index c7946d7..c20fcc7 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -188,7 +188,29 @@ jobs: restore-keys: | event-publisher-lambda- - + - name: Restore Create Team Artifact + uses: actions/cache@v2 + with: + path: create-team.zip + key: create-team-lambda-${{ github.run_id }} + restore-keys: | + create-team-lambda- + + - name: Restore Retrieve Teams Artifact + uses: actions/cache@v2 + with: + path: retrieve-teams.zip + key: retrieve-teams-lambda-${{ github.run_id }} + restore-keys: | + retrieve-teams-lambda- + + - name: Restore Is Tournament Eligible Artifact + uses: actions/cache@v2 + with: + path: is-tournament-eligible.zip + key: is-tournament-eligible-lambda-${{ github.run_id }} + restore-keys: | + is-tournament-eligible-lambda- # - name: Configure AWS Credentials # uses: aws-actions/configure-aws-credentials@v3 diff --git a/scripts/pull-latest-artifact.sh b/scripts/pull-latest-artifact.sh new file mode 100755 index 0000000..1863603 --- /dev/null +++ b/scripts/pull-latest-artifact.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +BUCKET_NAME=${1} +ARTIFACT_NAME=${2} +ENVIRONMENT=${3} + +# List objects in the S3 bucket path +OBJECTS=$(aws s3 ls s3://$BUCKET_NAME//artifacts/$ARTIFACT_NAME/$ENVIRONMENT --recursive --profile=Master | awk '{print $4}') + +# Extract numbers from the object names and find the maximum +MAX_NUMBER=0 +for object in $OBJECTS; do + NUMBER=$(basename $object | grep -o -E '[0-9]+') + if [[ "$NUMBER" -gt "$MAX_NUMBER" ]]; then + MAX_NUMBER=$NUMBER + fi +done + +echo "Maximum number: $MAX_NUMBER" \ No newline at end of file From ac88c3e2f0042fed88294d87c69854f67eac06e6 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 19:20:10 -0600 Subject: [PATCH 052/119] Adding in custom script for retrieving latest artifact version --- .github/workflows/build_and_deploy_lambda.yml | 28 +++---- .github/workflows/pull-request.yml | 74 ++++++++----------- scripts/pull-latest-artifact.sh | 19 +++-- 3 files changed, 59 insertions(+), 62 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 4f27b9d..cdd7139 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -54,36 +54,36 @@ jobs: restore-keys: | ${{ runner.os }}-node- - # - name: Cache lambda artifact - # id: cache - # uses: actions/cache@v2 - # with: - # path: ${{ inputs.working-directory}}/${{ inputs.artifact-name }}.zip - # key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/${{ inputs.artifact-name }}.zip') }} - # restore-keys: | - # ${{ inputs.artifact-name }}-lambda- + - name: Cache lambda artifact + id: cache + uses: actions/cache@v2 + with: + path: ${{ inputs.working-directory}}/${{ inputs.artifact-name }}.zip + key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/${{ inputs.artifact-name }}.zip') }} + restore-keys: | + ${{ inputs.artifact-name }}-lambda- - name: Install dependencies - # if: steps.cache.outputs.cache-hit != 'true' + if: steps.cache.outputs.cache-hit != 'true' run: npm install - name: Test - # if: steps.cache.outputs.cache-hit != 'true' + if: steps.cache.outputs.cache-hit != 'true' run: npm test - name: Build Artifact - # if: steps.cache.outputs.cache-hit != 'true' + if: steps.cache.outputs.cache-hit != 'true' run: npm run build - name: Configure AWS Credentials - # if: steps.cache.outputs.cache-hit != 'true' + if: steps.cache.outputs.cache-hit != 'true' uses: aws-actions/configure-aws-credentials@v3 with: role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser aws-region: ${{ inputs.region }} - name: Archive and Publish - # if: steps.cache.outputs.cache-hit != 'true' + if: steps.cache.outputs.cache-hit != 'true' run: | - zip -r ./${{ inputs.artifact-name}}.zip ./prod ./package.json + zip -r ./${{ inputs.artifact-name}}.zip ./prod ./package.json ./artifact-name.txt aws s3 cp ./${{ inputs.artifact-name}}.zip s3://${{ inputs.s3-bucket-name }}/artifacts/${{ inputs.artifact-name}}/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/artifact-${{ github.run_number }}.zip diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index c20fcc7..a033e7a 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -172,51 +172,39 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Restore Event Handler Artifact - uses: actions/cache@v2 - with: - path: event-handler.zip - key: event-handler-lambda-${{ github.run_id }} - restore-keys: | - event-handler-lambda- - - - name: Restore Event Publisher Artifact - uses: actions/cache@v2 - with: - path: event-handler.zip - key: event-publisher-lambda-${{ github.run_id }} - restore-keys: | - event-publisher-lambda- - - - name: Restore Create Team Artifact - uses: actions/cache@v2 - with: - path: create-team.zip - key: create-team-lambda-${{ github.run_id }} - restore-keys: | - create-team-lambda- - - - name: Restore Retrieve Teams Artifact - uses: actions/cache@v2 - with: - path: retrieve-teams.zip - key: retrieve-teams-lambda-${{ github.run_id }} - restore-keys: | - retrieve-teams-lambda- - - - name: Restore Is Tournament Eligible Artifact - uses: actions/cache@v2 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 with: - path: is-tournament-eligible.zip - key: is-tournament-eligible-lambda-${{ github.run_id }} - restore-keys: | - is-tournament-eligible-lambda- + role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser + aws-region: us-east-1 - # - name: Configure AWS Credentials - # uses: aws-actions/configure-aws-credentials@v3 - # with: - # role-to-assume: arn:aws:iam::816923827429:role/ClashBotGitHubUser - # aws-region: us-east-1 + - name: Find latest Event Handler artifact + id: eventHandlerArtifact + run: ./scripts/find_latest_artifact.sh event-handler ${{ vars.S3_BUCKET_NAME }} development + + - name: Find latest Event Publisher artifact + id: eventPublisherArtifact + run: ./scripts/find_latest_artifact.sh event-publisher ${{ vars.S3_BUCKET_NAME }} development + + - name: Find latest Create Team artifact + id: createTeamArtifact + run: ./scripts/find_latest_artifact.sh create-team ${{ vars.S3_BUCKET_NAME }} development + + - name: Find latest Retrieve Teams artifact + id: retrieveTeamsArtifact + run: ./scripts/find_latest_artifact.sh retrieve-teams ${{ vars.S3_BUCKET_NAME }} development + + - name: Find latest Is Tournament Eligible artifact + id: isTournamentEligibleArtifact + run: ./scripts/find_latest_artifact.sh is-tournament-eligible ${{ vars.S3_BUCKET_NAME }} development + + - name: Latest Artifacts + run: | + echo "Event Handler: ${{ steps.eventHandlerArtifact.outputs.artifact-path }}" + echo "Event Publisher: ${{ steps.eventPublisherArtifact.outputs.artifact-path }}" + echo "Create Team: ${{ steps.createTeamArtifact.outputs.artifact-path }}" + echo "Retrieve Teams: ${{ steps.retrieveTeamsArtifact.outputs.artifact-path }}" + echo "Is Tournament Eligible: ${{ steps.isTournamentEligibleArtifact.outputs.artifact-path }}" # - name: Setup Terraform # uses: hashicorp/setup-terraform@v1 diff --git a/scripts/pull-latest-artifact.sh b/scripts/pull-latest-artifact.sh index 1863603..88a696f 100755 --- a/scripts/pull-latest-artifact.sh +++ b/scripts/pull-latest-artifact.sh @@ -5,15 +5,24 @@ ARTIFACT_NAME=${2} ENVIRONMENT=${3} # List objects in the S3 bucket path -OBJECTS=$(aws s3 ls s3://$BUCKET_NAME//artifacts/$ARTIFACT_NAME/$ENVIRONMENT --recursive --profile=Master | awk '{print $4}') +OBJECTS=$(aws s3 ls s3://$BUCKET_NAME/artifacts/$ARTIFACT_NAME/$ENVIRONMENT --recursive --profile=Master | awk '{print $4}') -# Extract numbers from the object names and find the maximum -MAX_NUMBER=0 +# Extract numbers from the object names and find the maximum number artifact +ARTIFACT_PATH="" for object in $OBJECTS; do NUMBER=$(basename $object | grep -o -E '[0-9]+') if [[ "$NUMBER" -gt "$MAX_NUMBER" ]]; then - MAX_NUMBER=$NUMBER + ARTIFACT_PATH=$object fi done -echo "Maximum number: $MAX_NUMBER" \ No newline at end of file +# Check if ARTIFACT_PATH is empty +if [[ -z "$ARTIFACT_PATH" ]]; then + echo "Error: No artifact path found" >&2 + exit 1 +fi + +echo "PATH: $ARTIFACT_PATH" + +# Set ARTIFACT_PATH as an output +echo "::set-output name=artifact-path::$ARTIFACT_PATH" \ No newline at end of file From 001155a3ceecdd6d0cfac5f01df9df450ae13578 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 19:57:10 -0600 Subject: [PATCH 053/119] Fixing script path --- .github/workflows/pull-request.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index a033e7a..5b6ec72 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -180,23 +180,23 @@ jobs: - name: Find latest Event Handler artifact id: eventHandlerArtifact - run: ./scripts/find_latest_artifact.sh event-handler ${{ vars.S3_BUCKET_NAME }} development + run: ../../scripts/find_latest_artifact.sh event-handler ${{ vars.S3_BUCKET_NAME }} development - name: Find latest Event Publisher artifact id: eventPublisherArtifact - run: ./scripts/find_latest_artifact.sh event-publisher ${{ vars.S3_BUCKET_NAME }} development + run: ../../scripts/find_latest_artifact.sh event-publisher ${{ vars.S3_BUCKET_NAME }} development - name: Find latest Create Team artifact id: createTeamArtifact - run: ./scripts/find_latest_artifact.sh create-team ${{ vars.S3_BUCKET_NAME }} development + run: ../../scripts/find_latest_artifact.sh create-team ${{ vars.S3_BUCKET_NAME }} development - name: Find latest Retrieve Teams artifact id: retrieveTeamsArtifact - run: ./scripts/find_latest_artifact.sh retrieve-teams ${{ vars.S3_BUCKET_NAME }} development + run: ../../scripts/find_latest_artifact.sh retrieve-teams ${{ vars.S3_BUCKET_NAME }} development - name: Find latest Is Tournament Eligible artifact id: isTournamentEligibleArtifact - run: ./scripts/find_latest_artifact.sh is-tournament-eligible ${{ vars.S3_BUCKET_NAME }} development + run: ../../scripts/find_latest_artifact.sh is-tournament-eligible ${{ vars.S3_BUCKET_NAME }} development - name: Latest Artifacts run: | From b03cdc50b1bd2f498bdd2c8ad5637b9c60be98b1 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 20:00:39 -0600 Subject: [PATCH 054/119] Fixing script name --- scripts/{pull-latest-artifact.sh => find_latest_artifact.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts/{pull-latest-artifact.sh => find_latest_artifact.sh} (100%) diff --git a/scripts/pull-latest-artifact.sh b/scripts/find_latest_artifact.sh similarity index 100% rename from scripts/pull-latest-artifact.sh rename to scripts/find_latest_artifact.sh From c6c7d766369bec67469f5a4bcb87fd437aa5a610 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 20:02:18 -0600 Subject: [PATCH 055/119] Removing incorrect profile --- scripts/find_latest_artifact.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/find_latest_artifact.sh b/scripts/find_latest_artifact.sh index 88a696f..1fba753 100755 --- a/scripts/find_latest_artifact.sh +++ b/scripts/find_latest_artifact.sh @@ -5,7 +5,7 @@ ARTIFACT_NAME=${2} ENVIRONMENT=${3} # List objects in the S3 bucket path -OBJECTS=$(aws s3 ls s3://$BUCKET_NAME/artifacts/$ARTIFACT_NAME/$ENVIRONMENT --recursive --profile=Master | awk '{print $4}') +OBJECTS=$(aws s3 ls s3://$BUCKET_NAME/artifacts/$ARTIFACT_NAME/$ENVIRONMENT --recursive | awk '{print $4}') # Extract numbers from the object names and find the maximum number artifact ARTIFACT_PATH="" From 78bc6d67d8c501596560da29f5731e1df6f7e3f1 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 20:05:12 -0600 Subject: [PATCH 056/119] Fixing arguement order --- .github/workflows/pull-request.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 5b6ec72..d400686 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -180,23 +180,23 @@ jobs: - name: Find latest Event Handler artifact id: eventHandlerArtifact - run: ../../scripts/find_latest_artifact.sh event-handler ${{ vars.S3_BUCKET_NAME }} development + run: ../../scripts/find_latest_artifact.sh ${{ vars.S3_BUCKET_NAME }} event-handler development - name: Find latest Event Publisher artifact id: eventPublisherArtifact - run: ../../scripts/find_latest_artifact.sh event-publisher ${{ vars.S3_BUCKET_NAME }} development + run: ../../scripts/find_latest_artifact.sh ${{ vars.S3_BUCKET_NAME }} event-publisher development - name: Find latest Create Team artifact id: createTeamArtifact - run: ../../scripts/find_latest_artifact.sh create-team ${{ vars.S3_BUCKET_NAME }} development + run: ../../scripts/find_latest_artifact.sh ${{ vars.S3_BUCKET_NAME }} create-team development - name: Find latest Retrieve Teams artifact id: retrieveTeamsArtifact - run: ../../scripts/find_latest_artifact.sh retrieve-teams ${{ vars.S3_BUCKET_NAME }} development + run: ../../scripts/find_latest_artifact.sh ${{ vars.S3_BUCKET_NAME }} retrieve-teams development - name: Find latest Is Tournament Eligible artifact id: isTournamentEligibleArtifact - run: ../../scripts/find_latest_artifact.sh is-tournament-eligible ${{ vars.S3_BUCKET_NAME }} development + run: ../../scripts/find_latest_artifact.sh ${{ vars.S3_BUCKET_NAME }} is-tournament-eligible development - name: Latest Artifacts run: | From 1f73287706abe3a48f23c4807b958b665050068f Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 20:12:46 -0600 Subject: [PATCH 057/119] Adding terraform apply back in and adding in versions used to PR comment --- .github/workflows/pull-request.yml | 140 ++++++++++++++--------------- scripts/find_latest_artifact.sh | 6 +- 2 files changed, 75 insertions(+), 71 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index d400686..6fbd1c1 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -198,76 +198,76 @@ jobs: id: isTournamentEligibleArtifact run: ../../scripts/find_latest_artifact.sh ${{ vars.S3_BUCKET_NAME }} is-tournament-eligible development - - name: Latest Artifacts - run: | - echo "Event Handler: ${{ steps.eventHandlerArtifact.outputs.artifact-path }}" - echo "Event Publisher: ${{ steps.eventPublisherArtifact.outputs.artifact-path }}" - echo "Create Team: ${{ steps.createTeamArtifact.outputs.artifact-path }}" - echo "Retrieve Teams: ${{ steps.retrieveTeamsArtifact.outputs.artifact-path }}" - echo "Is Tournament Eligible: ${{ steps.isTournamentEligibleArtifact.outputs.artifact-path }}" - - # - name: Setup Terraform - # uses: hashicorp/setup-terraform@v1 - # with: - # cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} - - # - name: Terraform Format - # id: fmt - # run: terraform fmt -check - - # - name: Terraform Init - # id: init - # run: terraform init -backend-config=backend-configs/${{ vars.ENVIRONMENT }}.remote.tfbackend - - # - name: Terraform Validate - # id: validate - # run: terraform validate -no-color - - # - name: Apply - # id: apply - # env: - # TF_VAR_region: us-east-1 - # TF_VAR_environment: ${{ vars.ENVIRONMENT }} - # TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} - # TF_VAR_event_publisher_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-publisher.zip - # TF_VAR_event_handler_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/event-handler.zip - # TF_VAR_create_team_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/create-team.zip - # TF_VAR_retrieve_teams_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/retrieve-teams.zip - # TF_VAR_tournament_eligibility_lambda_artifact_path: artifacts/${{ vars.ENVIRONMENT }}/${{ github.run_number }}/is-tournament-eligible.zip - # TF_VAR_sqs_batch_size: "1" - # run: terraform apply -no-color -input=false --auto-approve - - # - name: Update Pull Request - # uses: actions/github-script@v6.1.0 - # if: github.event_name == 'pull_request' - # env: - # APPLY: "terraform\n${{ steps.apply.outputs.stdout }}" - # with: - # github-token: ${{ secrets.GITHUB_TOKEN }} - # script: | - # const output = `Clash Bot Workflow - Apply - # #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` - # #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` - # #### Workflow Apply 📖\`${{ steps.apply.outcome }}\` - # #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` - - #
Show Run - - # #### Workflow Apply - # \`\`\`\n - # ${process.env.APPLY} - # \`\`\` - - #
- - # *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; - - # github.rest.issues.createComment({ - # issue_number: context.issue.number, - # owner: context.repo.owner, - # repo: context.repo.repo, - # body: output - # }) + - name: Setup Terraform + uses: hashicorp/setup-terraform@v1 + with: + cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} + + - name: Terraform Format + id: fmt + run: terraform fmt -check + + - name: Terraform Init + id: init + run: terraform init -backend-config=backend-configs/${{ vars.ENVIRONMENT }}.remote.tfbackend + + - name: Terraform Validate + id: validate + run: terraform validate -no-color + + - name: Apply + id: apply + env: + TF_VAR_region: us-east-1 + TF_VAR_environment: ${{ vars.ENVIRONMENT }} + TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} + TF_VAR_event_publisher_artifact_path: ${{ steps.eventPublisherArtifact.outputs.artifact-path }} + TF_VAR_event_handler_artifact_path: ${{ steps.eventHandlerArtifact.outputs.artifact-path }} + TF_VAR_create_team_artifact_path: ${{ steps.createTeamArtifact.outputs.artifact-path }} + TF_VAR_retrieve_teams_artifact_path: ${{ steps.retrieveTeamsArtifact.outputs.artifact-path }} + TF_VAR_tournament_eligibility_lambda_artifact_path: ${{ steps.isTournamentEligibleArtifact.outputs.artifact-path }} + TF_VAR_sqs_batch_size: "1" + run: terraform apply -no-color -input=false --auto-approve + + - name: Update Pull Request + uses: actions/github-script@v6.1.0 + if: github.event_name == 'pull_request' + env: + APPLY: "terraform\n${{ steps.apply.outputs.stdout }}" + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const output = `Clash Bot Workflow - Apply + #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` + #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` + #### Workflow Apply 📖\`${{ steps.apply.outcome }}\` + #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` + + | Lambda Function | Artifact Version | + | --------------- | ---------------- | + | Event Publisher | ${{ steps.eventPublisherArtifact.outputs.version }} | + | Event Handler | ${{ steps.eventHandlerArtifact.outputs.version }} | + | Create Team | ${{ steps.createTeamArtifact.outputs.version }} | + | Retrieve Teams | ${{ steps.retrieveTeamsArtifact.outputs.version }} | + | Is Tournament Eligible | ${{ steps.isTournamentEligibleArtifact.outputs.version }} | + +
Show Run + + #### Workflow Apply + \`\`\`\n + ${process.env.APPLY} + \`\`\` + +
+ + *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output + }) # Commenting for now until I can cut down on graph size # - name: Install Graphviz diff --git a/scripts/find_latest_artifact.sh b/scripts/find_latest_artifact.sh index 1fba753..3d50020 100755 --- a/scripts/find_latest_artifact.sh +++ b/scripts/find_latest_artifact.sh @@ -9,10 +9,12 @@ OBJECTS=$(aws s3 ls s3://$BUCKET_NAME/artifacts/$ARTIFACT_NAME/$ENVIRONMENT --re # Extract numbers from the object names and find the maximum number artifact ARTIFACT_PATH="" +VERSION=0 for object in $OBJECTS; do NUMBER=$(basename $object | grep -o -E '[0-9]+') if [[ "$NUMBER" -gt "$MAX_NUMBER" ]]; then ARTIFACT_PATH=$object + VERSION=$NUMBER fi done @@ -23,6 +25,8 @@ if [[ -z "$ARTIFACT_PATH" ]]; then fi echo "PATH: $ARTIFACT_PATH" +echo "ARTIFACT_VERSION: $VERSION" # Set ARTIFACT_PATH as an output -echo "::set-output name=artifact-path::$ARTIFACT_PATH" \ No newline at end of file +echo "::set-output name=artifact-path::$ARTIFACT_PATH" +echo "::set-output name=version::$VERSION" \ No newline at end of file From 9395c7585ccb9062388351f440aba3cd417bab12 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 20:23:19 -0600 Subject: [PATCH 058/119] Updating state machine to use is eligible function --- .github/workflows/pull-request.yml | 13 ++++++++----- .../create-team-step-function.asl.json | 18 +++++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 6fbd1c1..67fcf6c 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -237,11 +237,9 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const output = `Clash Bot Workflow - Apply - #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` - #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` - #### Workflow Apply 📖\`${{ steps.apply.outcome }}\` - #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` + const output = `# Clash Bot Workflow PR Details + + ## Lambda Function Versions used | Lambda Function | Artifact Version | | --------------- | ---------------- | @@ -251,6 +249,11 @@ jobs: | Retrieve Teams | ${{ steps.retrieveTeamsArtifact.outputs.version }} | | Is Tournament Eligible | ${{ steps.isTournamentEligibleArtifact.outputs.version }} | + #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` + #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` + #### Workflow Apply 📖\`${{ steps.apply.outcome }}\` + #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` +
Show Run #### Workflow Apply diff --git a/terraform/workflow/step-functions/create-team-step-function.asl.json b/terraform/workflow/step-functions/create-team-step-function.asl.json index b3afa56..eaf94cc 100644 --- a/terraform/workflow/step-functions/create-team-step-function.asl.json +++ b/terraform/workflow/step-functions/create-team-step-function.asl.json @@ -1,16 +1,16 @@ { "Comment": "Creates a Clash Bot team for a Discord Service if the user is able to.", - "StartAt": "RetrieveTeamsForTournament", + "StartAt": "CheckIfTournamentIsEligible", "States": { - "RetrieveTeamsForTournament": { + "CheckIfTournamentIsEligible": { "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "Parameters": { "Payload": { - "tournamantName": "$.Payload.tournament.tournamentName", + "tournamant": "$.Payload.tournament.tournamentName", "tournamentDay": "$.Payload.tournament.tournamentDay" }, - "FunctionName": "${RetrieveTeamLambdaFunctionArn}" + "FunctionName": "${IsTournamentEligibleLambdaFunctionArn}" }, "OutputPath": "$.Payload", "Next": "CanTeamBeCreated", @@ -25,16 +25,16 @@ "Type": "Choice", "Choices": [ { - "Variable": "$.teamCreated", - "BooleanEquals": true, - "Next": "TeamAlreadyCreated" + "Variable": "$.Payload", + "BooleanEquals": false, + "Next": "TournamentIsNotEligible" } ], "Default": "CreateTeam" }, - "TeamAlreadyCreated": { + "TournamentIsNotEligible": { "Type": "Pass", - "Result": "Team already created", + "Result": "Tournamnet is not eligible for team creation.", "End": true }, "CreateTeam": { From 256101a7cf52b07d63eed7fac6a39ec10846ef9e Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 20:32:57 -0600 Subject: [PATCH 059/119] Adding link back to event handler --- terraform/workflow/step-functions.lambda.tf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index e66a40d..8acaa03 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -66,6 +66,12 @@ module "event_handler_lambda" { ) } +resource "aws_lambda_event_source_mapping" "event_handler_sqs_trigger" { + event_source_arn = module.clash_bot_event_sqs.queue_arn + function_name = module.event_handler_lambda.name + batch_size = 1 +} + module "event_publisher_lambda" { source = "./modules/lambda" From 2297c8855f4a8e6368a4e58d580235b5fb3b481f Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 20:41:32 -0600 Subject: [PATCH 060/119] Adding in appropriate permission for lambda functions to be invoked by step function --- terraform/workflow/step-functions.lambda.tf | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 8acaa03..d278ac1 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -144,4 +144,20 @@ resource "aws_lambda_permission" "apigw" { # The /*/* portion grants access from any method on any resource # within the API Gateway "REST API". source_arn = "${module.api_gateway.apigatewayv2_api_execution_arn}/*/*" +} + +resource "aws_lambda_permission" "tournament_eligibility_permission" { + statement_id = "TournamentEligibilityPermission-${lower(var.environment)}" + action = "lambda:InvokeFunction" + function_name = module.tournament_eligibility_lambda.name + principal = "states.amazonaws.com" + source_arn = module.create_team_step_function.state_machine_arn +} + +resource "aws_lambda_permission" "create_team_permission" { + statement_id = "CreateTeamPermission-${lower(var.environment)}" + action = "lambda:InvokeFunction" + function_name = module.create_team_step_function.name + principal = "states.amazonaws.com" + source_arn = module.create_team_step_function.state_machine_arn } \ No newline at end of file From 8be7b6c28371b718325085453726e635f6f1c480 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 20:48:34 -0600 Subject: [PATCH 061/119] Swapping to use the lambda --- terraform/workflow/step-functions.lambda.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index d278ac1..84a306b 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -157,7 +157,7 @@ resource "aws_lambda_permission" "tournament_eligibility_permission" { resource "aws_lambda_permission" "create_team_permission" { statement_id = "CreateTeamPermission-${lower(var.environment)}" action = "lambda:InvokeFunction" - function_name = module.create_team_step_function.name + function_name = module.create_team_lambda.name principal = "states.amazonaws.com" source_arn = module.create_team_step_function.state_machine_arn } \ No newline at end of file From 1e3637933a9ecc5a66df9a3b1122944cddaef9e3 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 21:04:41 -0600 Subject: [PATCH 062/119] Adding appropriate permissions for step function --- terraform/workflow/step-functions.lambda.tf | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 84a306b..d310698 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -13,6 +13,13 @@ module "create_team_step_function" { dynamodb = { dynamodb = [module.dynamodb_table.dynamodb_table_arn] } + lambda = { + lambda = [ + module.create_team_lambda.arn, + module.retrieve_team_lambda.arn, + module.tournament_eligibility_lambda.arn + ] + } } type = "STANDARD" From 5a2cd8a7e78f40a464589809fe24156597d4f193 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 21:21:31 -0600 Subject: [PATCH 063/119] Updating to parsing --- .../step-functions/create-team-step-function.asl.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/terraform/workflow/step-functions/create-team-step-function.asl.json b/terraform/workflow/step-functions/create-team-step-function.asl.json index eaf94cc..88a5920 100644 --- a/terraform/workflow/step-functions/create-team-step-function.asl.json +++ b/terraform/workflow/step-functions/create-team-step-function.asl.json @@ -7,12 +7,12 @@ "Resource": "arn:aws:states:::lambda:invoke", "Parameters": { "Payload": { - "tournamant": "$.Payload.tournament.tournamentName", + "tournamant": "$.tournament.tournamentName", "tournamentDay": "$.Payload.tournament.tournamentDay" }, "FunctionName": "${IsTournamentEligibleLambdaFunctionArn}" }, - "OutputPath": "$.Payload", + "ResultPath": "$.isTournamentEligible", "Next": "CanTeamBeCreated", "Catch": [ { @@ -25,7 +25,7 @@ "Type": "Choice", "Choices": [ { - "Variable": "$.Payload", + "Variable": "$.isTournamentEligible", "BooleanEquals": false, "Next": "TournamentIsNotEligible" } From eef95dfe9c58dcd5a1685cb18d1d7fbe3dbc43fd Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 21:55:55 -0600 Subject: [PATCH 064/119] Swapping output path --- .../workflow/step-functions/create-team-step-function.asl.json | 1 + 1 file changed, 1 insertion(+) diff --git a/terraform/workflow/step-functions/create-team-step-function.asl.json b/terraform/workflow/step-functions/create-team-step-function.asl.json index 88a5920..fadf9d5 100644 --- a/terraform/workflow/step-functions/create-team-step-function.asl.json +++ b/terraform/workflow/step-functions/create-team-step-function.asl.json @@ -13,6 +13,7 @@ "FunctionName": "${IsTournamentEligibleLambdaFunctionArn}" }, "ResultPath": "$.isTournamentEligible", + "OutputPath": "$.Payload", "Next": "CanTeamBeCreated", "Catch": [ { From 67272f6650f6e3e5c1b04e5b13faffdb16869b60 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 22:48:33 -0600 Subject: [PATCH 065/119] Updating to json payload for is eligible lambda --- .../team/is-tournament-eligible/src/handler.ts | 12 +++++++++--- .../is-tournament-eligible/tests/handler.test.ts | 12 +++++++++--- .../team/retrieve/ClashBotWorkflow.code-workspace | 2 +- .../create-team-step-function.asl.json | 6 +++--- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/functions/clash-bot/team/is-tournament-eligible/src/handler.ts b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts index 748e3c8..ef4df52 100644 --- a/functions/clash-bot/team/is-tournament-eligible/src/handler.ts +++ b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts @@ -12,7 +12,9 @@ export const handler: Handler = async (event, context) => { if (event.tournament === undefined && event.tournamentDay === undefined) { logger.info('No tournament or tournament day provided.'); - return false; + return { + isEligible: false + }; } const dynamoDBClient = new DynamoDBClient({}); @@ -73,7 +75,9 @@ export const handler: Handler = async (event, context) => { const queryCommandOutput = await dynamoDBClient.send(queryCommand); if (queryCommandOutput.Items === undefined || queryCommandOutput.Items.length === 0) { - return false; + return { + isEligible: false + }; } else { const currentDate = new Date(); currentDate.setHours(0, 0, 0, 0); @@ -87,6 +91,8 @@ export const handler: Handler = async (event, context) => { return tournamentIsEligible; }); - return tournaments.length > 0; + return { + isEligible: tournaments.length > 0 + }; } }; \ No newline at end of file diff --git a/functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts b/functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts index 64cad8d..42d63c4 100644 --- a/functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts +++ b/functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts @@ -26,7 +26,9 @@ describe('handler', () => { tournament: tournamentName }, {} as any, {} as any); - expect(result).toBe(true); + expect(result).toEqual({ + isEligible: true + }); expect(mock).toHaveReceivedCommandWith(QueryCommand, { TableName: process.env.TABLE_NAME, KeyConditionExpression: '#type = :type', @@ -66,7 +68,9 @@ describe('handler', () => { tournamentDay }, {} as any, {} as any); - expect(result).toBe(true); + expect(result).toEqual({ + isEligible: true + }); expect(mock).toHaveReceivedCommandWith(QueryCommand, { TableName: process.env.TABLE_NAME, KeyConditionExpression: '#type = :type', @@ -116,7 +120,9 @@ describe('handler', () => { tournamentDay }, {} as any, {} as any); - expect(result).toBe(true); + expect(result).toEqual({ + isEligible: true + }); expect(mock).toHaveReceivedCommandWith(QueryCommand, { TableName: process.env.TABLE_NAME, KeyConditionExpression: '#type = :type', diff --git a/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace index c4595d3..65f9b0c 100644 --- a/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace +++ b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace @@ -34,7 +34,7 @@ ], "settings": { "jest.disabledWorkspaceFolders": [ - "ClashBotWorkflow", + "can-create-team", "prod" ] } diff --git a/terraform/workflow/step-functions/create-team-step-function.asl.json b/terraform/workflow/step-functions/create-team-step-function.asl.json index fadf9d5..07c3239 100644 --- a/terraform/workflow/step-functions/create-team-step-function.asl.json +++ b/terraform/workflow/step-functions/create-team-step-function.asl.json @@ -7,13 +7,13 @@ "Resource": "arn:aws:states:::lambda:invoke", "Parameters": { "Payload": { - "tournamant": "$.tournament.tournamentName", - "tournamentDay": "$.Payload.tournament.tournamentDay" + "tournamant.$": "$.tournament.tournamentName", + "tournamentDay.$": "$.tournament.tournamentDay" }, "FunctionName": "${IsTournamentEligibleLambdaFunctionArn}" }, "ResultPath": "$.isTournamentEligible", - "OutputPath": "$.Payload", + "OutputPath": "$.isEligible", "Next": "CanTeamBeCreated", "Catch": [ { From cb39ff7b0543071d624007a4346a6c0711b1e4ac Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 22:56:08 -0600 Subject: [PATCH 066/119] Adding addtional logging --- .../clash-bot/team/is-tournament-eligible/src/handler.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/functions/clash-bot/team/is-tournament-eligible/src/handler.ts b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts index ef4df52..de158f6 100644 --- a/functions/clash-bot/team/is-tournament-eligible/src/handler.ts +++ b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts @@ -72,13 +72,17 @@ export const handler: Handler = async (event, context) => { const queryCommand = new QueryCommand(queryConditions); + logger.info("Sending query to DynamoDb..."); const queryCommandOutput = await dynamoDBClient.send(queryCommand); + logger.info({ queryCommandOutput }, "Recieved query response from DynamoDb."); if (queryCommandOutput.Items === undefined || queryCommandOutput.Items.length === 0) { + logger.info("No tournaments found."); return { isEligible: false }; } else { + logger.info({ tournaments: queryCommandOutput.Items }, "Tournaments found."); const currentDate = new Date(); currentDate.setHours(0, 0, 0, 0); const tournaments = queryCommandOutput.Items @@ -91,6 +95,8 @@ export const handler: Handler = async (event, context) => { return tournamentIsEligible; }); + logger.info({ tournaments }, 'Eligible tournaments found.'); + return { isEligible: tournaments.length > 0 }; From 6979b1f83e29f5e30bed75cc47f31bfb5bd41988 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 23:15:59 -0600 Subject: [PATCH 067/119] Adding in hash to restore key --- .github/workflows/build_and_deploy_lambda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index cdd7139..54854ea 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -61,7 +61,7 @@ jobs: path: ${{ inputs.working-directory}}/${{ inputs.artifact-name }}.zip key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/${{ inputs.artifact-name }}.zip') }} restore-keys: | - ${{ inputs.artifact-name }}-lambda- + ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/${{ inputs.artifact-name }}.zip') }} - name: Install dependencies if: steps.cache.outputs.cache-hit != 'true' From f3fcf01df61557dd14ecf4fa40a27da517856446 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Fri, 17 Nov 2023 23:58:26 -0600 Subject: [PATCH 068/119] Moving build and install before cache --- .github/workflows/build_and_deploy_lambda.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 54854ea..1dd469f 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -54,6 +54,12 @@ jobs: restore-keys: | ${{ runner.os }}-node- + - name: Install dependencies + run: npm install + + - name: Build Artifact + run: npm run build + - name: Cache lambda artifact id: cache uses: actions/cache@v2 @@ -63,18 +69,10 @@ jobs: restore-keys: | ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/${{ inputs.artifact-name }}.zip') }} - - name: Install dependencies - if: steps.cache.outputs.cache-hit != 'true' - run: npm install - - name: Test if: steps.cache.outputs.cache-hit != 'true' run: npm test - - name: Build Artifact - if: steps.cache.outputs.cache-hit != 'true' - run: npm run build - - name: Configure AWS Credentials if: steps.cache.outputs.cache-hit != 'true' uses: aws-actions/configure-aws-credentials@v3 From 86fc80ca60b6693891672b8a2f906825d6678fd2 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 00:36:43 -0600 Subject: [PATCH 069/119] Will this work? --- .github/workflows/build_and_deploy_lambda.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 1dd469f..a6ab4fe 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -65,9 +65,9 @@ jobs: uses: actions/cache@v2 with: path: ${{ inputs.working-directory}}/${{ inputs.artifact-name }}.zip - key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/${{ inputs.artifact-name }}.zip') }} + key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('**/${{ inputs.artifact-name }}.zip') }} restore-keys: | - ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/${{ inputs.artifact-name }}.zip') }} + ${{ inputs.artifact-name }}-lambda-${{ hashFiles('**/${{ inputs.artifact-name }}.zip') }} - name: Test if: steps.cache.outputs.cache-hit != 'true' From a38ef9e422ed252059ab7f6725ed7df04cbfa85b Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 00:46:09 -0600 Subject: [PATCH 070/119] Adding zip --- .github/workflows/build_and_deploy_lambda.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index a6ab4fe..45911b7 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -58,7 +58,9 @@ jobs: run: npm install - name: Build Artifact - run: npm run build + run: | + npm run build + zip -r ./${{ inputs.artifact-name}}.zip ./prod ./package.json - name: Cache lambda artifact id: cache @@ -83,5 +85,4 @@ jobs: - name: Archive and Publish if: steps.cache.outputs.cache-hit != 'true' run: | - zip -r ./${{ inputs.artifact-name}}.zip ./prod ./package.json ./artifact-name.txt aws s3 cp ./${{ inputs.artifact-name}}.zip s3://${{ inputs.s3-bucket-name }}/artifacts/${{ inputs.artifact-name}}/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/artifact-${{ github.run_number }}.zip From 5b560ddb6e80689c8243837d279c5d10f7c1863d Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 02:00:41 -0600 Subject: [PATCH 071/119] Making lambda aritifact in github actions generic --- .github/workflows/build_and_deploy_lambda.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 45911b7..84706ad 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -58,18 +58,21 @@ jobs: run: npm install - name: Build Artifact + env: + HASH: ${{ hashFiles('**/lambda-artifact.zip') }} run: | npm run build - zip -r ./${{ inputs.artifact-name}}.zip ./prod ./package.json + zip -r ./lambda-artifact.zip ./prod ./package.json + echo "Calulated hash: $HASH" - name: Cache lambda artifact id: cache uses: actions/cache@v2 with: - path: ${{ inputs.working-directory}}/${{ inputs.artifact-name }}.zip - key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('**/${{ inputs.artifact-name }}.zip') }} + path: ${{ inputs.working-directory}}/lambda-artifact.zip + key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('**/lambda-artifact.zip') }} restore-keys: | - ${{ inputs.artifact-name }}-lambda-${{ hashFiles('**/${{ inputs.artifact-name }}.zip') }} + ${{ inputs.artifact-name }}-lambda-${{ hashFiles('**/lambda-artifact.zip') }} - name: Test if: steps.cache.outputs.cache-hit != 'true' @@ -85,4 +88,4 @@ jobs: - name: Archive and Publish if: steps.cache.outputs.cache-hit != 'true' run: | - aws s3 cp ./${{ inputs.artifact-name}}.zip s3://${{ inputs.s3-bucket-name }}/artifacts/${{ inputs.artifact-name}}/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/artifact-${{ github.run_number }}.zip + aws s3 cp ./lambda-artifact.zip s3://${{ inputs.s3-bucket-name }}/artifacts/${{ inputs.artifact-name}}/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/artifact-${{ github.run_number }}.zip From 06c4be3b9be0cfba0c47a090c399c25b832fbaa0 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 02:06:51 -0600 Subject: [PATCH 072/119] Triggering build --- .github/workflows/build_and_deploy_lambda.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 84706ad..1f44c6f 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -58,12 +58,9 @@ jobs: run: npm install - name: Build Artifact - env: - HASH: ${{ hashFiles('**/lambda-artifact.zip') }} run: | npm run build zip -r ./lambda-artifact.zip ./prod ./package.json - echo "Calulated hash: $HASH" - name: Cache lambda artifact id: cache From 9419d0d9cb288f70dbe69c0cd2e330b269c8b2da Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 11:59:19 -0600 Subject: [PATCH 073/119] Will this work? --- .github/workflows/build_and_deploy_lambda.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 1f44c6f..e20148b 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -67,9 +67,9 @@ jobs: uses: actions/cache@v2 with: path: ${{ inputs.working-directory}}/lambda-artifact.zip - key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('**/lambda-artifact.zip') }} + key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('src/**,package.json') }} restore-keys: | - ${{ inputs.artifact-name }}-lambda-${{ hashFiles('**/lambda-artifact.zip') }} + ${{ inputs.artifact-name }}-lambda-${{ hashFiles('src/**,package.json') }} - name: Test if: steps.cache.outputs.cache-hit != 'true' From 2bbcba6cad0a89d861bcf3af8e608d78b9577204 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 12:02:57 -0600 Subject: [PATCH 074/119] Triggering build --- .github/workflows/build_and_deploy_lambda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index e20148b..6fbf8cf 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -85,4 +85,4 @@ jobs: - name: Archive and Publish if: steps.cache.outputs.cache-hit != 'true' run: | - aws s3 cp ./lambda-artifact.zip s3://${{ inputs.s3-bucket-name }}/artifacts/${{ inputs.artifact-name}}/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/artifact-${{ github.run_number }}.zip + aws s3 cp ./lambda-artifact.zip s3://${{ inputs.s3-bucket-name }}/artifacts/${{ inputs.artifact-name}}/$(echo ${{ inputs.environment-name }} | awk '{print tolower($0)}')/artifact-${{ github.run_number }}.zip \ No newline at end of file From ce848f87d8b4ff24d227090d705c2aada3285093 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 12:05:47 -0600 Subject: [PATCH 075/119] Testing out caching --- functions/clash-bot/team/is-tournament-eligible/src/handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/clash-bot/team/is-tournament-eligible/src/handler.ts b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts index de158f6..ace7ef0 100644 --- a/functions/clash-bot/team/is-tournament-eligible/src/handler.ts +++ b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts @@ -95,7 +95,7 @@ export const handler: Handler = async (event, context) => { return tournamentIsEligible; }); - logger.info({ tournaments }, 'Eligible tournaments found.'); + logger.info({ tournaments }, 'Eligible tournaments have been found.'); return { isEligible: tournaments.length > 0 From 5e38609ee5020162e87305e5499bfb83e43bb7d7 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 12:12:39 -0600 Subject: [PATCH 076/119] Adding working directory --- .github/workflows/build_and_deploy_lambda.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 6fbf8cf..ac574af 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -67,9 +67,9 @@ jobs: uses: actions/cache@v2 with: path: ${{ inputs.working-directory}}/lambda-artifact.zip - key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('src/**,package.json') }} + key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/src/**,${{ inputs.working-directory }}/package.json') }} restore-keys: | - ${{ inputs.artifact-name }}-lambda-${{ hashFiles('src/**,package.json') }} + ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/src/**,${{ inputs.working-directory }}/package.json') }} - name: Test if: steps.cache.outputs.cache-hit != 'true' From 4d5bf15c0ae6e6f6895600c8aa9cc436d075c091 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 12:20:59 -0600 Subject: [PATCH 077/119] Generating hash with shell --- .github/workflows/build_and_deploy_lambda.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index ac574af..c8e9aa2 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -62,14 +62,19 @@ jobs: npm run build zip -r ./lambda-artifact.zip ./prod ./package.json + - name: Generate hash + id: generate-hash + run: | + echo "::set-output name=hash::$(find ${{ inputs.working-directory }}/src ${{ inputs.working-directory }}/package.json -type f -exec sha256sum {} \; | sha256sum)" + - name: Cache lambda artifact id: cache uses: actions/cache@v2 with: path: ${{ inputs.working-directory}}/lambda-artifact.zip - key: ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/src/**,${{ inputs.working-directory }}/package.json') }} + key: ${{ inputs.artifact-name }}-lambda-${{ steps.generate-hash.outputs.hash }} restore-keys: | - ${{ inputs.artifact-name }}-lambda-${{ hashFiles('${{ inputs.working-directory }}/src/**,${{ inputs.working-directory }}/package.json') }} + ${{ inputs.artifact-name }}-lambda- - name: Test if: steps.cache.outputs.cache-hit != 'true' From 1730220a016927353098e3c9d1ffaebfe32e2791 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 13:22:41 -0600 Subject: [PATCH 078/119] Update build_and_deploy_lambda.yml --- .github/workflows/build_and_deploy_lambda.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index c8e9aa2..3839888 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -65,7 +65,8 @@ jobs: - name: Generate hash id: generate-hash run: | - echo "::set-output name=hash::$(find ${{ inputs.working-directory }}/src ${{ inputs.working-directory }}/package.json -type f -exec sha256sum {} \; | sha256sum)" + ls -lha ${{ inputs.working-directory }} + echo "::set-output name=hash::$(find ./${{ inputs.working-directory }}/src ./${{ inputs.working-directory }}/package.json -type f -exec sha256sum {} \; | sha256sum)" - name: Cache lambda artifact id: cache From 2a93c65f27fbd2a28a0409e7f0c927da1823264e Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 15:48:57 -0600 Subject: [PATCH 079/119] Update build_and_deploy_lambda.yml --- .github/workflows/build_and_deploy_lambda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 3839888..064d1bc 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -65,7 +65,7 @@ jobs: - name: Generate hash id: generate-hash run: | - ls -lha ${{ inputs.working-directory }} + ls -lha ./${{ inputs.working-directory }} echo "::set-output name=hash::$(find ./${{ inputs.working-directory }}/src ./${{ inputs.working-directory }}/package.json -type f -exec sha256sum {} \; | sha256sum)" - name: Cache lambda artifact From 94cabdff1d75d0fcf2fc92487fea456464faa881 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 15:53:19 -0600 Subject: [PATCH 080/119] Update build_and_deploy_lambda.yml --- .github/workflows/build_and_deploy_lambda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 064d1bc..4d20655 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -65,7 +65,7 @@ jobs: - name: Generate hash id: generate-hash run: | - ls -lha ./${{ inputs.working-directory }} + ls -lha echo "::set-output name=hash::$(find ./${{ inputs.working-directory }}/src ./${{ inputs.working-directory }}/package.json -type f -exec sha256sum {} \; | sha256sum)" - name: Cache lambda artifact From a81ade7da3de89b1f009f782e8b1d915097ea3db Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 15:57:49 -0600 Subject: [PATCH 081/119] Update build_and_deploy_lambda.yml --- .github/workflows/build_and_deploy_lambda.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 4d20655..bc52872 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -66,7 +66,7 @@ jobs: id: generate-hash run: | ls -lha - echo "::set-output name=hash::$(find ./${{ inputs.working-directory }}/src ./${{ inputs.working-directory }}/package.json -type f -exec sha256sum {} \; | sha256sum)" + echo "::set-output name=hash::$(find src/* package.json -type f -exec sha256sum {} \; | sha256sum)" - name: Cache lambda artifact id: cache From c7a44f6f0129f4e60af84e10751619928b5e23fc Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 16:01:40 -0600 Subject: [PATCH 082/119] Update handler.ts --- functions/clash-bot/team/is-tournament-eligible/src/handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/clash-bot/team/is-tournament-eligible/src/handler.ts b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts index ace7ef0..de158f6 100644 --- a/functions/clash-bot/team/is-tournament-eligible/src/handler.ts +++ b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts @@ -95,7 +95,7 @@ export const handler: Handler = async (event, context) => { return tournamentIsEligible; }); - logger.info({ tournaments }, 'Eligible tournaments have been found.'); + logger.info({ tournaments }, 'Eligible tournaments found.'); return { isEligible: tournaments.length > 0 From 739817b4f97adf7f0905550a2738fd92e2115d1c Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 16:17:23 -0600 Subject: [PATCH 083/119] Update build_and_deploy_lambda.yml --- .github/workflows/build_and_deploy_lambda.yml | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index bc52872..68441f6 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -41,13 +41,30 @@ jobs: - name: Checkout uses: actions/checkout@v2 + - name: Generate hash + id: generate-hash + run: | + ls -lha + echo "::set-output name=hash::$(find src/* package.json -type f -exec sha256sum {} \; | sha256sum)" + + - name: Cache lambda artifact + id: cache + uses: actions/cache@v2 + with: + path: ${{ inputs.working-directory}}/lambda-artifact.zip + key: ${{ inputs.artifact-name }}-lambda-${{ steps.generate-hash.outputs.hash }} + restore-keys: | + ${{ inputs.artifact-name }}-lambda- + - name: Setup Node.js + if: steps.cache.outputs.cache-hit != 'true' uses: actions/setup-node@v2 with: node-version: '16' - name: Cache Node.js modules uses: actions/cache@v2 + if: steps.cache.outputs.cache-hit != 'true' with: path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} @@ -58,25 +75,11 @@ jobs: run: npm install - name: Build Artifact + if: steps.cache.outputs.cache-hit != 'true' run: | npm run build zip -r ./lambda-artifact.zip ./prod ./package.json - - name: Generate hash - id: generate-hash - run: | - ls -lha - echo "::set-output name=hash::$(find src/* package.json -type f -exec sha256sum {} \; | sha256sum)" - - - name: Cache lambda artifact - id: cache - uses: actions/cache@v2 - with: - path: ${{ inputs.working-directory}}/lambda-artifact.zip - key: ${{ inputs.artifact-name }}-lambda-${{ steps.generate-hash.outputs.hash }} - restore-keys: | - ${{ inputs.artifact-name }}-lambda- - - name: Test if: steps.cache.outputs.cache-hit != 'true' run: npm test From 95b490e62312825bf30ca9801bfb6dff869410d6 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 16:20:38 -0600 Subject: [PATCH 084/119] Update build_and_deploy_lambda.yml --- .github/workflows/build_and_deploy_lambda.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_and_deploy_lambda.yml b/.github/workflows/build_and_deploy_lambda.yml index 68441f6..c67bb57 100644 --- a/.github/workflows/build_and_deploy_lambda.yml +++ b/.github/workflows/build_and_deploy_lambda.yml @@ -72,6 +72,7 @@ jobs: ${{ runner.os }}-node- - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' run: npm install - name: Build Artifact From caf6a56698315695b0ac9a8b7c938eed6fe7055a Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 18 Nov 2023 16:22:42 -0600 Subject: [PATCH 085/119] Update handler.ts --- functions/clash-bot/event-handler/src/handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/clash-bot/event-handler/src/handler.ts b/functions/clash-bot/event-handler/src/handler.ts index bd260b6..3913d1b 100644 --- a/functions/clash-bot/event-handler/src/handler.ts +++ b/functions/clash-bot/event-handler/src/handler.ts @@ -17,7 +17,7 @@ export const handler: SQSHandler = async (event: SQSEvent) => { const sfArn = eventMap.get(parsedEvent.event); if (!sfArn) { - logger.error(`Unmapped event found event=${parsedEvent.event}`); + logger.error(`Unmapped event found event=${parsedEvent.event}!`); return null; } From a333ecc3cea49eaa856ce9b372145f2ff006f9de Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 03:00:28 -0600 Subject: [PATCH 086/119] Adding in event notifier lambda --- .github/workflows/pull-request.yml | 18 ++ .../clash-bot/event-notifier/jest.config.js | 6 + .../clash-bot/event-notifier/package.json | 35 ++++ .../clash-bot/event-notifier/src/handler.ts | 68 +++++++ .../tests/event-notifier.test.ts | 168 ++++++++++++++++++ .../clash-bot/event-notifier/tsconfig.json | 24 +++ .../retrieve/ClashBotWorkflow.code-workspace | 4 +- terraform/workflow/api-gateway.tf | 22 --- terraform/workflow/dynamodb.tf | 43 +++++ terraform/workflow/event-setup-lambdas.tf | 74 ++++++++ terraform/workflow/modules/lambda/outputs.tf | 5 + .../event-notifier-lambda-policy.json | 27 +++ terraform/workflow/step-functions.lambda.tf | 54 ------ terraform/workflow/variables.tf | 5 + terraform/workflow/ws-api-gateway.tf | 29 +++ 15 files changed, 504 insertions(+), 78 deletions(-) create mode 100644 functions/clash-bot/event-notifier/jest.config.js create mode 100644 functions/clash-bot/event-notifier/package.json create mode 100644 functions/clash-bot/event-notifier/src/handler.ts create mode 100644 functions/clash-bot/event-notifier/tests/event-notifier.test.ts create mode 100644 functions/clash-bot/event-notifier/tsconfig.json create mode 100644 terraform/workflow/dynamodb.tf create mode 100644 terraform/workflow/event-setup-lambdas.tf create mode 100644 terraform/workflow/policies/event-notifier-lambda-policy.json create mode 100644 terraform/workflow/ws-api-gateway.tf diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 67fcf6c..2066270 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -114,6 +114,17 @@ jobs: needs: - terraformPreReqs + eventNotifier: + uses: ./.github/workflows/build_and_deploy_lambda.yml + with: + working-directory: functions/clash-bot/event-notifier + artifact-name: event-notifier + s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} + environment-name: Development + region: us-east-1 + needs: + - terraformPreReqs + createTeam: uses: ./.github/workflows/build_and_deploy_lambda.yml with: @@ -162,6 +173,7 @@ jobs: needs: - eventHandler - eventPublisher + - eventNotifier - createTeam - retrieveTeams - isTournamentEligible @@ -198,6 +210,10 @@ jobs: id: isTournamentEligibleArtifact run: ../../scripts/find_latest_artifact.sh ${{ vars.S3_BUCKET_NAME }} is-tournament-eligible development + - name: Find latest Event Notifier artifact + id: eventNotifierArtifact + run: ../../scripts/find_latest_artifact.sh ${{ vars.S3_BUCKET_NAME }} event-notifier development + - name: Setup Terraform uses: hashicorp/setup-terraform@v1 with: @@ -223,6 +239,7 @@ jobs: TF_VAR_s3_bucket_name: ${{ vars.S3_BUCKET_NAME }} TF_VAR_event_publisher_artifact_path: ${{ steps.eventPublisherArtifact.outputs.artifact-path }} TF_VAR_event_handler_artifact_path: ${{ steps.eventHandlerArtifact.outputs.artifact-path }} + TF_VAR_event_notifier_artifact_path: ${{ steps.eventNotifierArtifact.outputs.artifact-path }} TF_VAR_create_team_artifact_path: ${{ steps.createTeamArtifact.outputs.artifact-path }} TF_VAR_retrieve_teams_artifact_path: ${{ steps.retrieveTeamsArtifact.outputs.artifact-path }} TF_VAR_tournament_eligibility_lambda_artifact_path: ${{ steps.isTournamentEligibleArtifact.outputs.artifact-path }} @@ -245,6 +262,7 @@ jobs: | --------------- | ---------------- | | Event Publisher | ${{ steps.eventPublisherArtifact.outputs.version }} | | Event Handler | ${{ steps.eventHandlerArtifact.outputs.version }} | + | Event Notifier | ${{ steps.eventNotifierArtifact.outputs.version }} | | Create Team | ${{ steps.createTeamArtifact.outputs.version }} | | Retrieve Teams | ${{ steps.retrieveTeamsArtifact.outputs.version }} | | Is Tournament Eligible | ${{ steps.isTournamentEligibleArtifact.outputs.version }} | diff --git a/functions/clash-bot/event-notifier/jest.config.js b/functions/clash-bot/event-notifier/jest.config.js new file mode 100644 index 0000000..85c664c --- /dev/null +++ b/functions/clash-bot/event-notifier/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + transform: {'^.+\\.ts?$': 'ts-jest'}, + testEnvironment: 'node', + testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] + }; \ No newline at end of file diff --git a/functions/clash-bot/event-notifier/package.json b/functions/clash-bot/event-notifier/package.json new file mode 100644 index 0000000..3badbab --- /dev/null +++ b/functions/clash-bot/event-notifier/package.json @@ -0,0 +1,35 @@ +{ + "name": "event-notifier", + "version": "1.0.0", + "description": "A lambda that that notifies users of events related to their requests", + "main": "index.js", + "scripts": { + "test": "jest", + "build": "tsc", + "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + }, + "keywords": [ + "clash-bot" + ], + "author": "Poss111", + "license": "ISC", + "devDependencies": { + "@aws-sdk/types": "^3.398.0", + "@types/aws-lambda": "^8.10.119", + "@types/jest": "^29.5.4", + "@types/pino": "^7.0.5", + "aws-sdk-client-mock": "^3.0.0", + "aws-sdk-client-mock-jest": "^3.0.0", + "jest": "^29.6.4", + "pino-pretty": "^10.2.0", + "ts-jest": "^29.1.1", + "ts-mockito": "^2.6.1", + "typescript": "^5.2.2" + }, + "dependencies": { + "@aws-sdk/client-dynamodb": "^3.398.0", + "dotenv": "^16.3.1", + "pino": "^8.15.0", + "clash-bot-shared": "github:Poss111/ClashBotWorkflowShared#main" + } +} diff --git a/functions/clash-bot/event-notifier/src/handler.ts b/functions/clash-bot/event-notifier/src/handler.ts new file mode 100644 index 0000000..bbec12c --- /dev/null +++ b/functions/clash-bot/event-notifier/src/handler.ts @@ -0,0 +1,68 @@ +import { DeleteItemCommand, DeleteItemCommandInput, DynamoDBClient, PutItemCommand, PutItemCommandInput } from '@aws-sdk/client-dynamodb'; +import { APIGatewayProxyResultV2, APIGatewayProxyWebsocketEventV2, APIGatewayProxyWebsocketHandlerV2 } from 'aws-lambda'; +import pino from "pino"; + +export const handler: APIGatewayProxyWebsocketHandlerV2 = async (event: APIGatewayProxyWebsocketEventV2): Promise> => { + const level = process.env.LOGGER_LEVEL === undefined ? "info" : process.env.LOGGER_LEVEL; + const logger = pino({ level }); + logger.info({ + event + }, "Received event..."); + + const { + requestContext: { connectionId, routeKey }, + } = event; + + logger.info(routeKey, "Received route key..."); + + const client = new DynamoDBClient({}); + + if ('$connect' === routeKey) { + logger.info("Received connect event..."); + const persistItem: PutItemCommandInput = { + TableName: process.env.TABLE_NAME, + Item: { + "connectionId": { S: connectionId }, + "context": { S: "TBA" } + } + }; + await client.send(new PutItemCommand(persistItem)); + return { + statusCode: 200, + body: JSON.stringify({ message: 'Connected' }), + }; + } else if ('$disconnect' === routeKey) { + logger.info("Received disconnect event..."); + const deleteItem: DeleteItemCommandInput = { + TableName: process.env.TABLE_NAME, + Key: { + "connectionId": { S: connectionId } + } + }; + await client.send(new DeleteItemCommand(deleteItem)); + return { + statusCode: 200, + body: JSON.stringify({ message: 'Disconnected' }), + }; + } else if ('$message' === routeKey) { + logger.info("Received message event..."); + const persistItem: PutItemCommandInput = { + TableName: process.env.TABLE_NAME, + Item: { + "connectionId": { S: connectionId }, + "context": { S: "TBA" } + } + }; + await client.send(new PutItemCommand(persistItem)); + return { + statusCode: 200, + body: JSON.stringify({ message: 'Message received' }), + }; + } + + // Default response + return { + statusCode: 200, + body: JSON.stringify({ message: 'Success' }), + }; +}; diff --git a/functions/clash-bot/event-notifier/tests/event-notifier.test.ts b/functions/clash-bot/event-notifier/tests/event-notifier.test.ts new file mode 100644 index 0000000..329e612 --- /dev/null +++ b/functions/clash-bot/event-notifier/tests/event-notifier.test.ts @@ -0,0 +1,168 @@ +import { APIGatewayProxyEvent, APIGatewayProxyResult, APIGatewayProxyResultV2, APIGatewayProxyWebsocketEventV2 } from 'aws-lambda'; +import { handler } from '../src/handler'; +import { DeleteItemCommand, DeleteItemCommandInput, DynamoDBClient, PutItemCommand, PutItemCommandInput } from '@aws-sdk/client-dynamodb'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; + +describe('Handle Websocket connection requests', () => { + + test('If a websocket connection reqeust is recieved, it should post the details to DynamoDb to save the state.', async () => { + const event = createMockConnectEvent(); + const context = setupContext(); + + const client = mockClient(DynamoDBClient); + + client.on(PutItemCommand) + .resolves({}); + + const expectedPutCommand: PutItemCommandInput = { + TableName: process.env.TABLE_NAME, + Item: { + "connectionId": { S: event.requestContext.connectionId }, + "context": { S: "TBA" } + } + }; + + const result = await handler(event, context, () => { }); + expect(result).toBeDefined(); + expect((result as any).statusCode).toBe(200); + expect((result as any).body).toBe('{\"message\":\"Connected\"}'); + expect(client).toHaveReceivedCommandWith(PutItemCommand, expectedPutCommand); + }); + + test('If a websocket disconnect reqeust is recieved, it should post the details to DynamoDb to save the state.', async () => { + const event = createMockDisconnectEvent(); + const context = setupContext(); + + const client = mockClient(DynamoDBClient); + + client.on(DeleteItemCommand) + .resolves({}); + + const expectedDeleteCommand: DeleteItemCommandInput = { + TableName: process.env.TABLE_NAME, + Key: { + "connectionId": { S: event.requestContext.connectionId } + } + }; + + const result = await handler(event, context, () => { }); + expect(result).toBeDefined(); + expect((result as any).statusCode).toBe(200); + expect((result as any).body).toBe('{\"message\":\"Disconnected\"}'); + expect(client).toHaveReceivedCommandWith(DeleteItemCommand, expectedDeleteCommand); + }); + + test('If a websocket message reqeust is recieved, it should post the details to DynamoDb to save the state.', async () => { + const event = createMockMessageEvent(); + const context = setupContext(); + + const client = mockClient(DynamoDBClient); + + client.on(PutItemCommand) + .resolves({}); + + const expectedPutCommand: PutItemCommandInput = { + TableName: process.env.TABLE_NAME, + Item: { + "connectionId": { S: event.requestContext.connectionId }, + "context": { S: "TBA" } + } + }; + + const result = await handler(event, context, () => { }); + expect(result).toBeDefined(); + expect((result as any).statusCode).toBe(200); + expect((result as any).body).toBe('{\"message\":\"Message received\"}'); + expect(client).toHaveReceivedCommandWith(PutItemCommand, expectedPutCommand); + }); + +}); + +const setupContext = () => { + return { + callbackWaitsForEmptyEventLoop: false, + functionName: '', + functionVersion: '', + invokedFunctionArn: '', + memoryLimitInMB: '', + awsRequestId: '', + logGroupName: '', + logStreamName: '', + getRemainingTimeInMillis: function (): number { + throw new Error('Function not implemented.'); + }, + done: function (error?: Error | undefined, result?: any): void { + throw new Error('Function not implemented.'); + }, + fail: function (error: string | Error): void { + throw new Error('Function not implemented.'); + }, + succeed: function (messageOrObject: any): void { + throw new Error('Function not implemented.'); + } + }; +} + +const createMockConnectEvent = (): APIGatewayProxyWebsocketEventV2 => { + return { + requestContext: { + apiId: 'sampleApiId', + domainName: 'sampleDomainName', + requestId: 'sampleRequestId', + routeKey: '$connect', + stage: 'dev', + messageId: 'sampleMessageId', + eventType: 'CONNECT', + extendedRequestId: 'sampleExtendedRequestId', + requestTime: 'sampleRequestTime', + messageDirection: 'IN', + connectedAt: Date.now(), + requestTimeEpoch: Date.now(), + connectionId: 'sampleConnectionId' + }, + isBase64Encoded: false + }; +}; + +const createMockDisconnectEvent = (): APIGatewayProxyWebsocketEventV2 => { + return { + requestContext: { + apiId: 'sampleApiId', + domainName: 'sampleDomainName', + requestId: 'sampleRequestId', + routeKey: '$disconnect', + stage: 'dev', + messageId: 'sampleMessageId', + eventType: 'CONNECT', + extendedRequestId: 'sampleExtendedRequestId', + requestTime: 'sampleRequestTime', + messageDirection: 'IN', + connectedAt: Date.now(), + requestTimeEpoch: Date.now(), + connectionId: 'sampleConnectionId' + }, + isBase64Encoded: false + }; +}; + +const createMockMessageEvent = (): APIGatewayProxyWebsocketEventV2 => { + return { + requestContext: { + apiId: 'sampleApiId', + domainName: 'sampleDomainName', + requestId: 'sampleRequestId', + routeKey: '$message', + stage: 'dev', + messageId: 'sampleMessageId', + eventType: 'CONNECT', + extendedRequestId: 'sampleExtendedRequestId', + requestTime: 'sampleRequestTime', + messageDirection: 'IN', + connectedAt: Date.now(), + requestTimeEpoch: Date.now(), + connectionId: 'sampleConnectionId' + }, + isBase64Encoded: false + }; +}; \ No newline at end of file diff --git a/functions/clash-bot/event-notifier/tsconfig.json b/functions/clash-bot/event-notifier/tsconfig.json new file mode 100644 index 0000000..62859a2 --- /dev/null +++ b/functions/clash-bot/event-notifier/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es2020", + "strict": true, + "preserveConstEnums": true, + "noEmit": false, + "sourceMap": false, + "module":"commonjs", + "moduleResolution":"node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "rootDir": "./src", + "outDir": "prod", + "baseUrl": ".", + "paths": { + "clash-bot-shared": [ "node_modules/clash-bot-shared/src"], + "clash-bot-shared/*": [ "node_modules/clash-bot-shared/src/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.test.ts"], + } \ No newline at end of file diff --git a/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace index 65f9b0c..0f25a08 100644 --- a/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace +++ b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace @@ -28,8 +28,8 @@ "path": "../../event-handler" }, { - "name": "prod", - "path": "../../event-publisher/prod" + "name": "event-notifier", + "path": "../../event-notifier" } ], "settings": { diff --git a/terraform/workflow/api-gateway.tf b/terraform/workflow/api-gateway.tf index f0af987..2862a3f 100644 --- a/terraform/workflow/api-gateway.tf +++ b/terraform/workflow/api-gateway.tf @@ -41,25 +41,3 @@ data "aws_acm_certificate" "issued" { resource "aws_cloudwatch_log_group" "api_gateway_default_log_group" { name = "api_gateway_default_log_group-${var.environment}" } - -module "dynamodb_table" { - source = "terraform-aws-modules/dynamodb-table/aws" - - name = "clash-bot-workflow-${var.environment}" - hash_key = "type" - range_key = "id" - billing_mode = "PROVISIONED" - write_capacity = 5 - read_capacity = 1 - - attributes = [ - { - name = "type" - type = "S" - }, - { - name = "id" - type = "S" - } - ] -} diff --git a/terraform/workflow/dynamodb.tf b/terraform/workflow/dynamodb.tf new file mode 100644 index 0000000..e2433ce --- /dev/null +++ b/terraform/workflow/dynamodb.tf @@ -0,0 +1,43 @@ +module "dynamodb_table" { + source = "terraform-aws-modules/dynamodb-table/aws" + + name = "clash-bot-workflow-${var.environment}" + hash_key = "type" + range_key = "id" + billing_mode = "PROVISIONED" + write_capacity = 5 + read_capacity = 1 + + attributes = [ + { + name = "type" + type = "S" + }, + { + name = "id" + type = "S" + } + ] +} + +module "events_table" { + source = "terraform-aws-modules/dynamodb-table/aws" + + name = "clash-bot-events-${var.environment}" + hash_key = "connectionId" + range_key = "context" + billing_mode = "PROVISIONED" + write_capacity = 5 + read_capacity = 1 + + attributes = [ + { + name = "connectionId" + type = "S" + }, + { + name = "context" + type = "S" + } + ] +} \ No newline at end of file diff --git a/terraform/workflow/event-setup-lambdas.tf b/terraform/workflow/event-setup-lambdas.tf new file mode 100644 index 0000000..66b5da7 --- /dev/null +++ b/terraform/workflow/event-setup-lambdas.tf @@ -0,0 +1,74 @@ +module "event_handler_lambda" { + source = "./modules/lambda" + + prefix = "event-handler" + s3_bucket_name = var.s3_bucket_name + environment = var.environment + + artifact_path = var.event_handler_artifact_path + + environment_variables = { + CREATE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn + UPDATE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn + DELETE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn + CREATE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn + UPDATE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn + DELETE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn + } + + iam_policy_json = templatefile( + "${path.module}/policies/event-handler-lambda-policy.json", + { + CREATE_TEAM_STATE_MACHINE_ARN = module.create_team_step_function.state_machine_arn + SQS_ARN = module.clash_bot_event_sqs.queue_arn + } + ) +} + +resource "aws_lambda_event_source_mapping" "event_handler_sqs_trigger" { + event_source_arn = module.clash_bot_event_sqs.queue_arn + function_name = module.event_handler_lambda.name + batch_size = 1 +} + +module "event_publisher_lambda" { + source = "./modules/lambda" + + prefix = "event-publisher" + s3_bucket_name = var.s3_bucket_name + environment = var.environment + + artifact_path = var.event_publisher_artifact_path + + environment_variables = { + QUEUE_URL = module.clash_bot_event_sqs.queue_url + } + + iam_policy_json = templatefile( + "${path.module}/policies/event-publisher-lambda-policy.json", + { + SQS_ARN = module.clash_bot_event_sqs.queue_arn + } + ) +} + +module "event_notifier_lambda" { + source = "./modules/lambda" + + prefix = "event-notifier" + s3_bucket_name = var.s3_bucket_name + environment = var.environment + + artifact_path = var.event_notifier_artifact_path + + environment_variables = { + TABLE_NAME = module.events_table.dynamodb_table_id + } + + iam_policy_json = templatefile( + "${path.module}/policies/event-notifier-lambda-policy.json", + { + DYNAMODB_ARN = module.events_table.dynamodb_table_arn + } + ) +} \ No newline at end of file diff --git a/terraform/workflow/modules/lambda/outputs.tf b/terraform/workflow/modules/lambda/outputs.tf index 6406f25..4e252c8 100644 --- a/terraform/workflow/modules/lambda/outputs.tf +++ b/terraform/workflow/modules/lambda/outputs.tf @@ -6,4 +6,9 @@ output "arn" { output "name" { value = aws_lambda_function.lambda.function_name description = "The name for the lambda function." +} + +output "invoke_arn" { + value = aws_lambda_function.lambda.invoke_arn + description = "The invoke ARN for the lambda function." } \ No newline at end of file diff --git a/terraform/workflow/policies/event-notifier-lambda-policy.json b/terraform/workflow/policies/event-notifier-lambda-policy.json new file mode 100644 index 0000000..6d4cc8e --- /dev/null +++ b/terraform/workflow/policies/event-notifier-lambda-policy.json @@ -0,0 +1,27 @@ +{ + "Statement": [ + { + "Action": [ + "logs:PutLogEvents", + "logs:CreateLogStream", + "logs:CreateLogGroup" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "dynamodb:UpdateItem", + "dynamodb:Query", + "dynamodb:PutItem", + "dynamodb:GetItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem", + "dynamodb:BatchGetItem" + ], + "Effect": "Allow", + "Resource": "${DYNAMODB_ARN}" + } + ], + "Version": "2012-10-17" +} \ No newline at end of file diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index d310698..e06838f 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -46,60 +46,6 @@ module "create_team_lambda" { ) } -module "event_handler_lambda" { - source = "./modules/lambda" - - prefix = "event-handler" - s3_bucket_name = var.s3_bucket_name - environment = var.environment - - artifact_path = var.event_handler_artifact_path - - environment_variables = { - CREATE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn - UPDATE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn - DELETE_TEAM_SF_ARN = module.create_team_step_function.state_machine_arn - CREATE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn - UPDATE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn - DELETE_TENTATIVE_QUEUE_SF_ARN = module.create_team_step_function.state_machine_arn - } - - iam_policy_json = templatefile( - "${path.module}/policies/event-handler-lambda-policy.json", - { - CREATE_TEAM_STATE_MACHINE_ARN = module.create_team_step_function.state_machine_arn - SQS_ARN = module.clash_bot_event_sqs.queue_arn - } - ) -} - -resource "aws_lambda_event_source_mapping" "event_handler_sqs_trigger" { - event_source_arn = module.clash_bot_event_sqs.queue_arn - function_name = module.event_handler_lambda.name - batch_size = 1 -} - -module "event_publisher_lambda" { - source = "./modules/lambda" - - prefix = "event-publisher" - s3_bucket_name = var.s3_bucket_name - environment = var.environment - - artifact_path = var.event_publisher_artifact_path - - environment_variables = { - QUEUE_URL = module.clash_bot_event_sqs.queue_url - } - - iam_policy_json = templatefile( - "${path.module}/policies/event-publisher-lambda-policy.json", - { - SQS_ARN = module.clash_bot_event_sqs.queue_arn - } - ) -} - module "retrieve_team_lambda" { source = "./modules/lambda" diff --git a/terraform/workflow/variables.tf b/terraform/workflow/variables.tf index a4e3d0b..38098b8 100644 --- a/terraform/workflow/variables.tf +++ b/terraform/workflow/variables.tf @@ -25,6 +25,11 @@ variable "event_handler_artifact_path" { description = "Path to the artifact for the event handler lambda function." } +variable "event_notifier_artifact_path" { + type = string + description = "Path to the artifact for the event notifier lambda function." +} + variable "create_team_artifact_path" { type = string description = "Path to the artifact for the create team lambda function." diff --git a/terraform/workflow/ws-api-gateway.tf b/terraform/workflow/ws-api-gateway.tf new file mode 100644 index 0000000..6f1d66d --- /dev/null +++ b/terraform/workflow/ws-api-gateway.tf @@ -0,0 +1,29 @@ +resource "aws_apigatewayv2_api" "clash_bot_websocket_api" { + name = "clash-bot-workflow-ws-${var.environment}" + protocol_type = "WEBSOCKET" + route_selection_expression = "$request.body.action" +} + +resource "aws_apigatewayv2_route" "clash_bot_connection_route" { + api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id + route_key = "$connect" + target = "integrations/${aws_apigatewayv2_integration.clash_bot_websocket_api_integration.id}" +} + +resource "aws_apigatewayv2_integration" "clash_bot_websocket_api_integration" { + api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id + integration_type = "AWS_PROXY" + + connection_type = "INTERNET" + description = "Lambda integration" + integration_method = "POST" + integration_uri = module.event_notifier_lambda.invoke_arn +} + +resource "aws_lambda_permission" "clash_bot_ws_lambda_permission" { + action = "lambda:InvokeFunction" + function_name = module.event_notifier_lambda.name + principal = "apigateway.amazonaws.com" + + source_arn = "${aws_apigatewayv2_api.clash_bot_websocket_api.execution_arn}/*/*" +} \ No newline at end of file From 58c2bca9fdb147cec1ba2a036ef6fce0444e03fe Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 03:20:49 -0600 Subject: [PATCH 087/119] Adding in stage deployment for websocket --- terraform/workflow/ws-api-gateway.tf | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/terraform/workflow/ws-api-gateway.tf b/terraform/workflow/ws-api-gateway.tf index 6f1d66d..d60e27f 100644 --- a/terraform/workflow/ws-api-gateway.tf +++ b/terraform/workflow/ws-api-gateway.tf @@ -26,4 +26,21 @@ resource "aws_lambda_permission" "clash_bot_ws_lambda_permission" { principal = "apigateway.amazonaws.com" source_arn = "${aws_apigatewayv2_api.clash_bot_websocket_api.execution_arn}/*/*" +} + +resource "aws_apigatewayv2_deployment" "clash_bot_websocket_api_deployment" { + api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id + description = "Webscocket API deployment" + + depends_on = [ + aws_apigatewayv2_route.clash_bot_connection_route + ] +} + +resource "aws_apigatewayv2_stage" "clash_bot_websocket_api_stage" { + api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id + name = "events-${var.environment}" + description = "Clash Bot Workflow Websocket API stage" + deployment_id = aws_apigatewayv2_deployment.clash_bot_websocket_api_deployment.id + auto_deploy = true } \ No newline at end of file From fa15f703151c2ded3e05f3b7a96dc8066913b60e Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 12:18:02 -0600 Subject: [PATCH 088/119] Adding disconnect and message routes --- terraform/workflow/ws-api-gateway.tf | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/terraform/workflow/ws-api-gateway.tf b/terraform/workflow/ws-api-gateway.tf index d60e27f..5cfae4a 100644 --- a/terraform/workflow/ws-api-gateway.tf +++ b/terraform/workflow/ws-api-gateway.tf @@ -10,6 +10,18 @@ resource "aws_apigatewayv2_route" "clash_bot_connection_route" { target = "integrations/${aws_apigatewayv2_integration.clash_bot_websocket_api_integration.id}" } +resource "aws_apigatewayv2_route" "clash_bot_disconnection_route" { + api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id + route_key = "$disconnect" + target = "integrations/${aws_apigatewayv2_integration.clash_bot_websocket_api_integration.id}" +} + +resource "aws_apigatewayv2_route" "clash_bot_message_route" { + api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id + route_key = "$message" + target = "integrations/${aws_apigatewayv2_integration.clash_bot_websocket_api_integration.id}" +} + resource "aws_apigatewayv2_integration" "clash_bot_websocket_api_integration" { api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id integration_type = "AWS_PROXY" From 1eeaa1145b8d255365ad27552361cdadf91079a4 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 12:24:53 -0600 Subject: [PATCH 089/119] Adding a subscribe route --- terraform/workflow/ws-api-gateway.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/workflow/ws-api-gateway.tf b/terraform/workflow/ws-api-gateway.tf index 5cfae4a..03cebfe 100644 --- a/terraform/workflow/ws-api-gateway.tf +++ b/terraform/workflow/ws-api-gateway.tf @@ -16,9 +16,9 @@ resource "aws_apigatewayv2_route" "clash_bot_disconnection_route" { target = "integrations/${aws_apigatewayv2_integration.clash_bot_websocket_api_integration.id}" } -resource "aws_apigatewayv2_route" "clash_bot_message_route" { +resource "aws_apigatewayv2_route" "clash_bot_subscribe_route" { api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id - route_key = "$message" + route_key = "subscribe" target = "integrations/${aws_apigatewayv2_integration.clash_bot_websocket_api_integration.id}" } From dc400b1eabb8dc166a612b2ab5ee97c7da98c4e8 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 12:27:07 -0600 Subject: [PATCH 090/119] Removing deployment-id --- terraform/workflow/ws-api-gateway.tf | 1 - 1 file changed, 1 deletion(-) diff --git a/terraform/workflow/ws-api-gateway.tf b/terraform/workflow/ws-api-gateway.tf index 03cebfe..09517a3 100644 --- a/terraform/workflow/ws-api-gateway.tf +++ b/terraform/workflow/ws-api-gateway.tf @@ -53,6 +53,5 @@ resource "aws_apigatewayv2_stage" "clash_bot_websocket_api_stage" { api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id name = "events-${var.environment}" description = "Clash Bot Workflow Websocket API stage" - deployment_id = aws_apigatewayv2_deployment.clash_bot_websocket_api_deployment.id auto_deploy = true } \ No newline at end of file From ff63d3dad746442a95c61d64330265a7413b01b3 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 12:30:29 -0600 Subject: [PATCH 091/119] Formatting --- terraform/workflow/ws-api-gateway.tf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/terraform/workflow/ws-api-gateway.tf b/terraform/workflow/ws-api-gateway.tf index 09517a3..ba63d73 100644 --- a/terraform/workflow/ws-api-gateway.tf +++ b/terraform/workflow/ws-api-gateway.tf @@ -50,8 +50,8 @@ resource "aws_apigatewayv2_deployment" "clash_bot_websocket_api_deployment" { } resource "aws_apigatewayv2_stage" "clash_bot_websocket_api_stage" { - api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id - name = "events-${var.environment}" - description = "Clash Bot Workflow Websocket API stage" - auto_deploy = true + api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id + name = "events-${var.environment}" + description = "Clash Bot Workflow Websocket API stage" + auto_deploy = true } \ No newline at end of file From 38e25f6fee7647561d4a929714bba0e5513a4b80 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 12:51:53 -0600 Subject: [PATCH 092/119] Adding in default route --- terraform/workflow/ws-api-gateway.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/workflow/ws-api-gateway.tf b/terraform/workflow/ws-api-gateway.tf index ba63d73..b12ae9d 100644 --- a/terraform/workflow/ws-api-gateway.tf +++ b/terraform/workflow/ws-api-gateway.tf @@ -16,9 +16,9 @@ resource "aws_apigatewayv2_route" "clash_bot_disconnection_route" { target = "integrations/${aws_apigatewayv2_integration.clash_bot_websocket_api_integration.id}" } -resource "aws_apigatewayv2_route" "clash_bot_subscribe_route" { +resource "aws_apigatewayv2_route" "clash_bot_default_route" { api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id - route_key = "subscribe" + route_key = "$default" target = "integrations/${aws_apigatewayv2_integration.clash_bot_websocket_api_integration.id}" } From 7009eb760b31218a2d61973fe26d6c8433756cee Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 13:05:58 -0600 Subject: [PATCH 093/119] Swapping to subscribe route --- functions/clash-bot/event-notifier/tests/event-notifier.test.ts | 2 +- terraform/workflow/ws-api-gateway.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/functions/clash-bot/event-notifier/tests/event-notifier.test.ts b/functions/clash-bot/event-notifier/tests/event-notifier.test.ts index 329e612..b44be9e 100644 --- a/functions/clash-bot/event-notifier/tests/event-notifier.test.ts +++ b/functions/clash-bot/event-notifier/tests/event-notifier.test.ts @@ -53,7 +53,7 @@ describe('Handle Websocket connection requests', () => { expect(client).toHaveReceivedCommandWith(DeleteItemCommand, expectedDeleteCommand); }); - test('If a websocket message reqeust is recieved, it should post the details to DynamoDb to save the state.', async () => { + test('If a websocket message request is recieved, it should post the details to DynamoDb to save the state.', async () => { const event = createMockMessageEvent(); const context = setupContext(); diff --git a/terraform/workflow/ws-api-gateway.tf b/terraform/workflow/ws-api-gateway.tf index b12ae9d..b32c6c5 100644 --- a/terraform/workflow/ws-api-gateway.tf +++ b/terraform/workflow/ws-api-gateway.tf @@ -18,7 +18,7 @@ resource "aws_apigatewayv2_route" "clash_bot_disconnection_route" { resource "aws_apigatewayv2_route" "clash_bot_default_route" { api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id - route_key = "$default" + route_key = "subscribe" target = "integrations/${aws_apigatewayv2_integration.clash_bot_websocket_api_integration.id}" } From 3a5429ff0f68787af538cdb85a8884e503bbf59c Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 14:30:52 -0600 Subject: [PATCH 094/119] Adding in subscribe and unsubscribe functionality --- .../clash-bot/event-notifier/package.json | 1 + .../clash-bot/event-notifier/src/handler.ts | 133 ++++++++++++++---- .../tests/event-notifier.test.ts | 126 +++++++++++------ terraform/workflow/dynamodb.tf | 32 ++++- terraform/workflow/event-setup-lambdas.tf | 6 +- .../event-notifier-lambda-policy.json | 5 +- 6 files changed, 228 insertions(+), 75 deletions(-) diff --git a/functions/clash-bot/event-notifier/package.json b/functions/clash-bot/event-notifier/package.json index 3badbab..c9c2fd3 100644 --- a/functions/clash-bot/event-notifier/package.json +++ b/functions/clash-bot/event-notifier/package.json @@ -28,6 +28,7 @@ }, "dependencies": { "@aws-sdk/client-dynamodb": "^3.398.0", + "@aws-sdk/util-dynamodb": "^3.451.0", "dotenv": "^16.3.1", "pino": "^8.15.0", "clash-bot-shared": "github:Poss111/ClashBotWorkflowShared#main" diff --git a/functions/clash-bot/event-notifier/src/handler.ts b/functions/clash-bot/event-notifier/src/handler.ts index bbec12c..87000a7 100644 --- a/functions/clash-bot/event-notifier/src/handler.ts +++ b/functions/clash-bot/event-notifier/src/handler.ts @@ -1,4 +1,5 @@ -import { DeleteItemCommand, DeleteItemCommandInput, DynamoDBClient, PutItemCommand, PutItemCommandInput } from '@aws-sdk/client-dynamodb'; +import { DeleteItemCommand, DeleteItemCommandInput, DynamoDBClient, GetItemCommand, GetItemCommandInput, PutItemCommand, PutItemCommandInput, UpdateItemCommand, UpdateItemCommandInput } from '@aws-sdk/client-dynamodb'; +import { unmarshall } from '@aws-sdk/util-dynamodb'; import { APIGatewayProxyResultV2, APIGatewayProxyWebsocketEventV2, APIGatewayProxyWebsocketHandlerV2 } from 'aws-lambda'; import pino from "pino"; @@ -19,44 +20,124 @@ export const handler: APIGatewayProxyWebsocketHandlerV2 = async (event: APIGatew if ('$connect' === routeKey) { logger.info("Received connect event..."); - const persistItem: PutItemCommandInput = { - TableName: process.env.TABLE_NAME, - Item: { - "connectionId": { S: connectionId }, - "context": { S: "TBA" } - } - }; - await client.send(new PutItemCommand(persistItem)); return { statusCode: 200, body: JSON.stringify({ message: 'Connected' }), }; } else if ('$disconnect' === routeKey) { logger.info("Received disconnect event..."); - const deleteItem: DeleteItemCommandInput = { - TableName: process.env.TABLE_NAME, + const getCommand: GetItemCommandInput = { + TableName: process.env.SUBSCRIBER_TO_TOPIC_TABLE_NAME, Key: { - "connectionId": { S: connectionId } + "subscriber": { S: event.requestContext.connectionId } } }; - await client.send(new DeleteItemCommand(deleteItem)); - return { - statusCode: 200, - body: JSON.stringify({ message: 'Disconnected' }), - }; - } else if ('$message' === routeKey) { - logger.info("Received message event..."); - const persistItem: PutItemCommandInput = { - TableName: process.env.TABLE_NAME, - Item: { - "connectionId": { S: connectionId }, - "context": { S: "TBA" } + + const subscriberTopics = await client.send(new GetItemCommand(getCommand)); + + logger.info({ subscriberTopics }, "Subscriber topics."); + + const subscriberToTopics = unmarshall(subscriberTopics.Item!); + + if (subscriberToTopics.topics === undefined || subscriberToTopics.topics.length === 0) { + return { + statusCode: 400, + body: JSON.stringify({ message: 'Did not disconnect successfully' }), + }; + } else { + logger.info({ subscriberToTopics }, "Subscriber to topics.", ); + + const deleteSubscriberToTopics: DeleteItemCommandInput = { + TableName: process.env.TOPIC_TO_SUBSCRIBER_TABLE_NAME, + Key: { + "subscriber": { S: event.requestContext.connectionId } + } + }; + + const updateCommandPromises = [...subscriberToTopics.topics].map((topic: string) => { + return client.send(new UpdateItemCommand({ + TableName: process.env.SUBSCRIBER_TO_TOPIC_TABLE_NAME, + Key: { + "context": { S: topic } + }, + ExpressionAttributeNames: { + "#S": "subscribers" + }, + ExpressionAttributeValues: { + ":val": { SS: [event.requestContext.connectionId] } + }, + UpdateExpression: "DELETE #S :val", + })); + }); + try { + await Promise.all([ + ...updateCommandPromises, + client.send(new DeleteItemCommand(deleteSubscriberToTopics)) + ]); + } catch(error) { + logger.error({ error }, "Error deleting subscriber to topics."); + return { + statusCode: 400, + body: JSON.stringify({ message: 'Did not disconnect successfully' }), + }; } + return { + statusCode: 200, + body: JSON.stringify({ message: 'Disconnected' }), + }; + } + } else if ('subscribe' === routeKey) { + logger.info("Received message event..."); + let topic = ""; + if (event.body !== undefined) { + topic = JSON.parse(event.body).topic; + } else { + return { + statusCode: 400, + body: JSON.stringify({ message: 'Missing topic' }), + }; + } + const updateTopicToSubscribers: UpdateItemCommandInput = { + TableName: process.env.TOPIC_TO_SUBSCRIBER_TABLE_NAME, + Key: { + "topic": { S: topic }, + }, + ExpressionAttributeNames: { + "#S": "subscribers" + }, + ExpressionAttributeValues: { + ":val": { SS: [event.requestContext.connectionId] } + }, + UpdateExpression: "ADD #S :val" + }; + const updateSubscriberToTopics: UpdateItemCommandInput = { + TableName: process.env.SUBSCRIBER_TO_TOPIC_TABLE_NAME, + Key: { + "subscriber": { S: event.requestContext.connectionId }, + }, + ExpressionAttributeNames: { + "#S": "topics" + }, + ExpressionAttributeValues: { + ":val": { SS: [topic] } + }, + UpdateExpression: "ADD #S :val" }; - await client.send(new PutItemCommand(persistItem)); + try { + await Promise.all([ + client.send(new UpdateItemCommand(updateTopicToSubscribers)), + client.send(new UpdateItemCommand(updateSubscriberToTopics)) + ]); + } catch(error) { + logger.error({ error }, "Error updating topic to subscribers."); + return { + statusCode: 400, + body: JSON.stringify({ message: 'Did not subscribe successfully' }), + }; + } return { statusCode: 200, - body: JSON.stringify({ message: 'Message received' }), + body: JSON.stringify({ message: `Subcribed to '${topic}'` }), }; } diff --git a/functions/clash-bot/event-notifier/tests/event-notifier.test.ts b/functions/clash-bot/event-notifier/tests/event-notifier.test.ts index b44be9e..6e434a4 100644 --- a/functions/clash-bot/event-notifier/tests/event-notifier.test.ts +++ b/functions/clash-bot/event-notifier/tests/event-notifier.test.ts @@ -1,6 +1,6 @@ import { APIGatewayProxyEvent, APIGatewayProxyResult, APIGatewayProxyResultV2, APIGatewayProxyWebsocketEventV2 } from 'aws-lambda'; import { handler } from '../src/handler'; -import { DeleteItemCommand, DeleteItemCommandInput, DynamoDBClient, PutItemCommand, PutItemCommandInput } from '@aws-sdk/client-dynamodb'; +import { DeleteItemCommand, DeleteItemCommandInput, DynamoDBClient, GetItemCommand, GetItemCommandInput, PutItemCommand, PutItemCommandInput, UpdateItemCommand, UpdateItemCommandInput } from '@aws-sdk/client-dynamodb'; import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; @@ -10,71 +10,114 @@ describe('Handle Websocket connection requests', () => { const event = createMockConnectEvent(); const context = setupContext(); - const client = mockClient(DynamoDBClient); - - client.on(PutItemCommand) - .resolves({}); - - const expectedPutCommand: PutItemCommandInput = { - TableName: process.env.TABLE_NAME, - Item: { - "connectionId": { S: event.requestContext.connectionId }, - "context": { S: "TBA" } - } - }; - const result = await handler(event, context, () => { }); expect(result).toBeDefined(); expect((result as any).statusCode).toBe(200); expect((result as any).body).toBe('{\"message\":\"Connected\"}'); - expect(client).toHaveReceivedCommandWith(PutItemCommand, expectedPutCommand); }); test('If a websocket disconnect reqeust is recieved, it should post the details to DynamoDb to save the state.', async () => { const event = createMockDisconnectEvent(); const context = setupContext(); + const topic = "mockTopic"; const client = mockClient(DynamoDBClient); client.on(DeleteItemCommand) .resolves({}); + client.on(GetItemCommand) + .resolves({ + Item: { + "topics": { + SS: [topic] + } + } + }); + client.on(UpdateItemCommand) + .resolves({}); - const expectedDeleteCommand: DeleteItemCommandInput = { + const getCommand: GetItemCommandInput = { + TableName: process.env.SUBSCRIBER_TO_TOPIC_TABLE_NAME, + Key: { + "subscriber": { S: event.requestContext.connectionId } + } + }; + + const expectedDeleteCommandForSubscriberToTopic: DeleteItemCommandInput = { TableName: process.env.TABLE_NAME, Key: { - "connectionId": { S: event.requestContext.connectionId } + "subscriber": { S: event.requestContext.connectionId } } }; + const expectedDeleteCommandForTopicToSubscriber: UpdateItemCommandInput = { + TableName: process.env.TABLE_NAME, + Key: { + "context": { S: topic } + }, + ExpressionAttributeNames: { + "#S": "subscribers" + }, + ExpressionAttributeValues: { + ":val": { SS: [event.requestContext.connectionId] } + }, + UpdateExpression: "DELETE #S :val", + }; + + const result = await handler(event, context, () => { }); expect(result).toBeDefined(); expect((result as any).statusCode).toBe(200); expect((result as any).body).toBe('{\"message\":\"Disconnected\"}'); - expect(client).toHaveReceivedCommandWith(DeleteItemCommand, expectedDeleteCommand); + expect(client).toHaveReceivedCommandWith(GetItemCommand, getCommand); + expect(client).toHaveReceivedCommandWith(UpdateItemCommand, expectedDeleteCommandForTopicToSubscriber); + expect(client).toHaveReceivedCommandWith(DeleteItemCommand, expectedDeleteCommandForSubscriberToTopic); }); - test('If a websocket message request is recieved, it should post the details to DynamoDb to save the state.', async () => { + test('If a websocket subscribe request is recieved, it should post the details to DynamoDb to save the state.', async () => { const event = createMockMessageEvent(); const context = setupContext(); + const topic = JSON.parse(event.body ?? "{}").topic; const client = mockClient(DynamoDBClient); - client.on(PutItemCommand) + client.on(UpdateItemCommand) .resolves({}); - const expectedPutCommand: PutItemCommandInput = { - TableName: process.env.TABLE_NAME, - Item: { - "connectionId": { S: event.requestContext.connectionId }, - "context": { S: "TBA" } - } + const expectedUpdateItemInput: UpdateItemCommandInput = { + TableName: process.env.TOPIC_TO_SUBSCRIBER_TABLE_NAME, + Key: { + "topic": { S: topic }, + }, + ExpressionAttributeNames: { + "#S": "subscribers" + }, + ExpressionAttributeValues: { + ":val": { SS: [event.requestContext.connectionId] } + }, + UpdateExpression: "ADD #S :val" + }; + + const expectedUpdateItemInputSubscriberToTopics: UpdateItemCommandInput = { + TableName: process.env.SUBSCRIBER_TO_TOPIC_TABLE_NAME, + Key: { + "subscriber": { S: event.requestContext.connectionId }, + }, + ExpressionAttributeNames: { + "#S": "topics" + }, + ExpressionAttributeValues: { + ":val": { SS: [topic] } + }, + UpdateExpression: "ADD #S :val" }; const result = await handler(event, context, () => { }); expect(result).toBeDefined(); expect((result as any).statusCode).toBe(200); - expect((result as any).body).toBe('{\"message\":\"Message received\"}'); - expect(client).toHaveReceivedCommandWith(PutItemCommand, expectedPutCommand); + expect((result as any).body).toBe(`{\"message\":\"Subcribed to '${topic}'\"}`); + expect(client).toHaveReceivedCommandWith(UpdateItemCommand, expectedUpdateItemInput); + expect(client).toHaveReceivedCommandWith(UpdateItemCommand, expectedUpdateItemInputSubscriberToTopics); }); }); @@ -149,20 +192,21 @@ const createMockDisconnectEvent = (): APIGatewayProxyWebsocketEventV2 => { const createMockMessageEvent = (): APIGatewayProxyWebsocketEventV2 => { return { requestContext: { - apiId: 'sampleApiId', - domainName: 'sampleDomainName', - requestId: 'sampleRequestId', - routeKey: '$message', - stage: 'dev', - messageId: 'sampleMessageId', - eventType: 'CONNECT', - extendedRequestId: 'sampleExtendedRequestId', - requestTime: 'sampleRequestTime', - messageDirection: 'IN', - connectedAt: Date.now(), - requestTimeEpoch: Date.now(), - connectionId: 'sampleConnectionId' + routeKey: "subscribe", + messageId: "OqOHdcL-oAMCKcw=", + eventType: "MESSAGE", + extendedRequestId: "OqOHdHnOoAMFozw=", + requestTime: "19/Nov/2023:19:09:22 +0000", + messageDirection: "IN", + stage: "events-development", + connectedAt: 1700420961168, + requestTimeEpoch: 1700420962611, + requestId: "OqOHdHnOoAMFozw=", + domainName: "k10wm04op6.execute-api.us-east-1.amazonaws.com", + connectionId: "OqOHOcLhIAMCKcw=", + apiId: "k10wm04op6" }, + body: "{\"action\":\"subscribe\",\"topic\":\"mocktopic\"}", isBase64Encoded: false }; }; \ No newline at end of file diff --git a/terraform/workflow/dynamodb.tf b/terraform/workflow/dynamodb.tf index e2433ce..d038b26 100644 --- a/terraform/workflow/dynamodb.tf +++ b/terraform/workflow/dynamodb.tf @@ -23,21 +23,43 @@ module "dynamodb_table" { module "events_table" { source = "terraform-aws-modules/dynamodb-table/aws" - name = "clash-bot-events-${var.environment}" - hash_key = "connectionId" - range_key = "context" + name = "clash-bot-topics-${var.environment}" + hash_key = "topic" + range_key = "subscriber" billing_mode = "PROVISIONED" write_capacity = 5 read_capacity = 1 attributes = [ { - name = "connectionId" + name = "topic" type = "S" }, { - name = "context" + name = "subscriber" + type = "SS" + } + ] +} + +module "subscriber_table" { + source = "terraform-aws-modules/dynamodb-table/aws" + + name = "clash-bot-subscriber-${var.environment}" + hash_key = "subscriber" + range_key = "topics" + billing_mode = "PROVISIONED" + write_capacity = 5 + read_capacity = 1 + + attributes = [ + { + name = "subscriber" type = "S" + }, + { + name = "topics" + type = "SS" } ] } \ No newline at end of file diff --git a/terraform/workflow/event-setup-lambdas.tf b/terraform/workflow/event-setup-lambdas.tf index 66b5da7..dd1eaac 100644 --- a/terraform/workflow/event-setup-lambdas.tf +++ b/terraform/workflow/event-setup-lambdas.tf @@ -62,13 +62,15 @@ module "event_notifier_lambda" { artifact_path = var.event_notifier_artifact_path environment_variables = { - TABLE_NAME = module.events_table.dynamodb_table_id + TOPIC_TO_SUBSCRIBER_TABLE_NAME = module.events_table.dynamodb_table_id, + SUBSCRIBER_TO_TOPIC_TABLE_NAME = module.subscriber_table.dynamodb_table_id } iam_policy_json = templatefile( "${path.module}/policies/event-notifier-lambda-policy.json", { - DYNAMODB_ARN = module.events_table.dynamodb_table_arn + DYNAMODB_ARN = module.events_table.dynamodb_table_arn, + DYNAMODB_TWO_ARN = module.subscriber_table.dynamodb_table_arn, } ) } \ No newline at end of file diff --git a/terraform/workflow/policies/event-notifier-lambda-policy.json b/terraform/workflow/policies/event-notifier-lambda-policy.json index 6d4cc8e..d7b5e9c 100644 --- a/terraform/workflow/policies/event-notifier-lambda-policy.json +++ b/terraform/workflow/policies/event-notifier-lambda-policy.json @@ -20,7 +20,10 @@ "dynamodb:BatchGetItem" ], "Effect": "Allow", - "Resource": "${DYNAMODB_ARN}" + "Resource": [ + "${DYNAMODB_ARN}", + "${DYNAMODB_TWO_ARN}" + ] } ], "Version": "2012-10-17" From d7a02f0d424422e499a76c42836455dba977b134 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 14:34:38 -0600 Subject: [PATCH 095/119] Removing range key --- terraform/workflow/dynamodb.tf | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/terraform/workflow/dynamodb.tf b/terraform/workflow/dynamodb.tf index d038b26..d0019a6 100644 --- a/terraform/workflow/dynamodb.tf +++ b/terraform/workflow/dynamodb.tf @@ -25,7 +25,6 @@ module "events_table" { name = "clash-bot-topics-${var.environment}" hash_key = "topic" - range_key = "subscriber" billing_mode = "PROVISIONED" write_capacity = 5 read_capacity = 1 @@ -34,10 +33,6 @@ module "events_table" { { name = "topic" type = "S" - }, - { - name = "subscriber" - type = "SS" } ] } @@ -47,7 +42,6 @@ module "subscriber_table" { name = "clash-bot-subscriber-${var.environment}" hash_key = "subscriber" - range_key = "topics" billing_mode = "PROVISIONED" write_capacity = 5 read_capacity = 1 @@ -56,10 +50,6 @@ module "subscriber_table" { { name = "subscriber" type = "S" - }, - { - name = "topics" - type = "SS" } ] } \ No newline at end of file From a5d3837f010c475e0dde750266aae9a787cf71f4 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 14:47:05 -0600 Subject: [PATCH 096/119] Fixing schema --- functions/clash-bot/event-notifier/src/handler.ts | 2 +- .../clash-bot/event-notifier/tests/event-notifier.test.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/functions/clash-bot/event-notifier/src/handler.ts b/functions/clash-bot/event-notifier/src/handler.ts index 87000a7..d9beab9 100644 --- a/functions/clash-bot/event-notifier/src/handler.ts +++ b/functions/clash-bot/event-notifier/src/handler.ts @@ -58,7 +58,7 @@ export const handler: APIGatewayProxyWebsocketHandlerV2 = async (event: APIGatew return client.send(new UpdateItemCommand({ TableName: process.env.SUBSCRIBER_TO_TOPIC_TABLE_NAME, Key: { - "context": { S: topic } + "topic": { S: topic } }, ExpressionAttributeNames: { "#S": "subscribers" diff --git a/functions/clash-bot/event-notifier/tests/event-notifier.test.ts b/functions/clash-bot/event-notifier/tests/event-notifier.test.ts index 6e434a4..545a2c5 100644 --- a/functions/clash-bot/event-notifier/tests/event-notifier.test.ts +++ b/functions/clash-bot/event-notifier/tests/event-notifier.test.ts @@ -44,16 +44,16 @@ describe('Handle Websocket connection requests', () => { }; const expectedDeleteCommandForSubscriberToTopic: DeleteItemCommandInput = { - TableName: process.env.TABLE_NAME, + TableName: process.env.SUBSCRIBER_TO_TOPIC_TABLE_NAME, Key: { "subscriber": { S: event.requestContext.connectionId } } }; const expectedDeleteCommandForTopicToSubscriber: UpdateItemCommandInput = { - TableName: process.env.TABLE_NAME, + TableName: process.env.TOPIC_TO_SUBSCRIBER_TABLE_NAME, Key: { - "context": { S: topic } + "topic": { S: topic } }, ExpressionAttributeNames: { "#S": "subscribers" From 7621caceaf7d6f2fadc476659afadb71ce2c555e Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 19 Nov 2023 14:57:04 -0600 Subject: [PATCH 097/119] Setting up appropriately table to be invoked for notifier --- .../clash-bot/event-notifier/src/handler.ts | 16 ++++++++-------- .../event-notifier/tests/event-notifier.test.ts | 6 ++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/functions/clash-bot/event-notifier/src/handler.ts b/functions/clash-bot/event-notifier/src/handler.ts index d9beab9..36ab5e6 100644 --- a/functions/clash-bot/event-notifier/src/handler.ts +++ b/functions/clash-bot/event-notifier/src/handler.ts @@ -45,10 +45,10 @@ export const handler: APIGatewayProxyWebsocketHandlerV2 = async (event: APIGatew body: JSON.stringify({ message: 'Did not disconnect successfully' }), }; } else { - logger.info({ subscriberToTopics }, "Subscriber to topics.", ); + logger.info({ subscriberToTopics }, "Subscriber to topics.",); const deleteSubscriberToTopics: DeleteItemCommandInput = { - TableName: process.env.TOPIC_TO_SUBSCRIBER_TABLE_NAME, + TableName: process.env.SUBSCRIBER_TO_TOPIC_TABLE_NAME, Key: { "subscriber": { S: event.requestContext.connectionId } } @@ -56,15 +56,15 @@ export const handler: APIGatewayProxyWebsocketHandlerV2 = async (event: APIGatew const updateCommandPromises = [...subscriberToTopics.topics].map((topic: string) => { return client.send(new UpdateItemCommand({ - TableName: process.env.SUBSCRIBER_TO_TOPIC_TABLE_NAME, + TableName: process.env.TOPIC_TO_SUBSCRIBER_TABLE_NAME, Key: { - "topic": { S: topic } + "topic": { S: topic } }, ExpressionAttributeNames: { - "#S": "subscribers" + "#S": "subscribers" }, ExpressionAttributeValues: { - ":val": { SS: [event.requestContext.connectionId] } + ":val": { SS: [event.requestContext.connectionId] } }, UpdateExpression: "DELETE #S :val", })); @@ -74,7 +74,7 @@ export const handler: APIGatewayProxyWebsocketHandlerV2 = async (event: APIGatew ...updateCommandPromises, client.send(new DeleteItemCommand(deleteSubscriberToTopics)) ]); - } catch(error) { + } catch (error) { logger.error({ error }, "Error deleting subscriber to topics."); return { statusCode: 400, @@ -128,7 +128,7 @@ export const handler: APIGatewayProxyWebsocketHandlerV2 = async (event: APIGatew client.send(new UpdateItemCommand(updateTopicToSubscribers)), client.send(new UpdateItemCommand(updateSubscriberToTopics)) ]); - } catch(error) { + } catch (error) { logger.error({ error }, "Error updating topic to subscribers."); return { statusCode: 400, diff --git a/functions/clash-bot/event-notifier/tests/event-notifier.test.ts b/functions/clash-bot/event-notifier/tests/event-notifier.test.ts index 545a2c5..8c679b2 100644 --- a/functions/clash-bot/event-notifier/tests/event-notifier.test.ts +++ b/functions/clash-bot/event-notifier/tests/event-notifier.test.ts @@ -3,9 +3,15 @@ import { handler } from '../src/handler'; import { DeleteItemCommand, DeleteItemCommandInput, DynamoDBClient, GetItemCommand, GetItemCommandInput, PutItemCommand, PutItemCommandInput, UpdateItemCommand, UpdateItemCommandInput } from '@aws-sdk/client-dynamodb'; import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; +import { before } from 'node:test'; describe('Handle Websocket connection requests', () => { + beforeEach(() => { + process.env.TOPIC_TO_SUBSCRIBER_TABLE_NAME = "mockTopicToSubscriberTableName"; + process.env.SUBSCRIBER_TO_TOPIC_TABLE_NAME = "mockSubscriberToTopicTableName"; + }); + test('If a websocket connection reqeust is recieved, it should post the details to DynamoDb to save the state.', async () => { const event = createMockConnectEvent(); const context = setupContext(); From a8a49c0e19b70203bc346ca5d567559e14e8318b Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 25 Nov 2023 00:50:07 -0600 Subject: [PATCH 098/119] Adding websocket publisher lambda --- .../clash-bot/event-handler/src/handler.ts | 5 +- .../event-handler/tests/handler.test.ts | 15 ++- .../retrieve/ClashBotWorkflow.code-workspace | 7 +- .../websocket-publisher/.eslintrc.js | 14 ++ .../websocket-publisher/jest.config.js | 6 + .../websocket-publisher/package.json | 41 ++++++ .../websocket-publisher/src/handler.ts | 68 ++++++++++ .../websocket-publisher/tests/handler.test.ts | 125 ++++++++++++++++++ .../websocket-publisher/tsconfig.json | 24 ++++ lambda-generator/generator.py | 43 ++++++ .../templates/lambda/jest.config.js.j2 | 6 + .../templates/lambda/package.json.j2 | 36 +++++ .../templates/lambda/src/handler.ts.j2 | 27 ++++ .../templates/lambda/tests/handler.test.ts.j2 | 24 ++++ .../templates/lambda/tsconfig.json.j2 | 24 ++++ .../templates/terraform/lambda.tf.j2 | 19 +++ .../websocket-publisher-lambda-policy.json | 27 ++++ terraform/workflow/step-functions.lambda.tf | 33 ++++- .../create-team-step-function.asl.json | 32 ++++- terraform/workflow/variables.tf | 5 + 20 files changed, 567 insertions(+), 14 deletions(-) create mode 100644 functions/clash-bot/websocket-publisher/.eslintrc.js create mode 100644 functions/clash-bot/websocket-publisher/jest.config.js create mode 100644 functions/clash-bot/websocket-publisher/package.json create mode 100644 functions/clash-bot/websocket-publisher/src/handler.ts create mode 100644 functions/clash-bot/websocket-publisher/tests/handler.test.ts create mode 100644 functions/clash-bot/websocket-publisher/tsconfig.json create mode 100644 lambda-generator/generator.py create mode 100644 lambda-generator/templates/lambda/jest.config.js.j2 create mode 100644 lambda-generator/templates/lambda/package.json.j2 create mode 100644 lambda-generator/templates/lambda/src/handler.ts.j2 create mode 100644 lambda-generator/templates/lambda/tests/handler.test.ts.j2 create mode 100644 lambda-generator/templates/lambda/tsconfig.json.j2 create mode 100644 lambda-generator/templates/terraform/lambda.tf.j2 create mode 100644 terraform/workflow/policies/websocket-publisher-lambda-policy.json diff --git a/functions/clash-bot/event-handler/src/handler.ts b/functions/clash-bot/event-handler/src/handler.ts index 3913d1b..022ce15 100644 --- a/functions/clash-bot/event-handler/src/handler.ts +++ b/functions/clash-bot/event-handler/src/handler.ts @@ -26,7 +26,10 @@ export const handler: SQSHandler = async (event: SQSEvent) => { return client.send(new StartExecutionCommand({ stateMachineArn: sfArn, name: `${parsedEvent.event}-${record.messageId}`, - input: JSON.stringify(parsedEvent.payload), + input: JSON.stringify({ + requestId: parsedEvent.uuid, + payload: parsedEvent.payload + }), traceHeader: record.messageId })); }); diff --git a/functions/clash-bot/event-handler/tests/handler.test.ts b/functions/clash-bot/event-handler/tests/handler.test.ts index 0bb17e2..0436322 100644 --- a/functions/clash-bot/event-handler/tests/handler.test.ts +++ b/functions/clash-bot/event-handler/tests/handler.test.ts @@ -83,7 +83,10 @@ describe('Should invoke a AWS Step function based on the event.', () => { await expect(sfnMock).toHaveReceivedCommandWith(StartExecutionCommand, { stateMachineArn: eventEntry.arn, name: `${eventEntry.event}-${sqsEvent.Records[0].messageId}`, - input: JSON.stringify(event.payload), + input: JSON.stringify({ + requestId: event.uuid, + payload: event.payload + }), traceHeader: sqsEvent.Records[0].messageId }); }); @@ -149,13 +152,19 @@ describe('Should invoke a AWS Step function based on the event.', () => { await expect(sfnMock).toHaveReceivedCommandWith(StartExecutionCommand, { stateMachineArn: 'arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorld-StateMachine', name: `${createTeamEvent.event}-${sqsEvent.Records[0].messageId}`, - input: JSON.stringify(createTeamEvent.payload), + input: JSON.stringify({ + requestId: createTeamEvent.uuid, + payload: createTeamEvent.payload + }), traceHeader: sqsEvent.Records[0].messageId }); await expect(sfnMock).toHaveReceivedCommandWith(StartExecutionCommand, { stateMachineArn: 'arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorld-StateMachine-2', name: `${updateTeamEvent.event}-${sqsEvent.Records[1].messageId}`, - input: JSON.stringify(updateTeamEvent.payload), + input: JSON.stringify({ + requestId: updateTeamEvent.uuid, + payload: updateTeamEvent.payload + }), traceHeader: sqsEvent.Records[1].messageId }); }); diff --git a/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace index 0f25a08..3a098c5 100644 --- a/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace +++ b/functions/clash-bot/team/retrieve/ClashBotWorkflow.code-workspace @@ -30,12 +30,15 @@ { "name": "event-notifier", "path": "../../event-notifier" + }, + { + "name": "websocket-publisher", + "path": "../../websocket-publisher" } ], "settings": { "jest.disabledWorkspaceFolders": [ - "can-create-team", - "prod" + "can-create-team" ] } } \ No newline at end of file diff --git a/functions/clash-bot/websocket-publisher/.eslintrc.js b/functions/clash-bot/websocket-publisher/.eslintrc.js new file mode 100644 index 0000000..7d3cf15 --- /dev/null +++ b/functions/clash-bot/websocket-publisher/.eslintrc.js @@ -0,0 +1,14 @@ +module.exports = { + parser: '@typescript-eslint/parser', // Specifies the ESLint parser + extends: [ + 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin + ], + parserOptions: { + ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features + sourceType: 'module', // Allows for the use of imports + }, + rules: { + // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs + // e.g. "@typescript-eslint/explicit-function-return-type": "off", + }, + }; \ No newline at end of file diff --git a/functions/clash-bot/websocket-publisher/jest.config.js b/functions/clash-bot/websocket-publisher/jest.config.js new file mode 100644 index 0000000..85c664c --- /dev/null +++ b/functions/clash-bot/websocket-publisher/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + transform: {'^.+\\.ts?$': 'ts-jest'}, + testEnvironment: 'node', + testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] + }; \ No newline at end of file diff --git a/functions/clash-bot/websocket-publisher/package.json b/functions/clash-bot/websocket-publisher/package.json new file mode 100644 index 0000000..42c464a --- /dev/null +++ b/functions/clash-bot/websocket-publisher/package.json @@ -0,0 +1,41 @@ +{ + "name": "clash-bot-websocket-publisher", + "version": "1.0.0", + "description": "A auto generated lambda for Clash Bot", + "main": "index.js", + "scripts": { + "test": "jest", + "prebuild": "eslint . --ext .ts", + "build": "tsc", + "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + }, + "keywords": [ + "clash-bot" + ], + "author": "Poss111", + "license": "ISC", + "devDependencies": { + "@aws-sdk/types": "^3.398.0", + "@types/aws-lambda": "^8.10.119", + "@types/jest": "^29.5.4", + "@types/pino": "^7.0.5", + "@typescript-eslint/eslint-plugin": "^6.12.0", + "@typescript-eslint/parser": "^6.12.0", + "aws-sdk-client-mock": "^3.0.0", + "aws-sdk-client-mock-jest": "^3.0.0", + "eslint": "^8.54.0", + "jest": "^29.6.4", + "pino-pretty": "^10.2.0", + "ts-jest": "^29.1.1", + "ts-mockito": "^2.6.1", + "typescript": "^5.2.2" + }, + "dependencies": { + "@aws-sdk/client-apigatewaymanagementapi": "^3.454.0", + "@aws-sdk/client-dynamodb": "^3.398.0", + "@aws-sdk/util-dynamodb": "^3.451.0", + "clash-bot-shared": "github:Poss111/ClashBotWorkflowShared#main", + "dotenv": "^16.3.1", + "pino": "^8.15.0" + } +} diff --git a/functions/clash-bot/websocket-publisher/src/handler.ts b/functions/clash-bot/websocket-publisher/src/handler.ts new file mode 100644 index 0000000..f751a36 --- /dev/null +++ b/functions/clash-bot/websocket-publisher/src/handler.ts @@ -0,0 +1,68 @@ +import { Handler } from 'aws-lambda'; +import pino from "pino"; + +import { ApiGatewayManagementApiClient, PostToConnectionCommand } from '@aws-sdk/client-apigatewaymanagementapi'; +import { WebsocketEvent } from 'clash-bot-shared'; +import { DynamoDBClient, GetItemCommand } from '@aws-sdk/client-dynamodb'; +import { unmarshall } from '@aws-sdk/util-dynamodb'; + +export const handler: Handler = async (event: WebsocketEvent) => { + const level = process.env.LOGGER_LEVEL === undefined ? "info" : process.env.LOGGER_LEVEL; + const logger = pino({ level }); + logger.info({ eventRecieved: event }, 'Recieved event...'); + try { + + let topic = ""; + + if (event.requestId) { + topic = event.requestId; + } else { + throw new Error("Missing requestId"); + } + + logger.info({ topic }, 'Retrieving subscriptions to topic...'); + + const dynamoDbClient = new DynamoDBClient({}); + const results = await dynamoDbClient.send(new GetItemCommand({ + TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, + Key: { + "topic": { S: topic } + } + })); + + let subscribers: string[] = []; + + if (results.Item) { + subscribers = [...unmarshall(results.Item).subscribers]; + } else { + logger.info({ topic }, 'No subscribers found...'); + } + + logger.info({ subscribers }, 'Subscribers found...'); + logger.info({ event }, 'Sending payload...'); + + const posts = subscribers.map((subscriber) => { + const requestParams = { + ConnectionId: subscriber, + Data: JSON.stringify(event.payload), + }; + const client = new ApiGatewayManagementApiClient({ endpoint: process.env.WEBSOCKET_API_ENDPOINT }) + + return client.send(new PostToConnectionCommand(requestParams)); + }); + + const responses = await Promise.all(posts); + + return { + posts: responses, + topic, + payload: event.payload + }; + } catch (error) { + logger.error(error, "Failed."); + return { + error: 'Failed to publish to websocket', + stack: error + }; + } +}; \ No newline at end of file diff --git a/functions/clash-bot/websocket-publisher/tests/handler.test.ts b/functions/clash-bot/websocket-publisher/tests/handler.test.ts new file mode 100644 index 0000000..0c10c87 --- /dev/null +++ b/functions/clash-bot/websocket-publisher/tests/handler.test.ts @@ -0,0 +1,125 @@ +import { DynamoDBClient, GetItemCommand } from '@aws-sdk/client-dynamodb'; +import { handler } from '../src/handler'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; +import { ApiGatewayManagementApiClient, PostToConnectionCommand } from '@aws-sdk/client-apigatewaymanagementapi'; +import { WebsocketEvent } from 'clash-bot-shared'; +import { Context } from 'aws-lambda'; + +describe('Placeholder', () => { + + test('Happy Path', async () => { + const topicKey = 'topic'; + const event = { + requestId: topicKey, + payload: { + message: 'message' + } + }; + const context = {}; + + const dynamoClientMock = mockClient(DynamoDBClient); + const apiGatewayMock = mockClient(ApiGatewayManagementApiClient); + + dynamoClientMock.on(GetItemCommand) + .resolves({ + Item: { + topic: { + S: topicKey + }, + subscribers: { + SS: [ + 'connectionId' + ] + } + } + }); + apiGatewayMock.on(PostToConnectionCommand) + .resolves({}); + + const result = await handler(event as WebsocketEvent, context as Context, () => { }); + expect(result).toBeDefined(); + expect(dynamoClientMock).toHaveReceivedCommandWith(GetItemCommand, { + TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, + Key: { + "topic": { S: topicKey } + } + }); + expect(apiGatewayMock).toHaveReceivedCommandWith(PostToConnectionCommand, { + ConnectionId: 'connectionId', + Data: JSON.stringify(event.payload) + }); + }); + + test('No topics subscribers', async () => { + const topicKey = 'topic'; + const event = { + requestId: topicKey, + payload: { + message: 'message' + } + }; + const context = {}; + + const dynamoClientMock = mockClient(DynamoDBClient); + const apiGatewayMock = mockClient(ApiGatewayManagementApiClient); + + dynamoClientMock.on(GetItemCommand) + .resolves({ + Item: {} + }); + apiGatewayMock.on(PostToConnectionCommand) + .resolves({}); + + const result = await handler(event as WebsocketEvent, context as Context, () => { }); + expect(result).toBeDefined(); + expect(dynamoClientMock).toHaveReceivedCommandWith(GetItemCommand, { + TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, + Key: { + "topic": { S: topicKey } + } + }); + expect(apiGatewayMock).not.toHaveReceivedCommand(PostToConnectionCommand); + }); + + test('No topics subscribers', async () => { + const topicKey = 'topic'; + const event = { + requestId: topicKey, + payload: { + message: 'message' + } + }; + const context = {}; + + const dynamoClientMock = mockClient(DynamoDBClient); + const apiGatewayMock = mockClient(ApiGatewayManagementApiClient); + + dynamoClientMock.on(GetItemCommand) + .resolves({}); + apiGatewayMock.on(PostToConnectionCommand) + .resolves({}); + + const result = await handler(event as WebsocketEvent, context as Context, () => { }); + expect(result).toBeDefined(); + expect(dynamoClientMock).toHaveReceivedCommandWith(GetItemCommand, { + TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, + Key: { + "topic": { S: topicKey } + } + }); + expect(apiGatewayMock).not.toHaveReceivedCommand(PostToConnectionCommand); + }); + + test('Error Case - Missing requestId', async () => { + const event = {}; + const context = {}; + + const results = await handler(event as WebsocketEvent, context as Context, () => { }); + expect(results).toEqual({ + error: "Failed to publish to websocket", + stack: new Error("Missing requestId") + }); + }); + +}); \ No newline at end of file diff --git a/functions/clash-bot/websocket-publisher/tsconfig.json b/functions/clash-bot/websocket-publisher/tsconfig.json new file mode 100644 index 0000000..62859a2 --- /dev/null +++ b/functions/clash-bot/websocket-publisher/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es2020", + "strict": true, + "preserveConstEnums": true, + "noEmit": false, + "sourceMap": false, + "module":"commonjs", + "moduleResolution":"node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "rootDir": "./src", + "outDir": "prod", + "baseUrl": ".", + "paths": { + "clash-bot-shared": [ "node_modules/clash-bot-shared/src"], + "clash-bot-shared/*": [ "node_modules/clash-bot-shared/src/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.test.ts"], + } \ No newline at end of file diff --git a/lambda-generator/generator.py b/lambda-generator/generator.py new file mode 100644 index 0000000..3a5c0c4 --- /dev/null +++ b/lambda-generator/generator.py @@ -0,0 +1,43 @@ +import argparse +import os +from jinja2 import Environment, FileSystemLoader +import re + +def generate_file(name, items): + env = Environment(loader=FileSystemLoader('.')) + env.filters['to_kebab_case'] = to_kebab_case + + for template_name in env.list_templates(extensions=["j2"]): + template = env.get_template(template_name) + + output = template.render( + name=name, + items=items, + ) + + # Remove the .j2 extension from the template name for the output file + output_file_name = os.path.splitext(template_name)[0] + + print(f'Writing {output_file_name}...') + output_dir = os.path.join('../functions/clash-bot', name, os.path.dirname(output_file_name)) + os.makedirs(output_dir, exist_ok=True) + + with open(os.path.join(output_dir, os.path.basename(output_file_name)), 'w') as f: + + f.write(output) + +def to_kebab_case(s): + s = re.sub(r'(.)([A-Z][a-z]+)', r'\1-\2', s) + return re.sub(r'([a-z0-9])([A-Z])', r'\1-\2', s).lower() + +def main(): + parser = argparse.ArgumentParser(description='Generate a Terraform file for a Lambda function.') + parser.add_argument('--name', required=True, help='The name of the Lambda function.') + parser.add_argument('--items', nargs='+', help='A list of items.') + + args = parser.parse_args() + + generate_file(args.name, args.items) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/lambda-generator/templates/lambda/jest.config.js.j2 b/lambda-generator/templates/lambda/jest.config.js.j2 new file mode 100644 index 0000000..85c664c --- /dev/null +++ b/lambda-generator/templates/lambda/jest.config.js.j2 @@ -0,0 +1,6 @@ +module.exports = { + transform: {'^.+\\.ts?$': 'ts-jest'}, + testEnvironment: 'node', + testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] + }; \ No newline at end of file diff --git a/lambda-generator/templates/lambda/package.json.j2 b/lambda-generator/templates/lambda/package.json.j2 new file mode 100644 index 0000000..4146632 --- /dev/null +++ b/lambda-generator/templates/lambda/package.json.j2 @@ -0,0 +1,36 @@ +{ + "name": "clash-bot-{{ name|to_kebab_case }}", + "version": "1.0.0", + "description": "A auto generated lambda for Clash Bot", + "main": "index.js", + "scripts": { + "test": "jest", + "build": "tsc", + "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + }, + "keywords": [ + "clash-bot" + ], + "author": "Poss111", + "license": "ISC", + "devDependencies": { + "@aws-sdk/types": "^3.398.0", + "@types/aws-lambda": "^8.10.119", + "@types/jest": "^29.5.4", + "@types/pino": "^7.0.5", + "aws-sdk-client-mock": "^3.0.0", + "aws-sdk-client-mock-jest": "^3.0.0", + "jest": "^29.6.4", + "pino-pretty": "^10.2.0", + "ts-jest": "^29.1.1", + "ts-mockito": "^2.6.1", + "typescript": "^5.2.2" + }, + "dependencies": { + "@aws-sdk/client-dynamodb": "^3.398.0", + "@aws-sdk/util-dynamodb": "^3.451.0", + "dotenv": "^16.3.1", + "pino": "^8.15.0", + "clash-bot-shared": "github:Poss111/ClashBotWorkflowShared#main" + } +} diff --git a/lambda-generator/templates/lambda/src/handler.ts.j2 b/lambda-generator/templates/lambda/src/handler.ts.j2 new file mode 100644 index 0000000..a1e7b5f --- /dev/null +++ b/lambda-generator/templates/lambda/src/handler.ts.j2 @@ -0,0 +1,27 @@ +import { APIGatewayProxyEvent, APIGatewayProxyHandler } from 'aws-lambda'; +import pino from "pino"; + +import { APIGatewayProxyResult } from 'aws-lambda'; + +export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEvent): Promise => { + const level = process.env.LOGGER_LEVEL === undefined ? "info" : process.env.LOGGER_LEVEL; + const logger = pino({ level }); + logger.info({ eventRecieved: event }, 'Recieved event...'); + try { + return { + statusCode: 200, + body: JSON.stringify({ + requestId: "" + }) + }; + } catch (error) { + logger.error(error, "Failed."); + return { + statusCode: 500, + body: JSON.stringify({ + requestId: event.requestContext.requestId, + error: "Failed" + }) + }; + } +}; \ No newline at end of file diff --git a/lambda-generator/templates/lambda/tests/handler.test.ts.j2 b/lambda-generator/templates/lambda/tests/handler.test.ts.j2 new file mode 100644 index 0000000..c7f2455 --- /dev/null +++ b/lambda-generator/templates/lambda/tests/handler.test.ts.j2 @@ -0,0 +1,24 @@ +import { handler } from '../src/handler'; +import { mockClient } from 'aws-sdk-client-mock'; +import 'aws-sdk-client-mock-jest'; +import { before } from 'node:test'; + +describe('Placeholder', () => { + + test('Happy Path', async () => { + const event = {}; + const context = {}; + + const result = await handler(event as any, context as any, () => { }); + expect(result).toBeDefined(); + }); + + test('Error Case', async () => { + const event = {}; + const context = {}; + + const result = await handler(event as any, context as any, () => { }); + expect(result).toBeDefined(); + }); + +}); \ No newline at end of file diff --git a/lambda-generator/templates/lambda/tsconfig.json.j2 b/lambda-generator/templates/lambda/tsconfig.json.j2 new file mode 100644 index 0000000..62859a2 --- /dev/null +++ b/lambda-generator/templates/lambda/tsconfig.json.j2 @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es2020", + "strict": true, + "preserveConstEnums": true, + "noEmit": false, + "sourceMap": false, + "module":"commonjs", + "moduleResolution":"node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "rootDir": "./src", + "outDir": "prod", + "baseUrl": ".", + "paths": { + "clash-bot-shared": [ "node_modules/clash-bot-shared/src"], + "clash-bot-shared/*": [ "node_modules/clash-bot-shared/src/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.test.ts"], + } \ No newline at end of file diff --git a/lambda-generator/templates/terraform/lambda.tf.j2 b/lambda-generator/templates/terraform/lambda.tf.j2 new file mode 100644 index 0000000..f500ce7 --- /dev/null +++ b/lambda-generator/templates/terraform/lambda.tf.j2 @@ -0,0 +1,19 @@ +module "tournament_eligibility_lambda" { + source = "./modules/lambda" + + prefix = "{{ name }}" + s3_bucket_name = var.s3_bucket_name + environment = var.environment + + artifact_path = var.{{ name }}_artifact_path + + environment_variables = { + {% for item in items %} + {{ item }} = "placeholder" + {% endfor %} + } + + iam_policy_json = templatefile( + "${path.module}/policies/{{ name }}-lambda-policy.json", + ) +} \ No newline at end of file diff --git a/terraform/workflow/policies/websocket-publisher-lambda-policy.json b/terraform/workflow/policies/websocket-publisher-lambda-policy.json new file mode 100644 index 0000000..6d4cc8e --- /dev/null +++ b/terraform/workflow/policies/websocket-publisher-lambda-policy.json @@ -0,0 +1,27 @@ +{ + "Statement": [ + { + "Action": [ + "logs:PutLogEvents", + "logs:CreateLogStream", + "logs:CreateLogGroup" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "dynamodb:UpdateItem", + "dynamodb:Query", + "dynamodb:PutItem", + "dynamodb:GetItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem", + "dynamodb:BatchGetItem" + ], + "Effect": "Allow", + "Resource": "${DYNAMODB_ARN}" + } + ], + "Version": "2012-10-17" +} \ No newline at end of file diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index e06838f..d00f644 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -5,7 +5,8 @@ module "create_team_step_function" { definition = templatefile("${path.module}/step-functions/create-team-step-function.asl.json", { CreateTeamLambdaFunctionArn = module.create_team_lambda.arn, RetrieveTeamLambdaFunctionArn = module.retrieve_team_lambda.arn, - IsTournamentEligibleLambdaFunctionArn = module.tournament_eligibility_lambda.arn + IsTournamentEligibleLambdaFunctionArn = module.tournament_eligibility_lambda.arn, + WebSocketPublishLambdaFunctionArn = module.websocket_publisher_lambda.arn } ) @@ -88,6 +89,28 @@ module "tournament_eligibility_lambda" { ) } +module "websocket_publisher_lambda" { + source = "./modules/lambda" + + prefix = "websocket-publisher" + s3_bucket_name = var.s3_bucket_name + environment = var.environment + + artifact_path = var.websocket_publisher_artifact_path + + environment_variables = { + TOPIC_TO_SUBSCRIBERS_TABLE_NAME = module.events_table.dynamodb_table_arn, + WEBSOCKET_API_ENDPOINT = aws_apigatewayv2_api.clash_bot_websocket_api.api_endpoint + } + + iam_policy_json = templatefile( + "${path.module}/policies/websocket-publisher-lambda-policy.json", + { + DYNAMODB_ARN = module.events_table.dynamodb_table_arn + } + ) +} + resource "aws_lambda_permission" "apigw" { statement_id = "AllowExecutionFromAPIGateway-${lower(var.environment)}" action = "lambda:InvokeFunction" @@ -113,4 +136,12 @@ resource "aws_lambda_permission" "create_team_permission" { function_name = module.create_team_lambda.name principal = "states.amazonaws.com" source_arn = module.create_team_step_function.state_machine_arn +} + +resource "aws_lambda_permission" "websocket_publisher_permission" { + statement_id = "WebSocketPublisherPermission-${lower(var.environment)}" + action = "lambda:InvokeFunction" + function_name = module.websocket_publisher_lambda.name + principal = "states.amazonaws.com" + source_arn = module.create_team_step_function.state_machine_arn } \ No newline at end of file diff --git a/terraform/workflow/step-functions/create-team-step-function.asl.json b/terraform/workflow/step-functions/create-team-step-function.asl.json index 07c3239..c5a2b68 100644 --- a/terraform/workflow/step-functions/create-team-step-function.asl.json +++ b/terraform/workflow/step-functions/create-team-step-function.asl.json @@ -7,8 +7,8 @@ "Resource": "arn:aws:states:::lambda:invoke", "Parameters": { "Payload": { - "tournamant.$": "$.tournament.tournamentName", - "tournamentDay.$": "$.tournament.tournamentDay" + "tournamant.$": "$.payload.tournament.tournamentName", + "tournamentDay.$": "$.payload.tournament.tournamentDay" }, "FunctionName": "${IsTournamentEligibleLambdaFunctionArn}" }, @@ -35,13 +35,16 @@ }, "TournamentIsNotEligible": { "Type": "Pass", - "Result": "Tournamnet is not eligible for team creation.", - "End": true + "Result": { + "$.requestId": "$.requestId", + "$.messageToPublish": "Tournamnet is not eligible for team creation." + }, + "Next": "PublishEvent" }, "CreateTeam": { "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", - "OutputPath": "$.Payload", + "OutputPath": "$.messageToPublish", "Parameters": { "Payload.$": "$", "FunctionName": "${CreateTeamLambdaFunctionArn}" @@ -52,11 +55,26 @@ "Next": "DefaultErrorHandler" } ], - "End": true + "Next": "PublishEvent" }, "DefaultErrorHandler": { "Type": "Pass", - "Result": "An error occurred", + "Result": { + "$.requestId": "$.requestId", + "$.messageToPublish": "An error occurred" + }, + "Next": "PublishEvent" + }, + "PublishEvent": { + "Type": "Task", + "Resource": "arn:aws:states:::lambda:invoke", + "Parameters": { + "Payload": { + "requestId.$": "$.requestId", + "payload.$": "$.messageToPublish" + }, + "FunctionName": "${WebSocketPublishLambdaFunctionArn}" + }, "End": true } } diff --git a/terraform/workflow/variables.tf b/terraform/workflow/variables.tf index 38098b8..1ed7cc7 100644 --- a/terraform/workflow/variables.tf +++ b/terraform/workflow/variables.tf @@ -45,6 +45,11 @@ variable "tournament_eligibility_lambda_artifact_path" { description = "Path to the artifact for the tournament eligibility lambda function." } +variable "websocket_publisher_artifact_path" { + type = string + description = "Path to the artifact for the websocket publisher lambda function." +} + variable "sqs_batch_size" { type = number default = 1 From bf9575b96f304a08cab013a87ea01813e982973d Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sat, 25 Nov 2023 00:58:51 -0600 Subject: [PATCH 099/119] Adding websocket publisher to built artifacts --- .github/workflows/pull-request.yml | 34 +++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 2066270..408230b 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -158,6 +158,17 @@ jobs: needs: - terraformPreReqs + websocketPublisher: + uses: ./.github/workflows/build_and_deploy_lambda.yml + with: + working-directory: functions/clash-bot/websocket-publisher + artifact-name: websocket-publisher + s3-bucket-name: ${{ vars.S3_BUCKET_NAME }} + environment-name: Development + region: us-east-1 + needs: + - terraformPreReqs + terraformWorkflow: name: Workflow Apply runs-on: ubuntu-latest @@ -177,6 +188,7 @@ jobs: - createTeam - retrieveTeams - isTournamentEligible + - websocketPublisher steps: - uses: FranzDiebold/github-env-vars-action@v2.1.0 @@ -214,6 +226,10 @@ jobs: id: eventNotifierArtifact run: ../../scripts/find_latest_artifact.sh ${{ vars.S3_BUCKET_NAME }} event-notifier development + - name: Find latest Websocket Publisher artifact + id: webSocketPublisherArtifact + run: ../../scripts/find_latest_artifact.sh ${{ vars.S3_BUCKET_NAME }} websocket-publisher development + - name: Setup Terraform uses: hashicorp/setup-terraform@v1 with: @@ -243,6 +259,7 @@ jobs: TF_VAR_create_team_artifact_path: ${{ steps.createTeamArtifact.outputs.artifact-path }} TF_VAR_retrieve_teams_artifact_path: ${{ steps.retrieveTeamsArtifact.outputs.artifact-path }} TF_VAR_tournament_eligibility_lambda_artifact_path: ${{ steps.isTournamentEligibleArtifact.outputs.artifact-path }} + TF_VAR_websocket_publisher_artifact_path: ${{ steps.webSocketPublisherArtifact.outputs.artifact-path }} TF_VAR_sqs_batch_size: "1" run: terraform apply -no-color -input=false --auto-approve @@ -258,14 +275,15 @@ jobs: ## Lambda Function Versions used - | Lambda Function | Artifact Version | - | --------------- | ---------------- | - | Event Publisher | ${{ steps.eventPublisherArtifact.outputs.version }} | - | Event Handler | ${{ steps.eventHandlerArtifact.outputs.version }} | - | Event Notifier | ${{ steps.eventNotifierArtifact.outputs.version }} | - | Create Team | ${{ steps.createTeamArtifact.outputs.version }} | - | Retrieve Teams | ${{ steps.retrieveTeamsArtifact.outputs.version }} | - | Is Tournament Eligible | ${{ steps.isTournamentEligibleArtifact.outputs.version }} | + | Lambda Function | Type | Artifact Version | + | --------------- | ---- | ---------------- | + | Event Publisher | Foundation | ${{ steps.eventPublisherArtifact.outputs.version }} | + | Event Handler | Foundation | ${{ steps.eventHandlerArtifact.outputs.version }} | + | Event Notifier | Foundation | ${{ steps.eventNotifierArtifact.outputs.version }} | + | Websocket Publisher | Foundation | ${{ steps.webSocketPublisherArtifact.outputs.version }} | + | Create Team | Team | ${{ steps.createTeamArtifact.outputs.version }} | + | Retrieve Teams | Team | ${{ steps.retrieveTeamsArtifact.outputs.version }} | + | Is Tournament Eligible | Team | ${{ steps.isTournamentEligibleArtifact.outputs.version }} | #### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` From 9f7e45b36efb98a2c5f54b74b7d9e2cca7f78bac Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 4 Dec 2023 22:30:35 -0600 Subject: [PATCH 100/119] Updating to include WATCH_ALL subscription type to the ws api gateway --- .../websocket-publisher/src/handler.ts | 25 +++++++++----- .../websocket-publisher/tests/handler.test.ts | 34 +++++++++++++++++-- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/functions/clash-bot/websocket-publisher/src/handler.ts b/functions/clash-bot/websocket-publisher/src/handler.ts index f751a36..f9b104a 100644 --- a/functions/clash-bot/websocket-publisher/src/handler.ts +++ b/functions/clash-bot/websocket-publisher/src/handler.ts @@ -2,7 +2,7 @@ import { Handler } from 'aws-lambda'; import pino from "pino"; import { ApiGatewayManagementApiClient, PostToConnectionCommand } from '@aws-sdk/client-apigatewaymanagementapi'; -import { WebsocketEvent } from 'clash-bot-shared'; +import { SUBSCRIPTION_TYPE, WebsocketEvent } from 'clash-bot-shared'; import { DynamoDBClient, GetItemCommand } from '@aws-sdk/client-dynamodb'; import { unmarshall } from '@aws-sdk/util-dynamodb'; @@ -23,17 +23,26 @@ export const handler: Handler = async (event: WebsocketEvent) => { logger.info({ topic }, 'Retrieving subscriptions to topic...'); const dynamoDbClient = new DynamoDBClient({}); - const results = await dynamoDbClient.send(new GetItemCommand({ + + const topics = [topic, SUBSCRIPTION_TYPE.WATCH_ALL]; + logger.info({ topics }, 'Retrieving subscriptions to topics...') + const getTopicCommands = topics.map((topic) => new GetItemCommand({ TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, Key: { "topic": { S: topic } } })); - let subscribers: string[] = []; + const results = await Promise.all(getTopicCommands.map((command) => dynamoDbClient.send(command))); + + const subscribers: string[] = []; - if (results.Item) { - subscribers = [...unmarshall(results.Item).subscribers]; + if (results.length > 0) { + for (const getItemOutput of results) { + if (getItemOutput.Item !== undefined) { + subscribers.push(...unmarshall(getItemOutput.Item).subscribers); + } + } } else { logger.info({ topic }, 'No subscribers found...'); } @@ -41,7 +50,7 @@ export const handler: Handler = async (event: WebsocketEvent) => { logger.info({ subscribers }, 'Subscribers found...'); logger.info({ event }, 'Sending payload...'); - const posts = subscribers.map((subscriber) => { + const responses = await Promise.all(subscribers.map((subscriber) => { const requestParams = { ConnectionId: subscriber, Data: JSON.stringify(event.payload), @@ -49,9 +58,7 @@ export const handler: Handler = async (event: WebsocketEvent) => { const client = new ApiGatewayManagementApiClient({ endpoint: process.env.WEBSOCKET_API_ENDPOINT }) return client.send(new PostToConnectionCommand(requestParams)); - }); - - const responses = await Promise.all(posts); + })); return { posts: responses, diff --git a/functions/clash-bot/websocket-publisher/tests/handler.test.ts b/functions/clash-bot/websocket-publisher/tests/handler.test.ts index 0c10c87..bc02ac9 100644 --- a/functions/clash-bot/websocket-publisher/tests/handler.test.ts +++ b/functions/clash-bot/websocket-publisher/tests/handler.test.ts @@ -3,12 +3,12 @@ import { handler } from '../src/handler'; import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; import { ApiGatewayManagementApiClient, PostToConnectionCommand } from '@aws-sdk/client-apigatewaymanagementapi'; -import { WebsocketEvent } from 'clash-bot-shared'; +import { WebsocketEvent, SUBSCRIPTION_TYPE } from 'clash-bot-shared'; import { Context } from 'aws-lambda'; -describe('Placeholder', () => { +describe('Websocket Publisher', () => { - test('Happy Path', async () => { + test('Should query from Dynamo Db the list of connections associated to a specific topic based on the message request id and the all topic and post to the aws ws api', async () => { const topicKey = 'topic'; const event = { requestId: topicKey, @@ -17,6 +17,7 @@ describe('Placeholder', () => { } }; const context = {}; + const connectionIdForAll = 'connectionIdForAll'; const dynamoClientMock = mockClient(DynamoDBClient); const apiGatewayMock = mockClient(ApiGatewayManagementApiClient); @@ -34,6 +35,23 @@ describe('Placeholder', () => { } } }); + dynamoClientMock.on(GetItemCommand, { + TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, + Key: { + "topic": { S: SUBSCRIPTION_TYPE.WATCH_ALL } + } + }).resolves({ + Item: { + topic: { + S: topicKey + }, + subscribers: { + SS: [ + connectionIdForAll + ] + } + } + }); apiGatewayMock.on(PostToConnectionCommand) .resolves({}); @@ -45,10 +63,20 @@ describe('Placeholder', () => { "topic": { S: topicKey } } }); + expect(dynamoClientMock).toHaveReceivedCommandWith(GetItemCommand, { + TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, + Key: { + "topic": { S: SUBSCRIPTION_TYPE.WATCH_ALL } + } + }); expect(apiGatewayMock).toHaveReceivedCommandWith(PostToConnectionCommand, { ConnectionId: 'connectionId', Data: JSON.stringify(event.payload) }); + expect(apiGatewayMock).toHaveReceivedCommandWith(PostToConnectionCommand, { + ConnectionId: 'connectionIdForAll', + Data: JSON.stringify(event.payload) + }); }); test('No topics subscribers', async () => { From e34ff7f7bca019e65a182148edf692d7e63d33f9 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 4 Dec 2023 23:51:49 -0600 Subject: [PATCH 101/119] Adding service integration for websocket publisher --- terraform/workflow/step-functions.lambda.tf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index d00f644..b98c0e2 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -18,7 +18,8 @@ module "create_team_step_function" { lambda = [ module.create_team_lambda.arn, module.retrieve_team_lambda.arn, - module.tournament_eligibility_lambda.arn + module.tournament_eligibility_lambda.arn, + module.websocket_publisher_lambda.arn ] } } From 64dbf4d87935c938e79fe60ec4a7d750cefe7168 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Mon, 4 Dec 2023 23:53:31 -0600 Subject: [PATCH 102/119] Resolving typo --- .../create-team-step-function.asl.json | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/terraform/workflow/step-functions/create-team-step-function.asl.json b/terraform/workflow/step-functions/create-team-step-function.asl.json index c5a2b68..536f423 100644 --- a/terraform/workflow/step-functions/create-team-step-function.asl.json +++ b/terraform/workflow/step-functions/create-team-step-function.asl.json @@ -12,12 +12,13 @@ }, "FunctionName": "${IsTournamentEligibleLambdaFunctionArn}" }, - "ResultPath": "$.isTournamentEligible", - "OutputPath": "$.isEligible", + "ResultPath": "$.output", "Next": "CanTeamBeCreated", "Catch": [ { - "ErrorEquals": ["States.ALL"], + "ErrorEquals": [ + "States.ALL" + ], "Next": "DefaultErrorHandler" } ] @@ -26,7 +27,7 @@ "Type": "Choice", "Choices": [ { - "Variable": "$.isTournamentEligible", + "Variable": "$.output.Payload.isEligible", "BooleanEquals": false, "Next": "TournamentIsNotEligible" } @@ -35,11 +36,11 @@ }, "TournamentIsNotEligible": { "Type": "Pass", - "Result": { - "$.requestId": "$.requestId", - "$.messageToPublish": "Tournamnet is not eligible for team creation." - }, - "Next": "PublishEvent" + "Next": "PublishEvent", + "Parameters": { + "requestId.$": "$.requestId", + "messageToPublish": "Tournament is not eligible for team creation." + } }, "CreateTeam": { "Type": "Task", @@ -51,7 +52,9 @@ }, "Catch": [ { - "ErrorEquals": ["States.ALL"], + "ErrorEquals": [ + "States.ALL" + ], "Next": "DefaultErrorHandler" } ], From fb8cabe3b996e1a3466a81bb4a62ad1671cb651b Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 5 Dec 2023 00:24:02 -0600 Subject: [PATCH 103/119] Swapping to use table name instead --- terraform/workflow/step-functions.lambda.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index b98c0e2..f9279c7 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -100,7 +100,7 @@ module "websocket_publisher_lambda" { artifact_path = var.websocket_publisher_artifact_path environment_variables = { - TOPIC_TO_SUBSCRIBERS_TABLE_NAME = module.events_table.dynamodb_table_arn, + TOPIC_TO_SUBSCRIBERS_TABLE_NAME = module.events_table.dynamodb_table_id, WEBSOCKET_API_ENDPOINT = aws_apigatewayv2_api.clash_bot_websocket_api.api_endpoint } From 3f4f38b783584905201d248908928aed8cab3a38 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 5 Dec 2023 00:43:01 -0600 Subject: [PATCH 104/119] Building the callback url for the ws dynamically --- terraform/workflow/step-functions.lambda.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index f9279c7..177d7c7 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -101,7 +101,7 @@ module "websocket_publisher_lambda" { environment_variables = { TOPIC_TO_SUBSCRIBERS_TABLE_NAME = module.events_table.dynamodb_table_id, - WEBSOCKET_API_ENDPOINT = aws_apigatewayv2_api.clash_bot_websocket_api.api_endpoint + WEBSOCKET_API_ENDPOINT = "https://${aws_apigatewayv2_api.clash_bot_websocket_api.id}.execute-api.${var.region}.amazonaws.com/${aws_apigatewayv2_stage.clash_bot_websocket_api_stage.name}/@connections" } iam_policy_json = templatefile( From 483e43044520e844ac659a5a671799a91d8409fb Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 5 Dec 2023 00:52:57 -0600 Subject: [PATCH 105/119] Adding policy to allow invocation of api gateway --- .../policies/websocket-publisher-lambda-policy.json | 7 +++++++ terraform/workflow/step-functions.lambda.tf | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/terraform/workflow/policies/websocket-publisher-lambda-policy.json b/terraform/workflow/policies/websocket-publisher-lambda-policy.json index 6d4cc8e..51a20bf 100644 --- a/terraform/workflow/policies/websocket-publisher-lambda-policy.json +++ b/terraform/workflow/policies/websocket-publisher-lambda-policy.json @@ -21,6 +21,13 @@ ], "Effect": "Allow", "Resource": "${DYNAMODB_ARN}" + }, + { + "Action": [ + "execute-api:Invoke" + ], + "Effect": "Allow", + "Resource": "${WS_GATEWAY_ARN}/POST/@connections/*" } ], "Version": "2012-10-17" diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 177d7c7..1c4b7cb 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -101,13 +101,14 @@ module "websocket_publisher_lambda" { environment_variables = { TOPIC_TO_SUBSCRIBERS_TABLE_NAME = module.events_table.dynamodb_table_id, - WEBSOCKET_API_ENDPOINT = "https://${aws_apigatewayv2_api.clash_bot_websocket_api.id}.execute-api.${var.region}.amazonaws.com/${aws_apigatewayv2_stage.clash_bot_websocket_api_stage.name}/@connections" + WEBSOCKET_API_ENDPOINT = "https://${aws_apigatewayv2_api.clash_bot_websocket_api.id}.execute-api.${var.region}.amazonaws.com/${aws_apigatewayv2_stage.clash_bot_websocket_api_stage.name}" } iam_policy_json = templatefile( "${path.module}/policies/websocket-publisher-lambda-policy.json", { - DYNAMODB_ARN = module.events_table.dynamodb_table_arn + DYNAMODB_ARN = module.events_table.dynamodb_table_arn, + WS_GATEWAY_ARN = aws_apigatewayv2_api.clash_bot_websocket_api.arn } ) } From 88f7d5b7b154b7c0e17b1b19fc851d0b98f84c17 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 5 Dec 2023 00:57:27 -0600 Subject: [PATCH 106/119] Setting a rate limit and adding correct permissions --- terraform/workflow/step-functions.lambda.tf | 2 +- terraform/workflow/ws-api-gateway.tf | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 1c4b7cb..81ac08f 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -108,7 +108,7 @@ module "websocket_publisher_lambda" { "${path.module}/policies/websocket-publisher-lambda-policy.json", { DYNAMODB_ARN = module.events_table.dynamodb_table_arn, - WS_GATEWAY_ARN = aws_apigatewayv2_api.clash_bot_websocket_api.arn + WS_GATEWAY_ARN = aws_apigatewayv2_stage.clash_bot_websocket_api_stage.execution_arn } ) } diff --git a/terraform/workflow/ws-api-gateway.tf b/terraform/workflow/ws-api-gateway.tf index b32c6c5..44f15ba 100644 --- a/terraform/workflow/ws-api-gateway.tf +++ b/terraform/workflow/ws-api-gateway.tf @@ -53,5 +53,11 @@ resource "aws_apigatewayv2_stage" "clash_bot_websocket_api_stage" { api_id = aws_apigatewayv2_api.clash_bot_websocket_api.id name = "events-${var.environment}" description = "Clash Bot Workflow Websocket API stage" + + default_route_settings { + throttling_burst_limit = 100 + throttling_rate_limit = 100.0 + } + auto_deploy = true } \ No newline at end of file From 5142ab229ea7cdc8dbad91602d7caf5697ecfee3 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 5 Dec 2023 01:02:01 -0600 Subject: [PATCH 107/119] Adding manage connections permission --- .../workflow/policies/websocket-publisher-lambda-policy.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/terraform/workflow/policies/websocket-publisher-lambda-policy.json b/terraform/workflow/policies/websocket-publisher-lambda-policy.json index 51a20bf..4ac7ef3 100644 --- a/terraform/workflow/policies/websocket-publisher-lambda-policy.json +++ b/terraform/workflow/policies/websocket-publisher-lambda-policy.json @@ -24,7 +24,8 @@ }, { "Action": [ - "execute-api:Invoke" + "execute-api:Invoke", + "execute-api:ManageConnections" ], "Effect": "Allow", "Resource": "${WS_GATEWAY_ARN}/POST/@connections/*" From c1c73f44c89ca6c665118227e4fc1e6e0fa0903c Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Thu, 7 Dec 2023 23:09:29 -0600 Subject: [PATCH 108/119] Updating is eligible to use correct Tournament key and updating to use user defined request id --- .../clash-bot/event-publisher/src/handler.ts | 16 ++++-- .../tests/event-publisher.test.ts | 49 ++++++++++++++++++- .../is-tournament-eligible/src/handler.ts | 3 +- .../tests/handler.test.ts | 15 +++--- .../retrieve-active-clash-tournaments.json | 30 ++++++++++++ scripts/setup-data.sh | 11 +++++ scripts/setup-data/tournaments.json | 16 ++++++ 7 files changed, 127 insertions(+), 13 deletions(-) create mode 100644 sample-data/retrieve-active-clash-tournaments.json create mode 100755 scripts/setup-data.sh create mode 100644 scripts/setup-data/tournaments.json diff --git a/functions/clash-bot/event-publisher/src/handler.ts b/functions/clash-bot/event-publisher/src/handler.ts index eb64f39..8494146 100644 --- a/functions/clash-bot/event-publisher/src/handler.ts +++ b/functions/clash-bot/event-publisher/src/handler.ts @@ -15,6 +15,16 @@ export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEven try { const sqsClient = new SQSClient({}); const mappedEventType = eventMap.get(event.requestContext.path); + if (!event.body || !JSON.parse(event.body).uuid) { + logger.error(`Missing uuid in body for request url=${event.requestContext.path}...`); + return { + statusCode: 400, + body: JSON.stringify({ + error: "Missing request id." + }) + }; + } + const requestIdFromUser = JSON.parse(event.body).uuid; if (!mappedEventType) { logger.error(`Unmapped event found url=${event.requestContext.path}`); return { @@ -28,7 +38,7 @@ export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEven logger.info(`Event Type for url => ${mappedEventType}...`) const eventToBeSent: EventPayload = { payload: JSON.parse(event.body!), - uuid: event.requestContext.requestId, + uuid: requestIdFromUser, event: mappedEventType, url: event.requestContext.path, }; @@ -36,7 +46,7 @@ export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEven QueueUrl: process.env.QUEUE_URL, MessageBody: JSON.stringify(eventToBeSent), MessageGroupId: 'event', - MessageDeduplicationId: event.requestContext.requestId + MessageDeduplicationId: requestIdFromUser }; const message = new SendMessageCommand(input); const response = await sqsClient @@ -45,7 +55,7 @@ export const handler: APIGatewayProxyHandler = async (event: APIGatewayProxyEven return { statusCode: 200, body: JSON.stringify({ - requestId: event.requestContext.requestId + requestId: requestIdFromUser }) }; } catch (error) { diff --git a/functions/clash-bot/event-publisher/tests/event-publisher.test.ts b/functions/clash-bot/event-publisher/tests/event-publisher.test.ts index db98a3b..7960e35 100644 --- a/functions/clash-bot/event-publisher/tests/event-publisher.test.ts +++ b/functions/clash-bot/event-publisher/tests/event-publisher.test.ts @@ -17,7 +17,9 @@ describe('Publish an event to SQS with an event type.', () => { snsMock .on(SendMessageCommand) .resolvesOnce({}); + const requestIdFromUser = "1234"; const bodyOfRequest = { + uuid: requestIdFromUser, details: {} }; const eventTwo: APIGatewayProxyEvent = createEvent( @@ -27,7 +29,7 @@ describe('Publish an event to SQS with an event type.', () => { ); const expectedEventToBeSent: EventPayload = { payload: bodyOfRequest, - uuid: eventTwo.requestContext.requestId, + uuid: requestIdFromUser, event: EVENT_TYPE.CREATE_TEAM, url: eventTwo.requestContext.path, }; @@ -36,7 +38,49 @@ describe('Publish an event to SQS with an event type.', () => { QueueUrl: process.env.QUEUE_URL, MessageBody: JSON.stringify(expectedEventToBeSent), MessageGroupId: 'event', - MessageDeduplicationId: eventTwo.requestContext.requestId + MessageDeduplicationId: requestIdFromUser + }); + }); + + describe("Missing uuid", () => { + + test('Error - If the request does not have a uuid, then return a bad request response.', async () => { + const snsMock = mockClient(SQSClient) + snsMock + .on(SendMessageCommand) + .resolvesOnce({}); + const bodyOfRequest = { + details: { + somebody: "somebody" + } + }; + const eventTwo: APIGatewayProxyEvent = createEvent( + "/api/v2/teams", + "POST", + bodyOfRequest + ); + const response = await handler(eventTwo, setupContext(), {} as any); + expect((response as APIGatewayProxyResult).statusCode).toBe(400); + }); + + test('Error - If the request does not have a uuid as it is an empty string, then return a bad request response.', async () => { + const snsMock = mockClient(SQSClient) + snsMock + .on(SendMessageCommand) + .resolvesOnce({}); + const bodyOfRequest = { + uuid: "", + details: { + somebody: "somebody" + } + }; + const eventTwo: APIGatewayProxyEvent = createEvent( + "/api/v2/teams", + "POST", + bodyOfRequest + ); + const response = await handler(eventTwo, setupContext(), {} as any); + expect((response as APIGatewayProxyResult).statusCode).toBe(400); }); }); @@ -63,6 +107,7 @@ describe('Publish an event to SQS with an event type.', () => { .on(SendMessageCommand) .rejects({}); const bodyOfRequest = { + uuid: "1234", details: {} }; const eventTwo: APIGatewayProxyEvent = createEvent( diff --git a/functions/clash-bot/team/is-tournament-eligible/src/handler.ts b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts index de158f6..a7e3b6a 100644 --- a/functions/clash-bot/team/is-tournament-eligible/src/handler.ts +++ b/functions/clash-bot/team/is-tournament-eligible/src/handler.ts @@ -2,6 +2,7 @@ import { Handler } from 'aws-lambda'; import pino from "pino"; import { DynamoDBClient, QueryCommand, QueryCommandInput } from '@aws-sdk/client-dynamodb'; import { unmarshall } from '@aws-sdk/util-dynamodb'; +import { TABLE_TYPES } from 'clash-bot-shared'; export const handler: Handler = async (event, context) => { const level = process.env.LOGGER_LEVEL === undefined ? "info" : process.env.LOGGER_LEVEL; @@ -27,7 +28,7 @@ export const handler: Handler = async (event, context) => { }, ExpressionAttributeValues: { ":type": { - S: "Tournament" + S: TABLE_TYPES.TOURNAMENT }, } }; diff --git a/functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts b/functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts index 42d63c4..28eb9a1 100644 --- a/functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts +++ b/functions/clash-bot/team/is-tournament-eligible/tests/handler.test.ts @@ -3,6 +3,7 @@ import { DynamoDBClient, QueryCommand } from '@aws-sdk/client-dynamodb'; import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; import { marshall } from '@aws-sdk/util-dynamodb'; +import { TABLE_TYPES } from 'clash-bot-shared'; describe('handler', () => { test('If the tournament is after the current date, then respond with true', async () => { @@ -14,7 +15,7 @@ describe('handler', () => { mockQueryCommand.mockResolvedValue({ Items: [ marshall({ - type: 'Tournament', + type: TABLE_TYPES.TOURNAMENT, tournament: tournamentName, date: date.toISOString() }) @@ -38,7 +39,7 @@ describe('handler', () => { '#tournament': 'tournament' }, ExpressionAttributeValues: { - ':type': { S: 'Tournament' }, + ':type': { S: TABLE_TYPES.TOURNAMENT, }, ':tournament': { S: tournamentName } } }); @@ -54,7 +55,7 @@ describe('handler', () => { mockQueryCommand.mockResolvedValue({ Items: [ marshall({ - type: 'Tournament', + type: TABLE_TYPES.TOURNAMENT, tournament: tournamentName, tournamentDay, date: date.toISOString() @@ -81,7 +82,7 @@ describe('handler', () => { '#tournamentDay': 'tournamentDay' }, ExpressionAttributeValues: { - ':type': { S: 'Tournament' }, + ':type': { S: TABLE_TYPES.TOURNAMENT }, ':tournament': { S: tournamentName }, ':tournamentDay': { S: tournamentDay } } @@ -100,13 +101,13 @@ describe('handler', () => { mockQueryCommand.mockResolvedValue({ Items: [ marshall({ - type: 'Tournament', + type: TABLE_TYPES.TOURNAMENT, tournament: tournamentName, tournamentDay, date: priorDate.toISOString() }), marshall({ - type: 'Tournament', + type: TABLE_TYPES.TOURNAMENT, tournament: tournamentName, tournamentDay: '2', date: date.toISOString() @@ -133,7 +134,7 @@ describe('handler', () => { '#tournamentDay': 'tournamentDay' }, ExpressionAttributeValues: { - ':type': { S: 'Tournament' }, + ':type': { S: TABLE_TYPES.TOURNAMENT }, ':tournament': { S: tournamentName }, ':tournamentDay': { S: tournamentDay } } diff --git a/sample-data/retrieve-active-clash-tournaments.json b/sample-data/retrieve-active-clash-tournaments.json new file mode 100644 index 0000000..2405014 --- /dev/null +++ b/sample-data/retrieve-active-clash-tournaments.json @@ -0,0 +1,30 @@ +[ + { + "id": 135101, + "themeI d": 4, + "nameKey": "aram2022", + "nameKeySecondary": "day_4", + "schedule": [ + { + "id": 127581, + "registrationTime": 1702253700000, + "startTime": 1702263600000, + "cancelled": false + } + ] + }, + { + "id": 135081, + "themeId": 4, + "nameKey": "aram2022", + "nameKeySecondary": "day_3", + "schedule": [ + { + "id": 127561, + "registrationTime": 1702167300000, + "startTime": 1702177200000, + "cancelled": false + } + ] + } +] \ No newline at end of file diff --git a/scripts/setup-data.sh b/scripts/setup-data.sh new file mode 100755 index 0000000..76de2f6 --- /dev/null +++ b/scripts/setup-data.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +PROFILE_NAME="Master" +REGION="us-east-1" +TABLE_NAME="clash-bot-workflow-development" + +for row in $(cat ./setup-data/tournaments.json | jq -r '.[] | @base64'); do + ITEM=$(echo ${row} | base64 --decode | jq -r '{type: .type, id: .id, tournament: .tournament, date: .date, tournamentDay: .tournamentDay} | map_values({S: .})') + echo ${ITEM} + aws dynamodb put-item --table-name ${TABLE_NAME} --item "${ITEM}" --profile ${PROFILE_NAME} --region ${REGION} +done \ No newline at end of file diff --git a/scripts/setup-data/tournaments.json b/scripts/setup-data/tournaments.json new file mode 100644 index 0000000..26bfd2c --- /dev/null +++ b/scripts/setup-data/tournaments.json @@ -0,0 +1,16 @@ +[ + { + "type": "TOURNAMENT", + "id": "aram2022#day_3#135081", + "tournament": "aram2022", + "date": "2022-03-01T00:00:00Z", + "tournamentDay": "day_3" + }, + { + "type": "TOURNAMENT", + "id": "aram2022#day_4#135101", + "tournament": "aram2022", + "date": "2022-03-08T00:00:00Z", + "tournamentDay": "day_4" + } + ] \ No newline at end of file From 31aac448cec6bd20d953e506c90462879ec88960 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Sun, 10 Dec 2023 22:48:22 -0600 Subject: [PATCH 109/119] Fixing no WATCH_ALL mode issue with publisher --- .../websocket-publisher/src/handler.ts | 7 +- .../websocket-publisher/tests/handler.test.ts | 66 ++++++++++++++++++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/functions/clash-bot/websocket-publisher/src/handler.ts b/functions/clash-bot/websocket-publisher/src/handler.ts index f9b104a..5de9ad9 100644 --- a/functions/clash-bot/websocket-publisher/src/handler.ts +++ b/functions/clash-bot/websocket-publisher/src/handler.ts @@ -39,12 +39,17 @@ export const handler: Handler = async (event: WebsocketEvent) => { if (results.length > 0) { for (const getItemOutput of results) { - if (getItemOutput.Item !== undefined) { + if (getItemOutput.Item !== undefined && getItemOutput.Item.subscribers !== undefined) { subscribers.push(...unmarshall(getItemOutput.Item).subscribers); } } } else { logger.info({ topic }, 'No subscribers found...'); + return { + posts: [], + topic, + payload: event.payload + } } logger.info({ subscribers }, 'Subscribers found...'); diff --git a/functions/clash-bot/websocket-publisher/tests/handler.test.ts b/functions/clash-bot/websocket-publisher/tests/handler.test.ts index bc02ac9..8b00d82 100644 --- a/functions/clash-bot/websocket-publisher/tests/handler.test.ts +++ b/functions/clash-bot/websocket-publisher/tests/handler.test.ts @@ -129,7 +129,11 @@ describe('Websocket Publisher', () => { .resolves({}); const result = await handler(event as WebsocketEvent, context as Context, () => { }); - expect(result).toBeDefined(); + expect(result).toEqual({ + posts: [], + topic: topicKey, + payload: event.payload + }); expect(dynamoClientMock).toHaveReceivedCommandWith(GetItemCommand, { TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, Key: { @@ -139,6 +143,66 @@ describe('Websocket Publisher', () => { expect(apiGatewayMock).not.toHaveReceivedCommand(PostToConnectionCommand); }); + test('No topics subscribers for WATCH_ALL', async () => { + const topicKey = 'topic'; + const connectionIdForAll = '1234'; + const event = { + requestId: topicKey, + payload: { + message: 'message' + } + }; + const context = {}; + + const dynamoClientMock = mockClient(DynamoDBClient); + const apiGatewayMock = mockClient(ApiGatewayManagementApiClient); + + dynamoClientMock.on(GetItemCommand, { + TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, + Key: { + "topic": { S: SUBSCRIPTION_TYPE.WATCH_ALL } + } + }).resolves({ + Item: {} + }); + + dynamoClientMock.on(GetItemCommand, { + TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, + Key: { + "topic": { S: topicKey } + } + }).resolves({ + Item: { + topic: { + S: topicKey + }, + subscribers: { + SS: [ + connectionIdForAll + ] + } + } + }); + apiGatewayMock.on(PostToConnectionCommand) + .resolves({}); + + const result = await handler(event as WebsocketEvent, context as Context, () => { }); + expect(result).toBeDefined(); + expect(dynamoClientMock).toHaveReceivedCommandWith(GetItemCommand, { + TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, + Key: { + "topic": { S: topicKey } + } + }); + expect(dynamoClientMock).toHaveReceivedCommandWith(GetItemCommand, { + TableName: process.env.TOPIC_TO_SUBSCRIBERS_TABLE_NAME, + Key: { + "topic": { S: SUBSCRIPTION_TYPE.WATCH_ALL } + } + }); + expect(apiGatewayMock).toHaveReceivedCommand(PostToConnectionCommand); + }); + test('Error Case - Missing requestId', async () => { const event = {}; const context = {}; From cfb907e9d2a7781c3632f4e1138c5aa514a0652d Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Wed, 20 Dec 2023 17:01:36 -0600 Subject: [PATCH 110/119] Swapping to use PAY_PER_REQUEST instead of PROVISIONED --- terraform/workflow/dynamodb.tf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/terraform/workflow/dynamodb.tf b/terraform/workflow/dynamodb.tf index d0019a6..de4d895 100644 --- a/terraform/workflow/dynamodb.tf +++ b/terraform/workflow/dynamodb.tf @@ -4,7 +4,7 @@ module "dynamodb_table" { name = "clash-bot-workflow-${var.environment}" hash_key = "type" range_key = "id" - billing_mode = "PROVISIONED" + billing_mode = "PAY_PER_REQUEST" write_capacity = 5 read_capacity = 1 @@ -25,7 +25,7 @@ module "events_table" { name = "clash-bot-topics-${var.environment}" hash_key = "topic" - billing_mode = "PROVISIONED" + billing_mode = "PAY_PER_REQUEST" write_capacity = 5 read_capacity = 1 @@ -42,7 +42,7 @@ module "subscriber_table" { name = "clash-bot-subscriber-${var.environment}" hash_key = "subscriber" - billing_mode = "PROVISIONED" + billing_mode = "PAY_PER_REQUEST" write_capacity = 5 read_capacity = 1 From ab8fc7ed1ce61ba99f541159a0f9f311a493e2ae Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 12 Mar 2024 21:56:10 -0500 Subject: [PATCH 111/119] Removing node_modules to be uploaded for each lambda --- functions/clash-bot-layer/package.json | 41 +++++++++++++++++++ .../clash-bot/event-handler/package.json | 4 +- .../clash-bot/event-handler/src/handler.ts | 2 + .../clash-bot/event-notifier/package.json | 2 +- .../clash-bot/event-publisher/package.json | 2 +- functions/clash-bot/team/create/package.json | 2 +- .../team/is-tournament-eligible/package.json | 2 +- .../clash-bot/team/retrieve/package.json | 2 +- .../websocket-publisher/package.json | 2 +- .../templates/lambda/package.json.j2 | 2 +- scripts/list-dependencies.sh | 26 ++++++++++++ scripts/package-and-upload-lambda-layer.sh | 29 +++++++++++++ terraform/prereqs/main.tf | 10 ++++- terraform/workflow/modules/lambda/lambda.tf | 6 ++- .../workflow/modules/lambda/variables.tf | 23 +++++++---- terraform/workflow/step-functions.lambda.tf | 12 ++++++ 16 files changed, 147 insertions(+), 20 deletions(-) create mode 100644 functions/clash-bot-layer/package.json create mode 100755 scripts/list-dependencies.sh create mode 100755 scripts/package-and-upload-lambda-layer.sh diff --git a/functions/clash-bot-layer/package.json b/functions/clash-bot-layer/package.json new file mode 100644 index 0000000..d7cc48e --- /dev/null +++ b/functions/clash-bot-layer/package.json @@ -0,0 +1,41 @@ +{ + "name": "clash-bot-layer", + "version": "1.0.0", + "description": "Used for packaging shared dependencies", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Daniel Poss", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-apigatewaymanagementapi": "^3.529.1", + "@aws-sdk/client-dynamodb": "^3.529.1", + "@aws-sdk/client-s3": "^3.529.1", + "@aws-sdk/client-sfn": "^3.529.1", + "@aws-sdk/client-sqs": "^3.529.1", + "@aws-sdk/types": "^3.523.0", + "@aws-sdk/util-dynamodb": "^3.529.1", + "@openapitools/openapi-generator-cli": "^2.12.0", + "@types/aws-lambda": "^8.10.136", + "@types/jest": "^29.5.12", + "@types/pino": "^7.0.5", + "@types/uuid": "^9.0.8", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "aws-sdk-client-mock": "^3.0.1", + "aws-sdk-client-mock-jest": "^3.0.1", + "clash-bot-shared": "github:Poss111/ClashBotWorkflowShared#main", + "dotenv": "^16.4.5", + "eslint": "^8.57.0", + "gulp": "^4.0.2", + "gulp-typescript": "^6.0.0-alpha.1", + "jest": "^29.7.0", + "pino": "^8.19.0", + "pino-pretty": "^10.3.1", + "ts-jest": "^29.1.2", + "ts-mockito": "^2.6.1", + "typescript": "^5.4.2", + "uuid": "^9.0.1" + } +} diff --git a/functions/clash-bot/event-handler/package.json b/functions/clash-bot/event-handler/package.json index 9cffc29..4ac045c 100644 --- a/functions/clash-bot/event-handler/package.json +++ b/functions/clash-bot/event-handler/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "jest", "build": "tsc", - "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + "postbuild": "cp package.json prod/package.json" }, "keywords": [ "clash-bot", @@ -35,4 +35,4 @@ "pino": "^8.15.0", "clash-bot-shared": "github:Poss111/ClashBotWorkflowShared#main" } -} +} \ No newline at end of file diff --git a/functions/clash-bot/event-handler/src/handler.ts b/functions/clash-bot/event-handler/src/handler.ts index 022ce15..de123f6 100644 --- a/functions/clash-bot/event-handler/src/handler.ts +++ b/functions/clash-bot/event-handler/src/handler.ts @@ -23,6 +23,8 @@ export const handler: SQSHandler = async (event: SQSEvent) => { logger.info(`Setting up payload to trigger SFN ${sfArn}...`); + logger.info(`Triggering SFN ${sfArn}...`) + return client.send(new StartExecutionCommand({ stateMachineArn: sfArn, name: `${parsedEvent.event}-${record.messageId}`, diff --git a/functions/clash-bot/event-notifier/package.json b/functions/clash-bot/event-notifier/package.json index c9c2fd3..99e33a7 100644 --- a/functions/clash-bot/event-notifier/package.json +++ b/functions/clash-bot/event-notifier/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "jest", "build": "tsc", - "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + "postbuild": "cp package.json prod/package.json" }, "keywords": [ "clash-bot" diff --git a/functions/clash-bot/event-publisher/package.json b/functions/clash-bot/event-publisher/package.json index e0a80ff..409b63e 100644 --- a/functions/clash-bot/event-publisher/package.json +++ b/functions/clash-bot/event-publisher/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "jest", "build": "tsc", - "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + "postbuild": "cp package.json prod/package.json" }, "keywords": [ "clash-bot" diff --git a/functions/clash-bot/team/create/package.json b/functions/clash-bot/team/create/package.json index 95ad8c1..28f88e5 100644 --- a/functions/clash-bot/team/create/package.json +++ b/functions/clash-bot/team/create/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "jest", "build": "tsc", - "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + "postbuild": "cp package.json prod/package.json" }, "keywords": [ "clash-bot" diff --git a/functions/clash-bot/team/is-tournament-eligible/package.json b/functions/clash-bot/team/is-tournament-eligible/package.json index 31f7789..3e913b4 100644 --- a/functions/clash-bot/team/is-tournament-eligible/package.json +++ b/functions/clash-bot/team/is-tournament-eligible/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "jest", "build": "tsc", - "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + "postbuild": "cp package.json prod/package.json" }, "keywords": [ "clash-bot" diff --git a/functions/clash-bot/team/retrieve/package.json b/functions/clash-bot/team/retrieve/package.json index 680ee04..a73ae68 100644 --- a/functions/clash-bot/team/retrieve/package.json +++ b/functions/clash-bot/team/retrieve/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "jest", "build": "tsc", - "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + "postbuild": "cp package.json prod/package.json" }, "keywords": [ "clash-bot" diff --git a/functions/clash-bot/websocket-publisher/package.json b/functions/clash-bot/websocket-publisher/package.json index 42c464a..66b33b9 100644 --- a/functions/clash-bot/websocket-publisher/package.json +++ b/functions/clash-bot/websocket-publisher/package.json @@ -7,7 +7,7 @@ "test": "jest", "prebuild": "eslint . --ext .ts", "build": "tsc", - "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + "postbuild": "cp package.json prod/package.json" }, "keywords": [ "clash-bot" diff --git a/lambda-generator/templates/lambda/package.json.j2 b/lambda-generator/templates/lambda/package.json.j2 index 4146632..2abe661 100644 --- a/lambda-generator/templates/lambda/package.json.j2 +++ b/lambda-generator/templates/lambda/package.json.j2 @@ -6,7 +6,7 @@ "scripts": { "test": "jest", "build": "tsc", - "postbuild": "cp package.json prod/package.json && cd prod && npm install --omit=dev" + "postbuild": "cp package.json prod/package.json" }, "keywords": [ "clash-bot" diff --git a/scripts/list-dependencies.sh b/scripts/list-dependencies.sh new file mode 100755 index 0000000..f275371 --- /dev/null +++ b/scripts/list-dependencies.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Directory to search +DIR="../functions/clash-bot" + +# Find all package.json files in the directory +FILES=$(find $DIR -name 'node_modules' -prune -o -name 'package.json' -print) + +# Initialize an empty string to hold all dependencies +ALL_DEPENDENCIES="" + +# Loop over each file +for FILE in $FILES +do + # Extract dependencies and devDependencies + DEPENDENCIES=$(jq -r '.dependencies | to_entries[] | .key + "@" + .value' $FILE) + + # Add the dependencies from this file to the list of all dependencies + ALL_DEPENDENCIES="$ALL_DEPENDENCIES $DEPENDENCIES" +done + +# Remove duplicate dependencies +ALL_DEPENDENCIES=$(echo $ALL_DEPENDENCIES | tr ' ' '\n' | sort -u | tr '\n' ' ') + +# Generate the npm install command +echo "npm install --save-dev $ALL_DEPENDENCIES" \ No newline at end of file diff --git a/scripts/package-and-upload-lambda-layer.sh b/scripts/package-and-upload-lambda-layer.sh new file mode 100755 index 0000000..a00d7a4 --- /dev/null +++ b/scripts/package-and-upload-lambda-layer.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Directory to zip +DIR="../functions/clash-bot-layer/node_modules" + +# S3 bucket name +BUCKET_NAME="$1" + +# Zip file name +ZIP_FILE="lambda_layer.zip" + +# S3 key +S3_KEY="layers/$ZIP_FILE" + +# Create a temporary directory +TEMP_DIR=$(mktemp -d -t nodejs) + +# Copy the subdirectory to the temporary directory +cp -r $DIR $TEMP_DIR/ + +# Zip the directory +zip -r $ZIP_FILE $TEMP_DIR/* + +# Upload the zip file to S3 +aws s3 cp $ZIP_FILE s3://$BUCKET_NAME/$S3_KEY --profile Master + +# Remove the zip file and the temporary directory +rm $ZIP_FILE +rm -r $TEMP_DIR \ No newline at end of file diff --git a/terraform/prereqs/main.tf b/terraform/prereqs/main.tf index 5d24bfe..f4e2707 100644 --- a/terraform/prereqs/main.tf +++ b/terraform/prereqs/main.tf @@ -29,4 +29,12 @@ module "lambda_bucket" { ] } POLICY -} \ No newline at end of file +} + +resource "aws_lambda_layer_version" "lambda_layer" { + s3_bucket = module.lambda_bucket.s3_bucket_id + s3_key = "layers/lambda_layer.zip" + layer_name = "clash-bot-workflow-layer" + + compatible_runtimes = ["nodejs16.x"] +} diff --git a/terraform/workflow/modules/lambda/lambda.tf b/terraform/workflow/modules/lambda/lambda.tf index c23fac8..126d512 100644 --- a/terraform/workflow/modules/lambda/lambda.tf +++ b/terraform/workflow/modules/lambda/lambda.tf @@ -7,6 +7,10 @@ resource "aws_lambda_function" "lambda" { s3_bucket = var.s3_bucket_name s3_key = var.artifact_path + layers = [ + var.lambda_layer_arn + ] + environment { variables = var.environment_variables } @@ -38,4 +42,4 @@ resource "aws_iam_policy" "policy" { name = "${var.prefix}-Policy-${lower(var.environment)}" description = "Policy for the ${var.prefix} lambda function." policy = var.iam_policy_json -} \ No newline at end of file +} diff --git a/terraform/workflow/modules/lambda/variables.tf b/terraform/workflow/modules/lambda/variables.tf index 5f9a0cf..d08103d 100644 --- a/terraform/workflow/modules/lambda/variables.tf +++ b/terraform/workflow/modules/lambda/variables.tf @@ -1,29 +1,34 @@ variable "prefix" { - type = string - description = "The prefix to use for all resources." + type = string + description = "The prefix to use for all resources." } variable "environment" { - type = string - description = "The environment to use." + type = string + description = "The environment to use." } variable "s3_bucket_name" { - type = string + type = string description = "The s3 bucket that stores the lambda function code." } variable "artifact_path" { - type = string + type = string description = "Path to the artifact for the lambda function." } variable "environment_variables" { - type = map(string) + type = map(string) description = "Environment variables to set for the lambda function." } variable "iam_policy_json" { - type = string + type = string description = "The json for the iam policy." -} \ No newline at end of file +} + +variable "lambda_layer_arn" { + type = string + description = "The arn of the lambda layer." +} diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 81ac08f..942ec6c 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -1,3 +1,7 @@ +data "lambda_layer_version" "lambda_layer" { + layer_name = "clash-bot-workflow-layer" +} + module "create_team_step_function" { source = "terraform-aws-modules/step-functions/aws" @@ -36,6 +40,8 @@ module "create_team_lambda" { artifact_path = var.create_team_artifact_path + lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id } @@ -57,6 +63,8 @@ module "retrieve_team_lambda" { artifact_path = var.retrieve_teams_artifact_path + lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id } @@ -78,6 +86,8 @@ module "tournament_eligibility_lambda" { artifact_path = var.tournament_eligibility_lambda_artifact_path + lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id } @@ -99,6 +109,8 @@ module "websocket_publisher_lambda" { artifact_path = var.websocket_publisher_artifact_path + lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + environment_variables = { TOPIC_TO_SUBSCRIBERS_TABLE_NAME = module.events_table.dynamodb_table_id, WEBSOCKET_API_ENDPOINT = "https://${aws_apigatewayv2_api.clash_bot_websocket_api.id}.execute-api.${var.region}.amazonaws.com/${aws_apigatewayv2_stage.clash_bot_websocket_api_stage.name}" From 7181c83c9020833f3f47decaedca25eeeeb41466 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 12 Mar 2024 23:08:38 -0500 Subject: [PATCH 112/119] Fixing data resource --- terraform/workflow/event-setup-lambdas.tf | 6 ++++++ terraform/workflow/providers.tf | 6 ++++++ terraform/workflow/step-functions.lambda.tf | 10 +++++----- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/terraform/workflow/event-setup-lambdas.tf b/terraform/workflow/event-setup-lambdas.tf index dd1eaac..b919edb 100644 --- a/terraform/workflow/event-setup-lambdas.tf +++ b/terraform/workflow/event-setup-lambdas.tf @@ -5,6 +5,8 @@ module "event_handler_lambda" { s3_bucket_name = var.s3_bucket_name environment = var.environment + lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + artifact_path = var.event_handler_artifact_path environment_variables = { @@ -38,6 +40,8 @@ module "event_publisher_lambda" { s3_bucket_name = var.s3_bucket_name environment = var.environment + lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + artifact_path = var.event_publisher_artifact_path environment_variables = { @@ -59,6 +63,8 @@ module "event_notifier_lambda" { s3_bucket_name = var.s3_bucket_name environment = var.environment + lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + artifact_path = var.event_notifier_artifact_path environment_variables = { diff --git a/terraform/workflow/providers.tf b/terraform/workflow/providers.tf index 9d17f83..471fa3d 100644 --- a/terraform/workflow/providers.tf +++ b/terraform/workflow/providers.tf @@ -10,5 +10,11 @@ provider "aws" { } terraform { + required_version = ">= 0.12.0" backend "remote" {} + required_providers { + aws = { + source = "hashicorp/aws" + } + } } \ No newline at end of file diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 942ec6c..ae9134a 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -1,4 +1,4 @@ -data "lambda_layer_version" "lambda_layer" { +data "aws_lambda_layer_version" "lambda_layer" { layer_name = "clash-bot-workflow-layer" } @@ -40,7 +40,7 @@ module "create_team_lambda" { artifact_path = var.create_team_artifact_path - lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + lambda_layer_arn = "data.lambda_layer_version.lambda_layer.arn" environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id @@ -63,7 +63,7 @@ module "retrieve_team_lambda" { artifact_path = var.retrieve_teams_artifact_path - lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + lambda_layer_arn = "data.lambda_layer_version.lambda_layer.arn" environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id @@ -86,7 +86,7 @@ module "tournament_eligibility_lambda" { artifact_path = var.tournament_eligibility_lambda_artifact_path - lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + lambda_layer_arn = "data.lambda_layer_version.lambda_layer.arn" environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id @@ -109,7 +109,7 @@ module "websocket_publisher_lambda" { artifact_path = var.websocket_publisher_artifact_path - lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + lambda_layer_arn = "data.lambda_layer_version.lambda_layer.arn" environment_variables = { TOPIC_TO_SUBSCRIBERS_TABLE_NAME = module.events_table.dynamodb_table_id, From 1a859fff80bc725346f0ea70dee6ca36435c9e43 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 12 Mar 2024 23:11:04 -0500 Subject: [PATCH 113/119] Fixing data name --- terraform/workflow/event-setup-lambdas.tf | 6 +++--- terraform/workflow/step-functions.lambda.tf | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/terraform/workflow/event-setup-lambdas.tf b/terraform/workflow/event-setup-lambdas.tf index b919edb..6c7e549 100644 --- a/terraform/workflow/event-setup-lambdas.tf +++ b/terraform/workflow/event-setup-lambdas.tf @@ -5,7 +5,7 @@ module "event_handler_lambda" { s3_bucket_name = var.s3_bucket_name environment = var.environment - lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + lambda_layer_arn = data.aws_lambda_layer_version.lambda_layer.arn artifact_path = var.event_handler_artifact_path @@ -40,7 +40,7 @@ module "event_publisher_lambda" { s3_bucket_name = var.s3_bucket_name environment = var.environment - lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + lambda_layer_arn = data.aws_lambda_layer_version.lambda_layer.arn artifact_path = var.event_publisher_artifact_path @@ -63,7 +63,7 @@ module "event_notifier_lambda" { s3_bucket_name = var.s3_bucket_name environment = var.environment - lambda_layer_arn = data.lambda_layer_version.lambda_layer.arn + lambda_layer_arn = data.aws_lambda_layer_version.lambda_layer.arn artifact_path = var.event_notifier_artifact_path diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index ae9134a..6dba32e 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -1,4 +1,4 @@ -data "aws_lambda_layer_version" "lambda_layer" { +data "aws_aws_lambda_layer_version" "lambda_layer" { layer_name = "clash-bot-workflow-layer" } @@ -40,7 +40,7 @@ module "create_team_lambda" { artifact_path = var.create_team_artifact_path - lambda_layer_arn = "data.lambda_layer_version.lambda_layer.arn" + lambda_layer_arn = "data.aws_lambda_layer_version.lambda_layer.arn" environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id @@ -63,7 +63,7 @@ module "retrieve_team_lambda" { artifact_path = var.retrieve_teams_artifact_path - lambda_layer_arn = "data.lambda_layer_version.lambda_layer.arn" + lambda_layer_arn = "data.aws_lambda_layer_version.lambda_layer.arn" environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id @@ -86,7 +86,7 @@ module "tournament_eligibility_lambda" { artifact_path = var.tournament_eligibility_lambda_artifact_path - lambda_layer_arn = "data.lambda_layer_version.lambda_layer.arn" + lambda_layer_arn = "data.aws_lambda_layer_version.lambda_layer.arn" environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id @@ -109,7 +109,7 @@ module "websocket_publisher_lambda" { artifact_path = var.websocket_publisher_artifact_path - lambda_layer_arn = "data.lambda_layer_version.lambda_layer.arn" + lambda_layer_arn = "data.aws_lambda_layer_version.lambda_layer.arn" environment_variables = { TOPIC_TO_SUBSCRIBERS_TABLE_NAME = module.events_table.dynamodb_table_id, From 3a214e6db0e47f462170fbee957b98eb49ee2f6e Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 12 Mar 2024 23:13:35 -0500 Subject: [PATCH 114/119] Fixing data block name --- terraform/workflow/step-functions.lambda.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 6dba32e..4a37c7a 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -1,4 +1,4 @@ -data "aws_aws_lambda_layer_version" "lambda_layer" { +data "aws_lambda_layer_version" "lambda_layer" { layer_name = "clash-bot-workflow-layer" } From 6cb93e9266d330dde9e0cea1b296b4acd3d4dc5b Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 12 Mar 2024 23:16:50 -0500 Subject: [PATCH 115/119] Fixing pay per request issue --- terraform/workflow/dynamodb.tf | 6 ------ 1 file changed, 6 deletions(-) diff --git a/terraform/workflow/dynamodb.tf b/terraform/workflow/dynamodb.tf index de4d895..69d24e4 100644 --- a/terraform/workflow/dynamodb.tf +++ b/terraform/workflow/dynamodb.tf @@ -5,8 +5,6 @@ module "dynamodb_table" { hash_key = "type" range_key = "id" billing_mode = "PAY_PER_REQUEST" - write_capacity = 5 - read_capacity = 1 attributes = [ { @@ -26,8 +24,6 @@ module "events_table" { name = "clash-bot-topics-${var.environment}" hash_key = "topic" billing_mode = "PAY_PER_REQUEST" - write_capacity = 5 - read_capacity = 1 attributes = [ { @@ -43,8 +39,6 @@ module "subscriber_table" { name = "clash-bot-subscriber-${var.environment}" hash_key = "subscriber" billing_mode = "PAY_PER_REQUEST" - write_capacity = 5 - read_capacity = 1 attributes = [ { From 4980be6fd2c46dc5f6abdc50b87b5560761e1340 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 12 Mar 2024 23:19:48 -0500 Subject: [PATCH 116/119] Fixing format --- terraform/workflow/dynamodb.tf | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/terraform/workflow/dynamodb.tf b/terraform/workflow/dynamodb.tf index 69d24e4..dc837aa 100644 --- a/terraform/workflow/dynamodb.tf +++ b/terraform/workflow/dynamodb.tf @@ -1,10 +1,10 @@ module "dynamodb_table" { source = "terraform-aws-modules/dynamodb-table/aws" - name = "clash-bot-workflow-${var.environment}" - hash_key = "type" - range_key = "id" - billing_mode = "PAY_PER_REQUEST" + name = "clash-bot-workflow-${var.environment}" + hash_key = "type" + range_key = "id" + billing_mode = "PAY_PER_REQUEST" attributes = [ { @@ -21,9 +21,9 @@ module "dynamodb_table" { module "events_table" { source = "terraform-aws-modules/dynamodb-table/aws" - name = "clash-bot-topics-${var.environment}" - hash_key = "topic" - billing_mode = "PAY_PER_REQUEST" + name = "clash-bot-topics-${var.environment}" + hash_key = "topic" + billing_mode = "PAY_PER_REQUEST" attributes = [ { @@ -36,9 +36,9 @@ module "events_table" { module "subscriber_table" { source = "terraform-aws-modules/dynamodb-table/aws" - name = "clash-bot-subscriber-${var.environment}" - hash_key = "subscriber" - billing_mode = "PAY_PER_REQUEST" + name = "clash-bot-subscriber-${var.environment}" + hash_key = "subscriber" + billing_mode = "PAY_PER_REQUEST" attributes = [ { From 791ce07074876d467f36cb91235b274b8c846416 Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 12 Mar 2024 23:24:19 -0500 Subject: [PATCH 117/119] Fixing one of the lambda layer arns --- terraform/workflow/step-functions.lambda.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 4a37c7a..6725001 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -40,7 +40,7 @@ module "create_team_lambda" { artifact_path = var.create_team_artifact_path - lambda_layer_arn = "data.aws_lambda_layer_version.lambda_layer.arn" + lambda_layer_arn = data.aws_lambda_layer_version.lambda_layer.arn environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id From 859dcc73d9d5171d1f7a95cf6b5cabaa5f136b5e Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Tue, 12 Mar 2024 23:27:26 -0500 Subject: [PATCH 118/119] Fixing fixing fixing --- terraform/workflow/step-functions.lambda.tf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/terraform/workflow/step-functions.lambda.tf b/terraform/workflow/step-functions.lambda.tf index 6725001..7174f34 100644 --- a/terraform/workflow/step-functions.lambda.tf +++ b/terraform/workflow/step-functions.lambda.tf @@ -63,7 +63,7 @@ module "retrieve_team_lambda" { artifact_path = var.retrieve_teams_artifact_path - lambda_layer_arn = "data.aws_lambda_layer_version.lambda_layer.arn" + lambda_layer_arn = data.aws_lambda_layer_version.lambda_layer.arn environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id @@ -86,7 +86,7 @@ module "tournament_eligibility_lambda" { artifact_path = var.tournament_eligibility_lambda_artifact_path - lambda_layer_arn = "data.aws_lambda_layer_version.lambda_layer.arn" + lambda_layer_arn = data.aws_lambda_layer_version.lambda_layer.arn environment_variables = { TABLE_NAME = module.dynamodb_table.dynamodb_table_id @@ -109,7 +109,7 @@ module "websocket_publisher_lambda" { artifact_path = var.websocket_publisher_artifact_path - lambda_layer_arn = "data.aws_lambda_layer_version.lambda_layer.arn" + lambda_layer_arn = data.aws_lambda_layer_version.lambda_layer.arn environment_variables = { TOPIC_TO_SUBSCRIBERS_TABLE_NAME = module.events_table.dynamodb_table_id, From fa07d1b4fd0ed73a77b12400eeacc0e214a714fa Mon Sep 17 00:00:00 2001 From: Daniel Poss Date: Wed, 13 Mar 2024 18:46:38 -0500 Subject: [PATCH 119/119] Updating lambda layer version --- terraform/prereqs/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/prereqs/main.tf b/terraform/prereqs/main.tf index f4e2707..d80b8b4 100644 --- a/terraform/prereqs/main.tf +++ b/terraform/prereqs/main.tf @@ -33,7 +33,7 @@ module "lambda_bucket" { resource "aws_lambda_layer_version" "lambda_layer" { s3_bucket = module.lambda_bucket.s3_bucket_id - s3_key = "layers/lambda_layer.zip" + s3_key = "layers/lambda_layer_2.zip" layer_name = "clash-bot-workflow-layer" compatible_runtimes = ["nodejs16.x"]