Skip to content

Commit

Permalink
feat(api-testing): Add api service and unit testing to all services (#11
Browse files Browse the repository at this point in the history
)

* test endpoint creation

* add api to serverless-compose file

* add deps for typescript plugin to api

* add posts handler

* add serverless bundle

* add tsconfig.json file

* add getPost endpoint

* add post handler

* add delete handler

* add update post handler

* Init testing

* set foundation for post service

* Add ui test

* attempt 1 at fixing path

* move types

* add axios to react application

* remove cors for now

* fix errors

* fix error

* set cors to false

* enable cors

* Add testing wrapper

* barrel roll

* Update deps

* Add dashboard test

* refactor function handler

* refactor lambdas to use library function

* remove un-needed import

* Add libs file and tests

* Add dashboard body tests

* Update dashboard template tests

* Add error testing

* set outputs on api

* add outputs to ui for api layer

* fix stage

* fix variable resolution

* Move vitest config

* Move vitest config inside src

* change serverless-compose file

* modify deploy local script

* pass api url to axios instance

* Replace ts plugin with bundle in dashboard

* Update tsconfig

* Update api tests

* modify post return

* Refactoring

* Pretty

* Cleanup

* Pretty

* Add Docs for new services

---------

Co-authored-by: 13bfrancis <40218571+13bfrancis@users.noreply.github.com>
  • Loading branch information
benjaminpaige and 13bfrancis authored Mar 1, 2023
1 parent 813847f commit 772ab4f
Show file tree
Hide file tree
Showing 54 changed files with 4,221 additions and 1,653 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
)" | tr -d \'\" >> $GITHUB_OUTPUT
- name: Test
run: run test --stage $STAGE_NAME
run: yarn test-ci

- name: Release
run: npx semantic-release
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ yarn-error.log
.repack
.yarn_install
tsconfig.tsbuildinfo
build_run
build_run
.build
2 changes: 1 addition & 1 deletion docs/docs/developer-guide/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
layout: default
title: Deploy a Stage
parent: Developer Guide
nav_order: 3
nav_order: 2
---

# Deploy a Stage
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/developer-guide/list-running-stages.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
layout: default
title: List Running Stages
parent: Development Workflows
parent: Developer Guide
nav_order: 9
---

Expand Down
46 changes: 46 additions & 0 deletions docs/docs/developer-guide/run-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
layout: default
title: Run Commands
parent: Developer Guide
nav_order: 10
---

# Running Top Level Commands
{: .no_toc }

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

---

## Overview
The `src/run.ts` file defines many helpful commands you can use to perform helpful tasks for your project. This file utilizes Yargs to provide command line interfaces to several commands for managing a serverless project.

This code at `src/run.ts` is implementing a command-line interface (CLI) for your project. The CLI accepts various commands to manage and deploy the project. The CLI tool uses the yargs library to parse the command-line arguments.

The CLI provides the following commands to use like `run [command] [options]`

## Commands

`install` installs all service dependencies.
`ui` configures and starts a local React UI against a remote backend.
`deploy` deploys the project.
`test` runs all available tests.
`test-gui` opens the unit-testing GUI for vitest.
`destroy` destroys a stage in AWS.
`connect` prints a connection string that can be run to 'ssh' directly onto the ECS Fargate task.
`deleteTopics` deletes topics from Bigmac which were created by development/ephemeral branches.
`syncSecurityHubFindings` syncs Security Hub findings to GitHub Issues.
`docs` starts the Jekyll documentation site in a Docker container, available on http://localhost:4000.

## Options
Each command has its own set of options that can be passed in the command line.

For example, if the command deploy is used, it requires the options stage (type string, demanded option), and service (type string, not demanded option). The behavior of the command is defined by an async function, which will run the installation of all service dependencies and will execute the deployment process through the runner.run_command_and_output function with the command SLS Deploy and the options set in the command line.

The same approach is used for all other commands. They all start by installing the dependencies of the services, and then perform specific tasks based on the options passed in the command line.

The docs command starts a Jekyll documentation site in a Docker container. If the stop option is passed as true, it will stop any existing container. Otherwise, it will start a new container and run the documentation site at http://localhost:4000.
28 changes: 28 additions & 0 deletions docs/docs/developer-guide/unit-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
layout: default
title: Unit Testing
parent: Developer Guide
nav_order: 11
---

# Unit Testing
{: .no_toc }

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

---

## Overview
Unit testing is done using the vitest framework.

## Vitest
Vitest is a unit testing framework for testing JavaScript code. It allows you to write tests in a simple and concise manner, and provides tools for running and reporting on the results of those tests.

## Running Tests
Tests can be run using the top level run commands:
- `run test --stage [stage]` - running all tests
- `run test-gui --stage [stage]` - running all tests displaying results in browser ui
58 changes: 58 additions & 0 deletions docs/docs/services/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
layout: default
title: api
parent: Services
nav_order: 6
---

