From a9cd80f3a0b221e548811b28ba3194ccfca010b0 Mon Sep 17 00:00:00 2001 From: Shane Earley Date: Tue, 15 Aug 2023 12:33:32 -0400 Subject: [PATCH] Add docs build or publish to workflows --- .github/workflows/pull_request.yml | 6 + .github/workflows/push.yml | 6 + .github/workflows/release.yml | 6 + .github/workflows/sandbox.yml | 6 + common/data/notebooks/dkg.ipynb | 63 ++++++++ common/data/notebooks/ethereum.ipynb | 66 +++++---- contracts/ethereum/foundry.toml | 1 + contracts/ethereum/package.json | 4 +- contracts/ethereum/scripts/doc.ts | 19 --- contracts/ethereum/scripts/docs.ts | 40 +++++ infrastructure/cdk/src/index.ts | 9 +- .../cdk/src/interfaces/ProjectConfig.ts | 8 +- .../cdk/src/interfaces/StackProps.ts | 20 +-- infrastructure/cdk/src/providers/analytics.ts | 8 +- infrastructure/cdk/src/providers/config.ts | 2 +- infrastructure/cdk/src/providers/dns.ts | 6 - infrastructure/cdk/src/providers/docs.ts | 83 +++++++++++ infrastructure/cdk/src/providers/landing.ts | 137 ++++++++---------- infrastructure/cdk/src/providers/network.ts | 23 ++- infrastructure/cdk/src/providers/nodes.ts | 5 +- infrastructure/cdk/src/providers/users.ts | 21 +-- infrastructure/cdk/src/providers/web.ts | 124 ++++++++-------- infrastructure/cdk/test/all.test.ts | 80 +++++----- infrastructure/cdk/test/analytics.test.ts | 59 ++++---- scripts/cdk/deploy.ts | 13 +- scripts/cdk/test.ts | 12 +- 26 files changed, 496 insertions(+), 331 deletions(-) create mode 100644 common/data/notebooks/dkg.ipynb delete mode 100644 contracts/ethereum/scripts/doc.ts create mode 100644 contracts/ethereum/scripts/docs.ts create mode 100644 infrastructure/cdk/src/providers/docs.ts diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 5805d6067..0339fd2c0 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -29,6 +29,12 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + - name: Install mdBook KaTeX and Mermaid plugins + uses: actions-rs/cargo@v1 + with: + command: install + args: mdbook-katex mdbook-mermaid + - name: Install and build all package dependencies run: npm ci env: diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index faa13dffd..569105a59 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -36,6 +36,12 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + + - name: Install mdBook KaTeX and Mermaid plugins + uses: actions-rs/cargo@v1 + with: + command: install + args: mdbook-katex mdbook-mermaid - name: Install and build all package dependencies run: npm ci diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dfb16ed2d..9a1dd8777 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,12 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + - name: Install mdBook KaTeX and Mermaid plugins + uses: actions-rs/cargo@v1 + with: + command: install + args: mdbook-katex mdbook-mermaid + - name: Install and build all package dependencies run: npm ci env: diff --git a/.github/workflows/sandbox.yml b/.github/workflows/sandbox.yml index 74973904f..821ee5d0e 100644 --- a/.github/workflows/sandbox.yml +++ b/.github/workflows/sandbox.yml @@ -40,6 +40,12 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + - name: Install mdBook KaTeX and Mermaid plugins + uses: actions-rs/cargo@v1 + with: + command: install + args: mdbook-katex mdbook-mermaid + - name: Install and build all package dependencies run: npm ci env: diff --git a/common/data/notebooks/dkg.ipynb b/common/data/notebooks/dkg.ipynb new file mode 100644 index 000000000..9302ba1f5 --- /dev/null +++ b/common/data/notebooks/dkg.ipynb @@ -0,0 +1,63 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Check DKG " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Casimir Goerli DKG messenger is up and healthy\n" + ] + } + ], + "source": [ + "import requests\n", + "\n", + "url = f\"https://nodes.casimir.co/eth/goerli/dkg/messenger/ping\"\n", + "\n", + "response = requests.get(url)\n", + "\n", + "if response.status_code != 200:\n", + " print(f\"Casimir Goerli DKG messenger server is not responding\")\n", + "else: \n", + " data = response.json()\n", + " if data['message'] == \"pong\":\n", + " print(f\"Casimir Goerli DKG messenger is responding and healthy\")\n", + " else:\n", + " print(f\"Casimir Goerli DKG messenger is responding and not healthy\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "casimir-data", + "language": "python", + "name": "casimir-data" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.17" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/common/data/notebooks/ethereum.ipynb b/common/data/notebooks/ethereum.ipynb index 31366757f..0da9e1145 100644 --- a/common/data/notebooks/ethereum.ipynb +++ b/common/data/notebooks/ethereum.ipynb @@ -4,20 +4,20 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Check Casimir RPC nodes\n", - "\n", - "Edit `NETWORK` to select the network to check. " + "# Check Ethereum" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "Casimir mainnet node is fully synced\n", + "Casimir goerli node is fully synced\n", "Casimir hardhat node is fully synced\n" ] } @@ -28,35 +28,47 @@ "import requests\n", "from requests.auth import HTTPBasicAuth\n", "\n", - "NETWORK = \"hardhat\"\n", + "networks = {\n", + " \"mainnet\": {\n", + " \"requires_auth\": True\n", + " },\n", + " \"goerli\": {\n", + " \"requires_auth\": True\n", + " },\n", + " \"hardhat\": {\n", + " \"requires_auth\": False\n", + " }\n", + "}\n", "\n", - "if NETWORK not in [\"mainnet\", \"goerli\", \"hardhat\"]:\n", - " raise ValueError(f\"Invalid network: {NETWORK}\")\n", + "for network, requires_auth in networks.items():\n", "\n", - "url = f\"https://nodes.casimir.co/eth/{NETWORK}\"\n", + " url = f\"https://nodes.casimir.co/eth/{network}\"\n", "\n", - "headers = {'content-type': 'application/json'}\n", - "payload = {\n", - " \"method\": \"eth_syncing\",\n", - " \"params\": [],\n", - " \"jsonrpc\": \"2.0\",\n", - " \"id\": 0,\n", - "}\n", - "auth = None\n", + " headers = {'content-type': 'application/json'}\n", + " payload = {\n", + " \"method\": \"eth_syncing\",\n", + " \"params\": [],\n", + " \"jsonrpc\": \"2.0\",\n", + " \"id\": 0,\n", + " }\n", "\n", - "if NETWORK in [\"mainnet\", \"goerli\"]:\n", - " client = boto3.client('secretsmanager')\n", - " response = client.get_secret_value(SecretId='casimir-rpc-credentials')\n", - " secret = json.loads(response['SecretString'])\n", - " auth = HTTPBasicAuth(secret['username'], secret['password'])\n", + " auth = None\n", + " if requires_auth:\n", + " client = boto3.client('secretsmanager')\n", + " response = client.get_secret_value(SecretId='casimir-rpc-credentials')\n", + " secret = json.loads(response['SecretString'])\n", + " auth = HTTPBasicAuth(secret['username'], secret['password'])\n", "\n", - "response = requests.post(url, data=json.dumps(payload), headers=headers, auth=auth)\n", - "data = response.json()\n", + " response = requests.post(url, data=json.dumps(payload), headers=headers, auth=auth)\n", "\n", - "if data['result'] == False:\n", - " print(f\"Casimir {NETWORK} node is fully synced\")\n", - "else:\n", - " print(f\"Casimir {NETWORK} node is syncing. Current block: {data['result']['currentBlock']}, Highest block: {data['result']['highestBlock']}\")\n", + " if response.status_code != 200:\n", + " print(f\"Casimir {network} node is not responding\")\n", + " else: \n", + " data = response.json()\n", + " if data['result'] == False:\n", + " print(f\"Casimir {network} node is fully synced\")\n", + " else:\n", + " print(f\"Casimir {network} node is syncing. Current block: {data['result']['currentBlock']}, Highest block: {data['result']['highestBlock']}\")\n", "\n" ] } diff --git a/contracts/ethereum/foundry.toml b/contracts/ethereum/foundry.toml index 5468012f2..04a6b7a58 100644 --- a/contracts/ethereum/foundry.toml +++ b/contracts/ethereum/foundry.toml @@ -6,4 +6,5 @@ test = 'test' cache_path = 'build/foundry/cache' [doc] +title = 'Casimir Ethereum Docs' out = 'build/foundry/docs' \ No newline at end of file diff --git a/contracts/ethereum/package.json b/contracts/ethereum/package.json index 061f65b89..4566610f9 100644 --- a/contracts/ethereum/package.json +++ b/contracts/ethereum/package.json @@ -2,11 +2,11 @@ "name": "@casimir/ethereum", "scripts": { "build": "npx hardhat compile", + "build:docs": "npx esno scripts/docs.ts", "clean": "npx rimraf ./build", "deploy": "npx hardhat run scripts/deploy.ts", "dev": "npx hardhat run scripts/dev.ts", - "doc": "npx esno scripts/doc.ts", - "doc:serve": "SERVE=true npx esno scripts/doc.ts", + "dev:docs": "DEV=true npx esno scripts/docs.ts", "node": "npx hardhat node", "postinstall": "npx esno scripts/foundryup.ts", "test": "npx esno scripts/test.ts" diff --git a/contracts/ethereum/scripts/doc.ts b/contracts/ethereum/scripts/doc.ts deleted file mode 100644 index c1902e6df..000000000 --- a/contracts/ethereum/scripts/doc.ts +++ /dev/null @@ -1,19 +0,0 @@ -import fs from 'fs' -import { run } from '@casimir/helpers' - -void async function () { - const config = await run('forge config') as string - const outDir = config.match(/\[doc\]\nout = "(.*)"/)?.[1] as string - if (!fs.existsSync(outDir)) { - fs.mkdirSync(outDir, { recursive: true }) - } - const publicDir = fs.readdirSync('public') - for (const file of publicDir) { - fs.copyFileSync(`public/${file}`, `${outDir}/${file}`) - } - let command = 'forge doc' - if (process.env.SERVE === 'true') { - command += ' --serve --port=5458' - } - await run(command) -}() \ No newline at end of file diff --git a/contracts/ethereum/scripts/docs.ts b/contracts/ethereum/scripts/docs.ts new file mode 100644 index 000000000..4abe58e34 --- /dev/null +++ b/contracts/ethereum/scripts/docs.ts @@ -0,0 +1,40 @@ +import fs from 'fs' +import { run } from '@casimir/helpers' + +void async function () { + const cargo = await run('which cargo') as string + if (!cargo || cargo.includes('not found')) { + throw new Error('🚩 Please install rust (see https://www.rust-lang.org/tools/install)') + } + + const mdbookKatex = await run('cargo install --list | grep mdbook-katex') as string + if (!mdbookKatex) { + await run('cargo install mdbook-katex') + } + + const mdbookMermaid = await run('cargo install --list | grep mdbook-mermaid') as string + if (!mdbookMermaid) { + await run('cargo install mdbook-mermaid') + } + + const config = await run('forge config') as string + + const outDir = config.match(/\[doc\]\nout = "(.*)"/)?.[1] as string + if (!fs.existsSync(outDir)) { + fs.mkdirSync(outDir, { recursive: true }) + } + + const publicDir = fs.readdirSync('public') + for (const file of publicDir) { + fs.copyFileSync(`public/${file}`, `${outDir}/${file}`) + } + + let command = 'forge doc' + if (process.env.DEV === 'true') { + command += ' --serve --port=5458' + } else { + command += ' --build' + } + + await run(command) +}() \ No newline at end of file diff --git a/infrastructure/cdk/src/index.ts b/infrastructure/cdk/src/index.ts index 9951929cc..b689233db 100755 --- a/infrastructure/cdk/src/index.ts +++ b/infrastructure/cdk/src/index.ts @@ -7,21 +7,22 @@ import { LandingStack } from './providers/landing' import { NodesStack } from './providers/nodes' import { DnsStack } from './providers/dns' import { WebStack } from './providers/web' +import { DocsStack } from './providers/docs' -/** Create CDK app and stacks */ const config = new Config() const { env, stage } = config const app = new cdk.App() + const { hostedZone, certificate } = new DnsStack(app, config.getFullStackName('dns'), { env }) const { vpc } = new NetworkStack(app, config.getFullStackName('network'), { env }) + if (stage !== 'prod') { - /** Create development-only stacks */ new AnalyticsStack(app, config.getFullStackName('analytics'), { env }) new UsersStack(app, config.getFullStackName('users'), { env, certificate, hostedZone, vpc }) new WebStack(app, config.getFullStackName('web'), { env, certificate, hostedZone }) } else { - /** Create production-only stacks */ + new DocsStack(app, config.getFullStackName('docs'), { env, certificate, hostedZone }) new NodesStack(app, config.getFullStackName('nodes'), { env, hostedZone }) } -/** Create remaining stacks */ + new LandingStack(app, config.getFullStackName('landing'), { env, certificate, hostedZone }) \ No newline at end of file diff --git a/infrastructure/cdk/src/interfaces/ProjectConfig.ts b/infrastructure/cdk/src/interfaces/ProjectConfig.ts index fcaac0740..94181433e 100644 --- a/infrastructure/cdk/src/interfaces/ProjectConfig.ts +++ b/infrastructure/cdk/src/interfaces/ProjectConfig.ts @@ -1,19 +1,13 @@ export interface ProjectConfig { - /** Deployment AWS env */ env: { - /** AWS account number */ account: string - /** AWS region */ region: string } - /** Project name */ project: string - /** Stage name */ stage: string - /** Stage-specific root domain (i.e., casimir.co for prod, dev.casimir.co for dev) */ rootDomain: string - /** Stage-specific subdomains (i.e., api.casimir.co for prod, api.dev.casimir.co for dev) */ subdomains: { + docsEthereum: string landing: string nodes: string users: string diff --git a/infrastructure/cdk/src/interfaces/StackProps.ts b/infrastructure/cdk/src/interfaces/StackProps.ts index 29a28be71..7579a056b 100644 --- a/infrastructure/cdk/src/interfaces/StackProps.ts +++ b/infrastructure/cdk/src/interfaces/StackProps.ts @@ -1,37 +1,33 @@ import * as cdk from 'aws-cdk-lib' import * as certmgr from 'aws-cdk-lib/aws-certificatemanager' import * as ec2 from 'aws-cdk-lib/aws-ec2' -import * as ecs from 'aws-cdk-lib/aws-ecs' import * as route53 from 'aws-cdk-lib/aws-route53' export type DnsStackProps = cdk.StackProps export type AnalyticsStackProps = cdk.StackProps export type NetworkStackProps = cdk.StackProps +export interface DocsStackProps extends cdk.StackProps { + certificate: certmgr.Certificate + hostedZone: route53.HostedZone +} + export interface LandingStackProps extends cdk.StackProps { - /** Stage-specific certificate */ - certificate?: certmgr.Certificate - /** Project-wide route53 hosted zone */ + certificate: certmgr.Certificate hostedZone: route53.HostedZone } export interface WebStackProps extends cdk.StackProps { - /** Stage-specific certificate */ - certificate?: certmgr.Certificate - /** Project-wide route53 hosted zone */ + certificate: certmgr.Certificate hostedZone: route53.HostedZone } export interface NodesStackProps extends cdk.StackProps { - /** Project-wide route53 hosted zone */ hostedZone: route53.HostedZone } export interface UsersStackProps extends cdk.StackProps { - /** Stage-specific certificate */ - certificate?: certmgr.Certificate - /** Project-wide route53 hosted zone */ + certificate: certmgr.Certificate hostedZone: route53.HostedZone - /** Stage-specific VPC */ vpc: ec2.Vpc } \ No newline at end of file diff --git a/infrastructure/cdk/src/providers/analytics.ts b/infrastructure/cdk/src/providers/analytics.ts index f8bcc9a95..a3083f8e3 100644 --- a/infrastructure/cdk/src/providers/analytics.ts +++ b/infrastructure/cdk/src/providers/analytics.ts @@ -8,10 +8,9 @@ import { Config } from './config' import { AnalyticsStackProps } from '../interfaces/StackProps' /** - * Chain analytics stack + * Data analytics stack */ export class AnalyticsStack extends cdk.Stack { - /** Stack name */ public readonly name = pascalCase('analytics') constructor(scope: Construct, id: string, props: AnalyticsStackProps) { @@ -19,16 +18,13 @@ export class AnalyticsStack extends cdk.Stack { const config = new Config() - /** Get Glue Columns from JSON Schema for each table */ const eventColumns = new Schema(eventSchema).getGlueColumns() const actionColumns = new Schema(actionSchema).getGlueColumns() - /** Create Glue DB */ const database = new glue.Database(this, config.getFullStackResourceName(this.name, 'database'), { databaseName: snakeCase(config.getFullStackResourceName(this.name, 'database')) }) - /** Create S3 buckets */ const eventBucket = new s3.Bucket(this, config.getFullStackResourceName(this.name, 'event-bucket', config.dataVersion), { bucketName: kebabCase(config.getFullStackResourceName(this.name, 'event-bucket', config.dataVersion)) }) @@ -41,7 +37,6 @@ export class AnalyticsStack extends cdk.Stack { bucketName: kebabCase(config.getFullStackResourceName(this.name, 'output-bucket', config.dataVersion)) }) - /** Create Glue tables */ new glue.Table(this, config.getFullStackResourceName(this.name, 'event-table', config.dataVersion), { database: database, tableName: snakeCase(config.getFullStackResourceName(this.name, 'event-table', config.dataVersion)), @@ -49,6 +44,7 @@ export class AnalyticsStack extends cdk.Stack { columns: eventColumns, dataFormat: glue.DataFormat.JSON, }) + new glue.Table(this, config.getFullStackResourceName(this.name, 'action-table', config.dataVersion), { database: database, tableName: snakeCase(config.getFullStackResourceName(this.name, 'action-table', config.dataVersion)), diff --git a/infrastructure/cdk/src/providers/config.ts b/infrastructure/cdk/src/providers/config.ts index 32028ba85..7809fb889 100644 --- a/infrastructure/cdk/src/providers/config.ts +++ b/infrastructure/cdk/src/providers/config.ts @@ -13,7 +13,6 @@ export class Config implements ProjectConfig { public readonly subdomains public readonly dataVersion - /** List of required environment variables */ public readonly requiredEnvVars = ['PROJECT', 'STAGE', 'AWS_ACCOUNT', 'AWS_REGION'] constructor() { @@ -26,6 +25,7 @@ export class Config implements ProjectConfig { } this.rootDomain = `${this.stage === 'prod' ? '' : `${this.stage}.`}casimir.co` this.subdomains = { + docsEthereum: 'docs.ethereum', nodes: 'nodes', landing: 'www', users: 'users', diff --git a/infrastructure/cdk/src/providers/dns.ts b/infrastructure/cdk/src/providers/dns.ts index b3c641881..f8fe63506 100644 --- a/infrastructure/cdk/src/providers/dns.ts +++ b/infrastructure/cdk/src/providers/dns.ts @@ -10,11 +10,8 @@ import { Config } from './config' * Route53 dns stack */ export class DnsStack extends cdk.Stack { - /** Stack name */ public readonly name = pascalCase('dns') - /** Project-wide route53 hosted zone */ public readonly hostedZone: route53.HostedZone - /** Stage-specific certificate */ public readonly certificate: certmgr.Certificate constructor(scope: Construct, id: string, props: DnsStackProps) { @@ -23,7 +20,6 @@ export class DnsStack extends cdk.Stack { const config = new Config() const { rootDomain, subdomains } = config - /** Root domain with stage-specific subdomain removed */ const absoluteRootDomain = (() => { if (rootDomain.split('.').length > 2) { return rootDomain.split('.').slice(1).join('.') @@ -31,12 +27,10 @@ export class DnsStack extends cdk.Stack { return rootDomain })() - /** Get the hosted zone for the project domain */ this.hostedZone = route53.HostedZone.fromLookup(this, config.getFullStackResourceName(this.name, 'hosted-zone'), { domainName: absoluteRootDomain }) as route53.HostedZone - /** Create a stage-specific SSL certificate */ this.certificate = new certmgr.Certificate(this, config.getFullStackResourceName(this.name, 'cert'), { domainName: rootDomain, subjectAlternativeNames: [`${subdomains.wildcard}.${rootDomain}`], diff --git a/infrastructure/cdk/src/providers/docs.ts b/infrastructure/cdk/src/providers/docs.ts new file mode 100644 index 000000000..39ec34c1e --- /dev/null +++ b/infrastructure/cdk/src/providers/docs.ts @@ -0,0 +1,83 @@ +import { Construct } from 'constructs' +import * as cdk from 'aws-cdk-lib' +import * as certmgr from 'aws-cdk-lib/aws-certificatemanager' +import * as route53targets from 'aws-cdk-lib/aws-route53-targets' +import * as route53 from 'aws-cdk-lib/aws-route53' +import * as s3 from 'aws-cdk-lib/aws-s3' +import * as s3Deployment from 'aws-cdk-lib/aws-s3-deployment' +import * as cloudfront from 'aws-cdk-lib/aws-cloudfront' +import * as cloudfrontOrigins from 'aws-cdk-lib/aws-cloudfront-origins' +import { DocsStackProps } from '../interfaces/StackProps' +import { pascalCase } from '@casimir/helpers' +import { Config } from './config' + +/** + * Documentation sites stack + */ +export class DocsStack extends cdk.Stack { + public readonly name = pascalCase('docs') + public readonly assetPath = '../../contracts/ethereum/build/foundry/docs/book' + + constructor(scope: Construct, id: string, props: DocsStackProps) { + super(scope, id, props) + + const config = new Config() + const { rootDomain, subdomains } = config + const { hostedZone, certificate } = props + + const bucket = new s3.Bucket(this, config.getFullStackResourceName(this.name, 'bucket'), { + accessControl: s3.BucketAccessControl.PRIVATE + }) + const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, config.getFullStackResourceName(this.name, 'origin-access-identity')) + bucket.grantRead(originAccessIdentity) + + const distributionCertificate = (() => { + if (certificate?.env.region === 'us-east-1') return certificate + /** Replace deprecated method when cross-region support is out of beta */ + return new certmgr.DnsValidatedCertificate(this, config.getFullStackResourceName(this.name, 'cert'), { + domainName: rootDomain, + subjectAlternativeNames: [`${subdomains.docsEthereum}.${rootDomain}`], + hostedZone, + region: 'us-east-1' + }) + })() + + const distribution = new cloudfront.Distribution(this, config.getFullStackResourceName(this.name, 'distribution'), { + certificate: distributionCertificate, + defaultRootObject: 'index.html', + errorResponses: [ + { + httpStatus: 403, + responseHttpStatus: 200, + responsePagePath: '/index.html', + ttl: cdk.Duration.minutes(30) + }, + { + httpStatus: 404, + responseHttpStatus: 200, + responsePagePath: '/index.html', + ttl: cdk.Duration.minutes(30) + } + ], + defaultBehavior: { + viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + origin: new cloudfrontOrigins.S3Origin(bucket, { originAccessIdentity }) + }, + domainNames: [`${subdomains.docsEthereum}.${rootDomain}`] + }) + + new s3Deployment.BucketDeployment(this, config.getFullStackResourceName(this.name, 'bucket-deployment'), { + destinationBucket: bucket, + sources: [s3Deployment.Source.asset(this.assetPath)], + distribution, + distributionPaths: ['/*'] + }) + + new route53.ARecord(this, config.getFullStackResourceName(this.name, 'a-record-docs'), { + recordName: `${subdomains.docsEthereum}.${rootDomain}`, + zone: hostedZone as route53.IHostedZone, + target: route53.RecordTarget.fromAlias(new route53targets.CloudFrontTarget(distribution)), + ttl: cdk.Duration.minutes(1), + }) + } +} \ No newline at end of file diff --git a/infrastructure/cdk/src/providers/landing.ts b/infrastructure/cdk/src/providers/landing.ts index 0be3b4c3c..4db887fad 100644 --- a/infrastructure/cdk/src/providers/landing.ts +++ b/infrastructure/cdk/src/providers/landing.ts @@ -15,86 +15,77 @@ import { Config } from './config' * Landing page stack */ export class LandingStack extends cdk.Stack { - /** Stack name */ - public readonly name = pascalCase('landing') - /** Path to stack build assets or Dockerfile */ - public readonly assetPath = '../../apps/landing/dist' + public readonly name = pascalCase('landing') + public readonly assetPath = '../../apps/landing/dist' - constructor(scope: Construct, id: string, props: LandingStackProps) { - super(scope, id, props) + constructor(scope: Construct, id: string, props: LandingStackProps) { + super(scope, id, props) - const config = new Config() - const { rootDomain, subdomains } = config - const { hostedZone, certificate } = props + const config = new Config() + const { rootDomain, subdomains } = config + const { hostedZone, certificate } = props - /** Create a bucket for the landing page */ - const bucket = new s3.Bucket(this, config.getFullStackResourceName(this.name, 'bucket'), { - accessControl: s3.BucketAccessControl.PRIVATE - }) + const bucket = new s3.Bucket(this, config.getFullStackResourceName(this.name, 'bucket'), { + accessControl: s3.BucketAccessControl.PRIVATE + }) - /** Set bucket to allow cloudfront origin */ - const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, config.getFullStackResourceName(this.name, 'origin-access-identity')) - bucket.grantRead(originAccessIdentity) + const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, config.getFullStackResourceName(this.name, 'origin-access-identity')) + bucket.grantRead(originAccessIdentity) - /** Create a requisite us-east-1 certificate for distribution if needed */ - const distributionCertificate = (() => { - if (certificate?.env.region === 'us-east-1') return certificate - /** Replace deprecated method when cross-region support is out of beta */ - return new certmgr.DnsValidatedCertificate(this, config.getFullStackResourceName(this.name, 'cert'), { - domainName: rootDomain, - subjectAlternativeNames: [`${subdomains.landing}.${rootDomain}`], - hostedZone, - region: 'us-east-1' - }) - })() + const distributionCertificate = (() => { + if (certificate?.env.region === 'us-east-1') return certificate + /** Replace deprecated method when cross-region support is out of beta */ + return new certmgr.DnsValidatedCertificate(this, config.getFullStackResourceName(this.name, 'cert'), { + domainName: rootDomain, + subjectAlternativeNames: [`${subdomains.landing}.${rootDomain}`], + hostedZone, + region: 'us-east-1' + }) + })() - /** Create a cloudfront distribution for the landing page */ - const distribution = new cloudfront.Distribution(this, config.getFullStackResourceName(this.name, 'distribution'), { - certificate: distributionCertificate, - defaultRootObject: 'index.html', - errorResponses: [ - { - httpStatus: 403, - responseHttpStatus: 200, - responsePagePath: '/index.html', - ttl: cdk.Duration.minutes(30) - }, - { - httpStatus: 404, - responseHttpStatus: 200, - responsePagePath: '/index.html', - ttl: cdk.Duration.minutes(30) - } - ], - defaultBehavior: { - viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, - origin: new cloudfrontOrigins.S3Origin(bucket, { originAccessIdentity }) - }, - domainNames: [rootDomain, `${subdomains.landing}.${rootDomain}`] - }) + const distribution = new cloudfront.Distribution(this, config.getFullStackResourceName(this.name, 'distribution'), { + certificate: distributionCertificate, + defaultRootObject: 'index.html', + errorResponses: [ + { + httpStatus: 403, + responseHttpStatus: 200, + responsePagePath: '/index.html', + ttl: cdk.Duration.minutes(30) + }, + { + httpStatus: 404, + responseHttpStatus: 200, + responsePagePath: '/index.html', + ttl: cdk.Duration.minutes(30) + } + ], + defaultBehavior: { + viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + origin: new cloudfrontOrigins.S3Origin(bucket, { originAccessIdentity }) + }, + domainNames: [rootDomain, `${subdomains.landing}.${rootDomain}`] + }) - /** Deploy the landing page to the bucket */ - new s3Deployment.BucketDeployment(this, config.getFullStackResourceName(this.name, 'bucket-deployment'), { - destinationBucket: bucket, - sources: [s3Deployment.Source.asset(this.assetPath)], - distribution, - distributionPaths: ['/*'] - }) + new s3Deployment.BucketDeployment(this, config.getFullStackResourceName(this.name, 'bucket-deployment'), { + destinationBucket: bucket, + sources: [s3Deployment.Source.asset(this.assetPath)], + distribution, + distributionPaths: ['/*'] + }) - /** Create an A record for the landing page root */ - new route53.ARecord(this, config.getFullStackResourceName(this.name, 'a-record'), { - recordName: rootDomain, - zone: hostedZone as route53.IHostedZone, - target: route53.RecordTarget.fromAlias(new route53targets.CloudFrontTarget(distribution)), - ttl: cdk.Duration.minutes(1), - }) + new route53.ARecord(this, config.getFullStackResourceName(this.name, 'a-record'), { + recordName: rootDomain, + zone: hostedZone as route53.IHostedZone, + target: route53.RecordTarget.fromAlias(new route53targets.CloudFrontTarget(distribution)), + ttl: cdk.Duration.minutes(1), + }) - /** Create an A record for the landing page www subdomain */ - new route53.ARecord(this, config.getFullStackResourceName(this.name, 'a-record-www'), { - recordName: `${subdomains.landing}.${rootDomain}`, - zone: hostedZone as route53.IHostedZone, - target: route53.RecordTarget.fromAlias(new route53targets.CloudFrontTarget(distribution)), - ttl: cdk.Duration.minutes(1), - }) - } + new route53.ARecord(this, config.getFullStackResourceName(this.name, 'a-record-www'), { + recordName: `${subdomains.landing}.${rootDomain}`, + zone: hostedZone as route53.IHostedZone, + target: route53.RecordTarget.fromAlias(new route53targets.CloudFrontTarget(distribution)), + ttl: cdk.Duration.minutes(1), + }) + } } diff --git a/infrastructure/cdk/src/providers/network.ts b/infrastructure/cdk/src/providers/network.ts index f78772043..4288922d0 100644 --- a/infrastructure/cdk/src/providers/network.ts +++ b/infrastructure/cdk/src/providers/network.ts @@ -7,22 +7,19 @@ import { pascalCase } from '@casimir/helpers' import { Config } from './config' /** - * Route53 network stack + * VPC network stack */ export class NetworkStack extends cdk.Stack { - /** Stack name */ - public readonly name = pascalCase('network') - /** Stage-specific Ec2 VPC */ - public readonly vpc: ec2.Vpc + public readonly name = pascalCase('network') + public readonly vpc: ec2.Vpc - constructor(scope: Construct, id: string, props: NetworkStackProps) { - super(scope, id, props) + constructor(scope: Construct, id: string, props: NetworkStackProps) { + super(scope, id, props) - const config = new Config() + const config = new Config() - /** Create a stage-specific VPC */ - this.vpc = new ec2.Vpc(this, config.getFullStackResourceName(this.name, 'vpc'), { - natGateways: 0 - }) - } + this.vpc = new ec2.Vpc(this, config.getFullStackResourceName(this.name, 'vpc'), { + natGateways: 0 + }) + } } \ No newline at end of file diff --git a/infrastructure/cdk/src/providers/nodes.ts b/infrastructure/cdk/src/providers/nodes.ts index 20051ee88..976c72d17 100644 --- a/infrastructure/cdk/src/providers/nodes.ts +++ b/infrastructure/cdk/src/providers/nodes.ts @@ -7,10 +7,9 @@ import { kebabCase, pascalCase } from '@casimir/helpers' import { Config } from './config' /** - * Cryptonodes stack + * Public node stack */ export class NodesStack extends cdk.Stack { - /** Stack name */ public readonly name = pascalCase('nodes') constructor(scope: Construct, id: string, props: NodesStackProps) { @@ -20,10 +19,8 @@ export class NodesStack extends cdk.Stack { const { rootDomain, subdomains } = config const { hostedZone } = props - /** Get the nodes web server IP */ const nodesIp = secretsmanager.Secret.fromSecretNameV2(this, config.getFullStackResourceName(this.name, 'nodes-ip'), kebabCase(config.getFullStackResourceName(this.name, 'nodes-ip'))) - /** Create an A record for the nodes web server IP */ new route53.ARecord(this, config.getFullStackResourceName(this.name, 'a-record-api'), { recordName: `${subdomains.nodes}.${rootDomain}`, zone: hostedZone as route53.IHostedZone, diff --git a/infrastructure/cdk/src/providers/users.ts b/infrastructure/cdk/src/providers/users.ts index 6dfcde1c3..cedb3d288 100644 --- a/infrastructure/cdk/src/providers/users.ts +++ b/infrastructure/cdk/src/providers/users.ts @@ -11,14 +11,11 @@ import { Config } from './config' import { kebabCase } from '@casimir/helpers' /** - * Users API stack + * Users service stack */ export class UsersStack extends cdk.Stack { - /** Stack name */ public readonly name = 'users' - /** Path to stack build assets or Dockerfile */ public readonly assetPath = 'services/users/Dockerfile' - /** Path to stack build context */ public readonly contextPath = '../../' constructor(scope: Construct, id: string, props: UsersStackProps) { @@ -28,7 +25,6 @@ export class UsersStack extends cdk.Stack { const { project, stage, rootDomain, subdomains } = config const { certificate, hostedZone, vpc } = props - /** Create the users DB credentials */ const dbCredentials = new secretsmanager.Secret(this, config.getFullStackResourceName(this.name, 'db-credentials'), { secretName: kebabCase(config.getFullStackResourceName(this.name, 'db-credentials')), generateSecretString: { @@ -41,16 +37,13 @@ export class UsersStack extends cdk.Stack { } }) - /** Create a DB security group */ const dbSecurityGroup = new ec2.SecurityGroup(this, config.getFullStackResourceName(this.name, 'db-security-group'), { vpc, allowAllOutbound: true }) - /** Allow inbound traffic to DB security group */ dbSecurityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(5432)) - /** Create a DB cluster */ const dbCluster = new rds.DatabaseCluster(this, config.getFullStackResourceName(this.name, 'db-cluster'), { engine: rds.DatabaseClusterEngine.auroraPostgres({ version: rds.AuroraPostgresEngineVersion.VER_15_2 @@ -70,24 +63,21 @@ export class UsersStack extends cdk.Stack { port: 5432 }) - /** Add serverless V2 scaling configuration to DB cluster */ cdk.Aspects.of(dbCluster).add({ visit(node) { if (node instanceof rds.CfnDBCluster) { node.serverlessV2ScalingConfiguration = { - minCapacity: 0.5, // min capacity is 0.5 vCPU - maxCapacity: 1 // max capacity is 1 vCPU (default) + minCapacity: 0.5, + maxCapacity: 1 } } }, }) - /** Create an ECS cluster */ const ecsCluster = new ecs.Cluster(this, config.getFullStackResourceName(this.name, 'cluster'), { vpc }) - /** Build the users service image */ const imageAsset = new ecrAssets.DockerImageAsset(this, config.getFullStackResourceName(this.name, 'image'), { directory: this.contextPath, file: this.assetPath, @@ -95,7 +85,6 @@ export class UsersStack extends cdk.Stack { ignoreMode: cdk.IgnoreMode.GIT }) - /** Get the required secrets */ const requiredSecrets = { DB_HOST: ecs.Secret.fromSecretsManager(dbCredentials, 'host'), DB_PORT: ecs.Secret.fromSecretsManager(dbCredentials, 'port'), @@ -104,17 +93,14 @@ export class UsersStack extends cdk.Stack { DB_PASSWORD: ecs.Secret.fromSecretsManager(dbCredentials, 'password') } - /** Define optional secrets */ const optionalSecrets: { SESSIONS_HOST?: ecs.Secret, SESSIONS_KEY?: ecs.Secret } = {} if (config.stage === 'prod' || config.stage === 'dev') { - /** Get the sessions credentials */ const sessionsCredentials = secretsmanager.Secret.fromSecretNameV2(this, config.getFullStackResourceName(this.name, 'sessions-credentials'), kebabCase(config.getFullStackResourceName(this.name, 'sessions-credentials'))) optionalSecrets.SESSIONS_HOST = ecs.Secret.fromSecretsManager(sessionsCredentials, 'host') optionalSecrets.SESSIONS_KEY = ecs.Secret.fromSecretsManager(sessionsCredentials, 'key') } - /** Create a load-balanced users service */ const fargateService = new ecsPatterns.ApplicationLoadBalancedFargateService(this, config.getFullStackResourceName(this.name, 'fargate'), { assignPublicIp: true, certificate, @@ -134,7 +120,6 @@ export class UsersStack extends cdk.Stack { } }) - /** Override the default health check path */ fargateService.targetGroup.configureHealthCheck({ path: '/health' }) diff --git a/infrastructure/cdk/src/providers/web.ts b/infrastructure/cdk/src/providers/web.ts index 2ef904b00..d97cd65c9 100644 --- a/infrastructure/cdk/src/providers/web.ts +++ b/infrastructure/cdk/src/providers/web.ts @@ -15,78 +15,70 @@ import { Config } from './config' * Web app stack */ export class WebStack extends cdk.Stack { - /** Stack name */ - public readonly name = pascalCase('web') - /** Path to stack build assets or Dockerfile */ - public readonly assetPath = '../../apps/web/dist' + public readonly name = pascalCase('web') + public readonly assetPath = '../../apps/web/dist' - constructor(scope: Construct, id: string, props: WebStackProps) { - super(scope, id, props) + constructor(scope: Construct, id: string, props: WebStackProps) { + super(scope, id, props) - const config = new Config() - const { rootDomain, subdomains } = config - const { hostedZone, certificate } = props + const config = new Config() + const { rootDomain, subdomains } = config + const { hostedZone, certificate } = props - /** Create a bucket for the web app */ - const bucket = new s3.Bucket(this, config.getFullStackResourceName(this.name, 'bucket'), { - accessControl: s3.BucketAccessControl.PRIVATE - }) + const bucket = new s3.Bucket(this, config.getFullStackResourceName(this.name, 'bucket'), { + accessControl: s3.BucketAccessControl.PRIVATE + }) - /** Set bucket to allow cloudfront origin */ - const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, config.getFullStackResourceName(this.name, 'origin-access-identity')) - bucket.grantRead(originAccessIdentity) + const originAccessIdentity = new cloudfront.OriginAccessIdentity(this, config.getFullStackResourceName(this.name, 'origin-access-identity')) + bucket.grantRead(originAccessIdentity) - /** Create a requisite us-east-1 certificate for distribution if needed */ - const distributionCertificate = (() => { - if (certificate?.env.region === 'us-east-1') return certificate - /** Replace deprecated method when cross-region support is out of beta */ - return new certmgr.DnsValidatedCertificate(this, config.getFullStackResourceName(this.name, 'cert'), { - domainName: rootDomain, - subjectAlternativeNames: [`${subdomains.web}.${rootDomain}`], - hostedZone, - region: 'us-east-1' - }) - })() + const distributionCertificate = (() => { + if (certificate?.env.region === 'us-east-1') return certificate + /** Replace deprecated method when cross-region support is out of beta */ + return new certmgr.DnsValidatedCertificate(this, config.getFullStackResourceName(this.name, 'cert'), { + domainName: rootDomain, + subjectAlternativeNames: [`${subdomains.web}.${rootDomain}`], + hostedZone, + region: 'us-east-1' + }) + })() - /** Create a cloudfront distribution for the web app */ - const distribution = new cloudfront.Distribution(this, config.getFullStackResourceName(this.name, 'distribution'), { - certificate: distributionCertificate, - defaultRootObject: 'index.html', - errorResponses: [ - { - httpStatus: 403, - responseHttpStatus: 200, - responsePagePath: '/index.html', - ttl: cdk.Duration.minutes(30) - }, - { - httpStatus: 404, - responseHttpStatus: 200, - responsePagePath: '/index.html', - ttl: cdk.Duration.minutes(30) - } - ], - defaultBehavior: { - viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, - origin: new cloudfrontOrigins.S3Origin(bucket, { originAccessIdentity }) - }, - domainNames: [`${subdomains.web}.${rootDomain}`] - }) + const distribution = new cloudfront.Distribution(this, config.getFullStackResourceName(this.name, 'distribution'), { + certificate: distributionCertificate, + defaultRootObject: 'index.html', + errorResponses: [ + { + httpStatus: 403, + responseHttpStatus: 200, + responsePagePath: '/index.html', + ttl: cdk.Duration.minutes(30) + }, + { + httpStatus: 404, + responseHttpStatus: 200, + responsePagePath: '/index.html', + ttl: cdk.Duration.minutes(30) + } + ], + defaultBehavior: { + viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + origin: new cloudfrontOrigins.S3Origin(bucket, { originAccessIdentity }) + }, + domainNames: [`${subdomains.web}.${rootDomain}`] + }) - /** Deploy the web app to the bucket */ - new s3Deployment.BucketDeployment(this, config.getFullStackResourceName(this.name, 'bucket-deployment'), { - destinationBucket: bucket, - sources: [s3Deployment.Source.asset(this.assetPath)], - distribution, - distributionPaths: ['/*'] - }) + new s3Deployment.BucketDeployment(this, config.getFullStackResourceName(this.name, 'bucket-deployment'), { + destinationBucket: bucket, + sources: [s3Deployment.Source.asset(this.assetPath)], + distribution, + distributionPaths: ['/*'] + }) - /** Create an A record for the web app subdomain */ - new route53.ARecord(this, config.getFullStackResourceName(this.name, 'a-record-app'), { - recordName: `${subdomains.web}.${rootDomain}`, - zone: hostedZone as route53.IHostedZone, - target: route53.RecordTarget.fromAlias(new route53targets.CloudFrontTarget(distribution)), - ttl: cdk.Duration.minutes(1), - }) - } + new route53.ARecord(this, config.getFullStackResourceName(this.name, 'a-record-app'), { + recordName: `${subdomains.web}.${rootDomain}`, + zone: hostedZone as route53.IHostedZone, + target: route53.RecordTarget.fromAlias(new route53targets.CloudFrontTarget(distribution)), + ttl: cdk.Duration.minutes(1), + }) + } } \ No newline at end of file diff --git a/infrastructure/cdk/test/all.test.ts b/infrastructure/cdk/test/all.test.ts index 3dc2dcb77..892262420 100644 --- a/infrastructure/cdk/test/all.test.ts +++ b/infrastructure/cdk/test/all.test.ts @@ -8,42 +8,50 @@ import { LandingStack } from '../src/providers/landing' import { NodesStack } from '../src/providers/nodes' import { DnsStack } from '../src/providers/dns' import { WebStack } from '../src/providers/web' +import { DocsStack } from '../src/providers/docs' test('All stacks created', () => { - const config = new Config() - const { env } = config - const app = new cdk.App() - - const { hostedZone, certificate } = new DnsStack(app, config.getFullStackName('dns'), { env }) - const { vpc } = new NetworkStack(app, config.getFullStackName('network'), { env }) - const analyticsStack = new AnalyticsStack(app, config.getFullStackName('analytics'), { env }) - const usersStack = new UsersStack(app, config.getFullStackName('users'), { env, certificate, hostedZone, vpc }) - const nodesStack = new NodesStack(app, config.getFullStackName('nodes'), { env, hostedZone }) - const landingStack = new LandingStack(app, config.getFullStackName('landing'), { env, certificate, hostedZone }) - const webStack = new WebStack(app, config.getFullStackName('web'), { env, certificate, hostedZone }) - - const analyticsTemplate = assertions.Template.fromStack(analyticsStack) - Object.keys(analyticsTemplate.findOutputs('*')).forEach(output => { - expect(output).toBeDefined() - }) - - const usersTemplate = assertions.Template.fromStack(usersStack) - Object.keys(usersTemplate.findOutputs('*')).forEach(output => { - expect(output).toBeDefined() - }) - - const nodesTemplate = assertions.Template.fromStack(nodesStack) - Object.keys(nodesTemplate.findOutputs('*')).forEach(output => { - expect(output).toBeDefined() - }) - - const landingTemplate = assertions.Template.fromStack(landingStack) - Object.keys(landingTemplate.findOutputs('*')).forEach(output => { - expect(output).toBeDefined() - }) - - const webTemplate = assertions.Template.fromStack(webStack) - Object.keys(webTemplate.findOutputs('*')).forEach(output => { - expect(output).toBeDefined() - }) + const config = new Config() + const { env } = config + const app = new cdk.App() + + const { hostedZone, certificate } = new DnsStack(app, config.getFullStackName('dns'), { env }) + const { vpc } = new NetworkStack(app, config.getFullStackName('network'), { env }) + + const analyticsStack = new AnalyticsStack(app, config.getFullStackName('analytics'), { env }) + const docsStack = new DocsStack(app, config.getFullStackName('docs'), { env, certificate, hostedZone }) + const landingStack = new LandingStack(app, config.getFullStackName('landing'), { env, certificate, hostedZone }) + const nodesStack = new NodesStack(app, config.getFullStackName('nodes'), { env, hostedZone }) + const usersStack = new UsersStack(app, config.getFullStackName('users'), { env, certificate, hostedZone, vpc }) + const webStack = new WebStack(app, config.getFullStackName('web'), { env, certificate, hostedZone }) + + const analyticsTemplate = assertions.Template.fromStack(analyticsStack) + Object.keys(analyticsTemplate.findOutputs('*')).forEach(output => { + expect(output).toBeDefined() + }) + + const usersTemplate = assertions.Template.fromStack(usersStack) + Object.keys(usersTemplate.findOutputs('*')).forEach(output => { + expect(output).toBeDefined() + }) + + const webTemplate = assertions.Template.fromStack(webStack) + Object.keys(webTemplate.findOutputs('*')).forEach(output => { + expect(output).toBeDefined() + }) + + const docsTemplate = assertions.Template.fromStack(docsStack) + Object.keys(docsTemplate.findOutputs('*')).forEach(output => { + expect(output).toBeDefined() + }) + + const nodesTemplate = assertions.Template.fromStack(nodesStack) + Object.keys(nodesTemplate.findOutputs('*')).forEach(output => { + expect(output).toBeDefined() + }) + + const landingTemplate = assertions.Template.fromStack(landingStack) + Object.keys(landingTemplate.findOutputs('*')).forEach(output => { + expect(output).toBeDefined() + }) }) diff --git a/infrastructure/cdk/test/analytics.test.ts b/infrastructure/cdk/test/analytics.test.ts index 8d9fa52c1..12d33c51f 100644 --- a/infrastructure/cdk/test/analytics.test.ts +++ b/infrastructure/cdk/test/analytics.test.ts @@ -5,43 +5,42 @@ import { AnalyticsStack } from '../src/providers/analytics' import { Schema, eventSchema, actionSchema } from '@casimir/data' test('Analytics stack created', () => { - const config = new Config() - const { env } = config - const app = new cdk.App() - const analyticsStack = new AnalyticsStack(app, config.getFullStackName('analytics'), { env }) + const config = new Config() + const { env } = config + const app = new cdk.App() - const analyticsTemplate = assertions.Template.fromStack(analyticsStack) + const analyticsStack = new AnalyticsStack(app, config.getFullStackName('analytics'), { env }) + const analyticsTemplate = assertions.Template.fromStack(analyticsStack) + Object.keys(analyticsTemplate.findOutputs('*')).forEach(output => { + expect(output).toBeDefined() + }) - const resource = analyticsTemplate.findResources('AWS::Glue::Table') + const resource = analyticsTemplate.findResources('AWS::Glue::Table') - const eventTable = Object.keys(resource).filter(key => key.includes('EventTable')) - const eventColumns = resource[eventTable[0]].Properties.TableInput.StorageDescriptor.Columns - const eventGlueSchema = new Schema(eventSchema).getGlueColumns() + const eventTable = Object.keys(resource).filter(key => key.includes('EventTable')) + const eventColumns = resource[eventTable[0]].Properties.TableInput.StorageDescriptor.Columns + const eventGlueSchema = new Schema(eventSchema).getGlueColumns() - for (const column of eventColumns) { - const { Name: name, Type: type } = column - const columnName = Object.keys(eventSchema.properties).filter(key => key === name)[0] - const columnType = eventGlueSchema.filter(key => key.name === name)[0].type.inputString + for (const column of eventColumns) { + const { Name: name, Type: type } = column + const columnName = Object.keys(eventSchema.properties).filter(key => key === name)[0] + const columnType = eventGlueSchema.filter(key => key.name === name)[0].type.inputString - expect(columnType).toEqual(type) - expect(columnName).toEqual(name) - } + expect(columnType).toEqual(type) + expect(columnName).toEqual(name) + } - const actionTable = Object.keys(resource).filter(key => key.includes('ActionTable'))[0] - const actionColumns = resource[actionTable].Properties.TableInput.StorageDescriptor.Columns - const actionGlueSchema = new Schema(actionSchema).getGlueColumns() + const actionTable = Object.keys(resource).filter(key => key.includes('ActionTable'))[0] + const actionColumns = resource[actionTable].Properties.TableInput.StorageDescriptor.Columns + const actionGlueSchema = new Schema(actionSchema).getGlueColumns() - for (const column of actionColumns) { - const { Name: name, Type: type } = column - const columnName = Object.keys(actionSchema.properties).filter(key => key === name)[0] - const columnType = actionGlueSchema.filter(key => key.name === name)[0].type.inputString + for (const column of actionColumns) { + const { Name: name, Type: type } = column + const columnName = Object.keys(actionSchema.properties).filter(key => key === name)[0] + const columnType = actionGlueSchema.filter(key => key.name === name)[0].type.inputString - expect(columnType).toEqual(type) - expect(columnName).toEqual(name) - } - - Object.keys(analyticsTemplate.findOutputs('*')).forEach(output => { - expect(output).toBeDefined() - }) + expect(columnType).toEqual(type) + expect(columnName).toEqual(name) + } }) \ No newline at end of file diff --git a/scripts/cdk/deploy.ts b/scripts/cdk/deploy.ts index 88d2417a4..3f5ca750c 100755 --- a/scripts/cdk/deploy.ts +++ b/scripts/cdk/deploy.ts @@ -8,11 +8,6 @@ void async function () { process.env.PROJECT = process.env.PROJECT || 'casimir' process.env.STAGE = process.env.STAGE || 'dev' process.env.AWS_REGION = process.env.AWS_REGION || 'us-east-2' - - /** Set default values for contracts addresses */ - process.env.PUBLIC_MANAGER_ADDRESS = '0x5d35a44Db8a390aCfa997C9a9Ba3a2F878595630' - process.env.PUBLIC_VIEWS_ADDRESS = '0xC88C4022347305336E344e624E5Fa4fB8e61c21E' - process.env.PUBLIC_REGISTRY_ADDRESS = '0xB567C0E87Ec176177E44C91577704267C24Fbd83' await loadCredentials() process.env.AWS_ACCOUNT = await getSecret('casimir-aws-account') @@ -20,11 +15,17 @@ void async function () { process.env.ETHEREUM_RPC_URL = 'https://nodes.casimir.co/eth/hardhat' process.env.USERS_URL = `https://users.${process.env.STAGE}.casimir.co` + process.env.PUBLIC_MANAGER_ADDRESS = '0x5d35a44Db8a390aCfa997C9a9Ba3a2F878595630' + process.env.PUBLIC_VIEWS_ADDRESS = '0xC88C4022347305336E344e624E5Fa4fB8e61c21E' + process.env.PUBLIC_REGISTRY_ADDRESS = '0xB567C0E87Ec176177E44C91577704267C24Fbd83' process.env.PUBLIC_ETHEREUM_RPC_URL = process.env.ETHEREUM_RPC_URL process.env.PUBLIC_USERS_URL = process.env.USERS_URL process.env.PUBLIC_CRYPTO_COMPARE_API_KEY = await getSecret('casimir-crypto-compare-api-key') - + + await run('npm run build --workspace @casimir/ethereum') + await run('npm run build:docs --workspace @casimir/ethereum') await run('npm run build --workspace @casimir/landing') + await run('npm run build --workspace @casimir/users') await run('npm run build --workspace @casimir/web') await run('npm run bootstrap --workspace @casimir/cdk') diff --git a/scripts/cdk/test.ts b/scripts/cdk/test.ts index 3478b09c1..7e631b150 100644 --- a/scripts/cdk/test.ts +++ b/scripts/cdk/test.ts @@ -12,10 +12,20 @@ void async function () { await loadCredentials() process.env.AWS_ACCOUNT = await getSecret('casimir-aws-account') - process.env.PUBLIC_USERS_URL = `https://users.${process.env.STAGE}.casimir.co` + process.env.ETHEREUM_RPC_URL = 'https://nodes.casimir.co/eth/hardhat' + process.env.USERS_URL = `https://users.${process.env.STAGE}.casimir.co` + + process.env.PUBLIC_MANAGER_ADDRESS = '0x5d35a44Db8a390aCfa997C9a9Ba3a2F878595630' + process.env.PUBLIC_VIEWS_ADDRESS = '0xC88C4022347305336E344e624E5Fa4fB8e61c21E' + process.env.PUBLIC_REGISTRY_ADDRESS = '0xB567C0E87Ec176177E44C91577704267C24Fbd83' + process.env.PUBLIC_ETHEREUM_RPC_URL = process.env.ETHEREUM_RPC_URL + process.env.PUBLIC_USERS_URL = process.env.USERS_URL process.env.PUBLIC_CRYPTO_COMPARE_API_KEY = await getSecret('casimir-crypto-compare-api-key') + await run('npm run build --workspace @casimir/ethereum') + await run('npm run build:docs --workspace @casimir/ethereum') await run('npm run build --workspace @casimir/landing') + await run('npm run build --workspace @casimir/users') await run('npm run build --workspace @casimir/web') console.log('🚀 Testing CDK app')