# API
{: .no_toc }

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

## Overview
This stack is used to deploy a RESTful API service to AWS. The service includes a set of functions that can be used to interact with the API, and it is secured with various AWS security policies.

## Service
The service name is ${self:custom.project}-api, where ${self:custom.project} is a parameter provided by the user. This ensures that the service name is unique to the user's project.

## Package
The package section is used to configure how the deployment package for the service is generated. The "individually: true" setting is used to generate separate packages for each function in the service. This makes it easier to deploy and update individual functions without having to deploy the entire service.

## Plugins
The plugins section is used to specify the plugins that will be used during the deployment of the service. The following plugins are used:

serverless-bundle: A plugin that optimizes the packaging and deployment process for serverless applications.
serverless-stack-termination-protection: A plugin that applies CloudFormation termination protection to the specified stages, ensuring that accidental deletions of the service do not occur.
"@stratiformdigital/serverless-iam-helper": A plugin that simplifies the creation and management of AWS IAM roles and policies.
"@stratiformdigital/serverless-s3-security-helper": A plugin that adds security best practices to S3 buckets.

## Provider
The provider section is used to configure the cloud provider (AWS), and any additional settings for the provider. The following settings are used:

- name: aws
-runtime: nodejs18.x
region: ${env:REGION_A} (This setting retrieves the region value from an environment variable called "REGION_A").
stackTags: Specifies tags to be applied to the CloudFormation stack. The tags include PROJECT and SERVICE, which are set to the custom.project and service values, respectively.
iam: Specifies IAM related settings for the CloudFormation stack. The role setting specifies the path and permissions boundary for the IAM role. The statements setting specifies the permissions granted to the role. In this case, the role is granted permission to access all CloudWatch resources.
Custom:
The custom section is used to specify custom settings for the service. The following settings are used:

project: ${env:PROJECT} (This setting retrieves the project value from an environment variable called "PROJECT").
accountId: !Sub "${AWS::AccountId}" (This setting retrieves the account ID for the AWS account in which the stack is deployed).
stage: ${opt:stage, self:provider.stage} (This setting specifies the deployment stage for the service. It is retrieved from an option called "stage", and if the option is not set, it defaults to the value specified in provider.stage).
serverlessTerminationProtection: Specifies the stages to which CloudFormation termination protection will be applied.

## Endpoints
The service is defining an API with five endpoints: getPosts, getPost, createPost, deletePost, and updatePost, which will handle GET, POST, PUT and DELETE requests for /posts and /posts/{id} paths.

## Resources
The resources section is used to specify the additional AWS resources that the service requires. In this case, two gateway responses are created for 4xx and 5xx responses.

## Outputs
The output values include the name and URL of the API Gateway, as well as the AWS region in which the stack is deployed.

33 changes: 33 additions & 0 deletions docs/docs/services/ui-infra.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
layout: default
title: ui-infra
parent: Services
nav_order: 5
---

# UI Infra
{: .no_toc }

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

## Overview
This service provides the appropriate infrastructure for the UI application running on AWS. It creates several resources including an S3 bucket, a bucket policy, a logging bucket, a logging bucket policy, and an IAM role with permissions.


## Components

- AWS IAM role with permissions for CloudWatch logs and an IAM boundary policy.
- Serverless plugins to help with deploying and managing the infrastructure.
- Configuration settings for different stages of the infrastructure, including DNS record, CloudFront domain name, and certificates.
- A set of resources to be created, including S3 buckets for hosting the UI, logging, and their policies.

## Resources

- An S3 bucket with server-side encryption and the ability to serve static web content.
- A bucket policy that allows access to the bucket from an AWS CloudFront distribution using an Origin Access Identity (OAI).
- An S3 bucket for CloudFront access logs with server-side encryption and an access policy that allows AWS root account to write logs.
- A conditional statement for DNS record creation and a conditional statement for CloudFront distribution creation.
37 changes: 37 additions & 0 deletions docs/docs/services/ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
layout: default
title: ui
parent: Services
nav_order: 4
---

# UI
{: .no_toc }

## Table of contents
{: .no_toc .text-delta }

- TOC
{:toc}

## Overview
This service deploys a static web application to an S3 bucket with a CloudFront distribution in front of it for CDN caching and performance optimization. The template uses the serverless framework and includes several plugins to help with deployment and configuration.

## Configuration

The custom section defines some custom variables, including the project name, stage, region, and CloudFormation termination protection for specific stages. The s3Sync section defines the S3 bucket to which the files will be synced, the local directory where the files will be found, and whether to delete removed files.

The cloudfrontInvalidate section invalidates the CloudFront distribution cache by specifying the distribution ID and the items to invalidate. The scripts section defines a script to set environment variables during deployment, which are used to specify the API region and URL.

The provider section configures the runtime environment for the Lambda functions, the AWS region, and stack tags. It does not include any IAM configuration since no Lambda functions are defined.

This template is mainly focused on deploying the static web application to S3 and configuring the CloudFront distribution to serve the content. The environment variables set in the scripts section are used by the application to connect to the backend API.

## Scripts
There are three npm scripts that are defined in the package.json file of a project. These scripts are used to automate certain development tasks related to the project.

1. `dev`: This script runs the Vite development server. Vite is a build tool that enables fast development by providing a development server that reloads the browser quickly whenever changes are made to the code. When the dev script is run, Vite starts the development server and serves the project files on a local web server. The output of this script will typically be a URL that can be opened in a web browser to access the development server.

1. `build`: This script builds the project for production. This script first runs the TypeScript compiler (tsc) to compile the TypeScript code to JavaScript. After that, the Vite build tool is run to bundle the code and assets for production. The output of this script will typically be a set of static files that can be deployed to a web server.

1. `preview`: This script starts a Vite server that serves the production build of the project on a local web server. This is useful for testing the production build locally before deploying it to a web server. When this script is run, Vite starts the production server and serves the project files on a local web server. The output of this script will typically be a URL that can be opened in a web browser to access the production server.
15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"test": "tests"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "vitest --config ./src/vitest.config.ts",
"test-ci": "vitest run --config ./src/vitest.config.ts",
"test-gui": "vitest --ui"
},
"repository": {
"type": "git",
Expand All @@ -27,16 +29,23 @@
"@stratiformdigital/serverless-online": "^3.1.0",
"@stratiformdigital/serverless-s3-security-helper": "^4.0.0",
"@stratiformdigital/serverless-stage-destroyer": "^2.0.0",
"@testing-library/react": "^14.0.0",
"@vitejs/plugin-react": "^3.1.0",
"@vitest/ui": "^0.26.2",
"aws-sdk-client-mock": "^2.0.1",
"prettier": "2.7.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"semantic-release": "^19.0.5",
"serverless": "^3.17.0",
"serverless-bundle": "^6.0.0",
"serverless-disable-functions": "^1.0.0",
"serverless-plugin-scripts": "^1.0.2",
"serverless-plugin-typescript": "^2.1.4",
"serverless-plugin-warmup": "^7.1.0",
"serverless-stack-termination-protection": "^2.0.2"
"serverless-stack-termination-protection": "^2.0.2",
"vitest": "^0.26.2"
},
"dependencies": {},
"release": {
"branches": [
"production"
Expand Down
5 changes: 4 additions & 1 deletion serverless-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ services:
path: src/services/alerts
dashboard:
path: src/services/dashboard
api:
path: src/services/api
ui-infra:
path: src/services/ui-infra
ui:
path: src/services/ui
dependsOn: ui-infra
params:
CloudfrontDistributionId: ${ui-infra.CloudFrontDistributionId}
S3BucketName: ${ui-infra.S3BucketName}
ApplicationEndpointUrl: ${ui-infra.ApplicationEndpointUrl}
ApiRegion: ${api.Region}
ApiUrl: ${api.ApiGatewayRestApiUrl}
# params: # params will be added once other services are
# ecsFailureTopicArn: ${alerts.ECSFailureTopicArn}
# mskClusterArn: ${bigmac.MskClusterArn}
Expand Down
8 changes: 8 additions & 0 deletions src/libs/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function checkEnvVars(requiredVars: string[]) {
const missingVars = requiredVars.filter((v) => !process.env[v]);
if (missingVars.length > 0) {
throw new Error(
`Missing required environment variables: ${missingVars.join(", ")}`
);
}
}
1 change: 1 addition & 0 deletions src/libs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./env";
28 changes: 28 additions & 0 deletions src/libs/tests/env.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { checkEnvVars } from "..";
import { it, describe, expect, afterEach } from "vitest";

describe("checkEnvVars", () => {
afterEach(() => {
delete process.env.FOO;
delete process.env.BAZ;
});

it("should not throw an error when all required environment variables are present", () => {
process.env.FOO = "bar";
process.env.BAZ = "qux";
expect(() => checkEnvVars(["FOO", "BAZ"])).not.toThrow();
});

it("should throw an error when a required environment variable is missing", () => {
process.env.FOO = "bar";
expect(() => checkEnvVars(["FOO", "BAZ"])).toThrowError(
"Missing required environment variables: BAZ"
);
});

it("should throw an error when multiple required environment variables are missing", () => {
expect(() => checkEnvVars(["FOO", "BAZ"])).toThrowError(
"Missing required environment variables: FOO, BAZ"
);
});
});
Loading

0 comments on commit 772ab4f

Please sign in to comment.