From e28c0ffb419512e0e49f1b83b7e8b811e8f119ec Mon Sep 17 00:00:00 2001 From: p3430233 <61738524+p3430233@users.noreply.github.com> Date: Tue, 18 Aug 2020 10:46:29 +0100 Subject: [PATCH 01/23] corrected conflict between Graph Name and Release Name (#41) * corrected conflict between Graph Name and Release Name * id -> releaseName * changed naming convention release_Name and graph_Name * lowercase -> format, graph_Name release_Name -> snake case * graphId -> graphName * graphName -> releaseName * graphId -> graphName * ResourceId Ref changed * ResourceId Ref changed * DELETE HttpMethod ResourcId Ref mismatch fixed * successfully deploys, deletes and gets graph * graphName -> graphId * refactored format_graph_name, minor style changes --- lib/database/graph-database.ts | 2 +- lib/rest-api/README.md | 6 +- lib/rest-api/kai-rest-api.ts | 2 +- lib/rest-api/lambdas/add_graph_request.py | 23 +- lib/rest-api/lambdas/delete_graph_request.py | 20 +- lib/rest-api/lambdas/get_graph_request.py | 18 +- lib/rest-api/lambdas/graph/__init__.py | 21 +- lib/workers/lambdas/add_graph.py | 32 +- lib/workers/lambdas/delete_graph.py | 8 +- lib/workers/lambdas/graph/__init__.py | 17 +- lib/workers/lambdas/kubernetes/__init__.py | 1 - package-lock.json | 860 ++++++++++++++++++- test/database/graph-database.test.ts | 6 +- test/rest-api/kai-rest-api.test.ts | 6 +- 14 files changed, 949 insertions(+), 73 deletions(-) diff --git a/lib/database/graph-database.ts b/lib/database/graph-database.ts index 56efe35..27b5c3d 100644 --- a/lib/database/graph-database.ts +++ b/lib/database/graph-database.ts @@ -30,7 +30,7 @@ export class GraphDatabase extends cdk.Construct { // Table this._table = new dynamo.Table(this, "GraphDynamoTable", { - partitionKey: { name: "graphId", type: dynamo.AttributeType.STRING }, + partitionKey: { name: "releaseName", type: dynamo.AttributeType.STRING }, billingMode: dynamo.BillingMode.PROVISIONED, removalPolicy: cdk.RemovalPolicy.DESTROY }); diff --git a/lib/rest-api/README.md b/lib/rest-api/README.md index 41c66ed..0dae986 100644 --- a/lib/rest-api/README.md +++ b/lib/rest-api/README.md @@ -67,11 +67,11 @@ Example response: ```json [ { - "graphId": "roadTraffic", + "graphName": "roadTraffic", "currentState": "DEPLOYED" }, { - "graphId": "basicGraph", + "graphName": "basicGraph", "currentState": "DELETION_QUEUED" } ] @@ -83,7 +83,7 @@ Retrieves a single graph from the backend database. If the Graph Id is not found Example response: ```json { - "graphId": "roadTraffic", + "graphName": "roadTraffic", "currentState": "DEPLOYED" } ``` diff --git a/lib/rest-api/kai-rest-api.ts b/lib/rest-api/kai-rest-api.ts index 28e0544..eb5bf3b 100644 --- a/lib/rest-api/kai-rest-api.ts +++ b/lib/rest-api/kai-rest-api.ts @@ -33,7 +33,7 @@ export class KaiRestApi extends cdk.Construct { // REST API const restApi = new api.RestApi(this, this.node.uniqueId + "RestApi"); // Could add a default 404 handler here const graphsResource = restApi.root.addResource("graphs"); - const graph = graphsResource.addResource("{graphId}"); + const graph = graphsResource.addResource("{graphName}"); // Create MethodOptions to secure access to the RestApi methods using the Cognito user pool const methodOptions = new KaiRestAuthorizer(this, "KaiRestApiAuthorizer", { diff --git a/lib/rest-api/lambdas/add_graph_request.py b/lib/rest-api/lambdas/add_graph_request.py index 0353444..4b38422 100644 --- a/lib/rest-api/lambdas/add_graph_request.py +++ b/lib/rest-api/lambdas/add_graph_request.py @@ -9,23 +9,24 @@ graph = Graph() user = User() -def is_graph_id_valid(graph_id): - if graph_id is None: +def is_graph_name_valid(graph_name): + if graph_name is None: return False - return re.match("^[a-z0-9]+$", graph_id) # At present Graph ids have to be lowercase alphanumerics + return re.match("^[a-zA-Z0-9]+$", graph_name) # Graph names have to be alphanumerics + def handler(event, context): request_body = json.loads(event["body"]) # Check request is valid - graph_id = request_body["graphId"] + graph_name = request_body["graphName"] schema = request_body["schema"] - if not is_graph_id_valid(graph_id): + if not is_graph_name_valid(graph_name): return { "statusCode": 400, - "body": "graphId is a required field which must made up of lowercase alphanumeric characters" + "body": "graphName is a required field which must made up of alphanumeric characters" } if schema is None: return { @@ -36,6 +37,9 @@ def handler(event, context): # Get variables from env queue_url = os.getenv("sqs_queue_url") + # Convert graph name to lowercase + release_name = graph.format_graph_name(graph_name) + initial_status = "DEPLOYMENT_QUEUED" administrators = [] @@ -53,12 +57,12 @@ def handler(event, context): } try: - graph.create_graph(graph_id, initial_status, administrators) + graph.create_graph(release_name, graph_name, initial_status, administrators) except ClientError as e: if e.response['Error']['Code']=='ConditionalCheckFailedException': return { "statusCode": 400, - "body": "Graph " + graph_id + " already exists. Graph names must be unique" + "body": "Graph release name " + release_name + " already exists as the lowercase conversion of " + graph_name + ". Graph names must be unique" } else: return { @@ -68,7 +72,8 @@ def handler(event, context): # Create message to send to worker. This also filters out anything else in the body message = { - "graphId": graph_id, + "graphName": graph_name, + "releaseName": release_name, "schema": schema, "expectedStatus": initial_status } diff --git a/lib/rest-api/lambdas/delete_graph_request.py b/lib/rest-api/lambdas/delete_graph_request.py index bf71ac1..f39afb9 100644 --- a/lib/rest-api/lambdas/delete_graph_request.py +++ b/lib/rest-api/lambdas/delete_graph_request.py @@ -15,31 +15,34 @@ def handler(event, context): params = event["pathParameters"] # Check request is valid - graph_id = params["graphId"] + graph_name = params["graphName"] - if graph_id is None: + # Convert graph name to lowercase + release_name = graph.format_graph_name(graph_name) + + if graph_name is None: return { statusCode: 400, - body: "graphId is a required field" + body: "graphName is a required field" } requesting_user = user.get_requesting_cognito_user(event) - if not user.is_authorized(requesting_user, graph_id): + if not user.is_authorized(requesting_user, graph_name): return { "statusCode": 403, - "body": "User: {} is not authorized to delete graph: {}".format(requesting_user, graph_id) + "body": "User: {} is not authorized to delete graph: {}".format(requesting_user, graph_name) } initial_status = "DELETION_QUEUED" # Add Entry to table try: - graph.update_graph(graph_id, initial_status) + graph.update_graph(release_name, initial_status) except ClientError as e: if e.response['Error']['Code'] == 'ConditionalCheckFailedException': return { "statusCode": 400, - "body": "Graph " + graph_id + " does not exist. It may have already been deleted" + "body": "Graph " + graph_name + " does not exist. It may have already been deleted" } else: return { @@ -49,7 +52,8 @@ def handler(event, context): # Set the status so the worker knows what to expect. This also filters out anything else in the body message = { - "graphId": graph_id, + "graphName": graph_name, + "releaseName": release_name, "expectedStatus": initial_status } diff --git a/lib/rest-api/lambdas/get_graph_request.py b/lib/rest-api/lambdas/get_graph_request.py index c774cc2..99d8b0d 100644 --- a/lib/rest-api/lambdas/get_graph_request.py +++ b/lib/rest-api/lambdas/get_graph_request.py @@ -7,21 +7,19 @@ graph = Graph() user = User() - def handler(event, context): """ Main entrypoint for the HTTP GET lambda functions. This function - serves both GET handlers so returns all graphs if no graphId + serves both GET handlers so returns all graphs if no graphName is specified in the path parameters """ - path_params = event["pathParameters"] return_all = False - graph_id = None - if path_params is None or path_params["graphId"] is None: + graph_name = None + if path_params is None or path_params["graphName"] is None: return_all = True else: - graph_id = path_params["graphId"] + graph_name = path_params["graphName"] requesting_user = user.get_requesting_cognito_user(event) @@ -31,19 +29,19 @@ def handler(event, context): "body": json.dumps(graph.get_all_graphs(requesting_user)) } else: - if not user.is_authorized(requesting_user, graph_id): + if not user.is_authorized(requesting_user, graph_name): return { "statusCode": 403, - "body": "User: {} is not authorized to retrieve graph: {}".format(requesting_user, graph_id) + "body": "User: {} is not authorized to retrieve graph: {}".format(requesting_user, graph_name) } try: return { "statusCode": 200, - "body": json.dumps(graph.get_graph(graph_id)) + "body": json.dumps(graph.get_graph(graph.format_graph_name(graph_name))) } except Exception as e: return { "statusCode": 404, - "body": graph_id + " was not found" + "body": graph_name + " was not found" } diff --git a/lib/rest-api/lambdas/graph/__init__.py b/lib/rest-api/lambdas/graph/__init__.py index 8de4a70..908665d 100644 --- a/lib/rest-api/lambdas/graph/__init__.py +++ b/lib/rest-api/lambdas/graph/__init__.py @@ -10,6 +10,10 @@ def __init__(self): self.table = dynamodb.Table(graph_table_name) + def format_graph_name(self, graph_name): + return graph_name.lower() + + def get_all_graphs(self, requesting_user): """ Gets all graphs from Dynamodb table @@ -21,13 +25,13 @@ def get_all_graphs(self, requesting_user): return list(filter(lambda graph: requesting_user in graph["administrators"], graphs)) - def get_graph(self, graph_id): + def get_graph(self, release_name): """ Gets a specific graph from Dynamodb table """ response = self.table.get_item( Key={ - "graphId": graph_id + "releaseName": release_name } ) if "Item" in response: @@ -35,25 +39,26 @@ def get_graph(self, graph_id): raise Exception - def update_graph(self, graph_id, status): + def update_graph(self, release_name, status): self.table.update_item( Key={ - "graphId": graph_id + "releaseName": release_name }, UpdateExpression="SET currentState = :state", ExpressionAttributeValues={ ":state": status }, - ConditionExpression=boto3.dynamodb.conditions.Attr("graphId").exists() + ConditionExpression=boto3.dynamodb.conditions.Attr("releaseName").exists() ) - def create_graph(self, graph_id, status, administrators): + def create_graph(self, release_name, graph_name, status, administrators): self.table.put_item( Item={ - "graphId": graph_id, + "graphName": graph_name, + "releaseName": release_name, "currentState": status, "administrators": administrators }, - ConditionExpression=boto3.dynamodb.conditions.Attr("graphId").not_exists() + ConditionExpression=boto3.dynamodb.conditions.Attr("releaseName").not_exists() ) diff --git a/lib/workers/lambdas/add_graph.py b/lib/workers/lambdas/add_graph.py index bf3a4b6..cbf71bc 100644 --- a/lib/workers/lambdas/add_graph.py +++ b/lib/workers/lambdas/add_graph.py @@ -1,12 +1,13 @@ -import os -import kubernetes -from graph import Graph import json -import boto3 import logging +import os import random import string +import boto3 +import kubernetes +from graph import Graph + logger = logging.getLogger() logger.setLevel(logging.INFO) @@ -31,7 +32,7 @@ def generate_password(length=8): return random_password -def create_values(graph_id, schema, security_groups): +def create_values(graph_name, schema, security_groups): """ Generates the Json required to deploy the Gaffer Helm Chart """ @@ -48,7 +49,7 @@ def create_values(graph_id, schema, security_groups): return { "graph": { "config": { - "graphId": graph_id + "graphId": graph_name }, "schema": { "elements.json": json.dumps(schema["elements"]), @@ -70,7 +71,7 @@ def create_values(graph_id, schema, security_groups): "password": generate_password(), "permissions": { "table": { - graph_id: [ + graph_name: [ "READ", "WRITE", "BULK_IMPORT", @@ -101,32 +102,33 @@ def deploy_graph(helm_client, body, security_groups): Helm Chart. """ # Extract values from body - graph_id = body["graphId"] + graph_name = body["graphName"] + release_name = body["releaseName"] schema = body["schema"] expected_status = body["expectedStatus"] # Create Graph to log progress of deployment - graph = Graph(graph_table_name, graph_id) + graph = Graph(graph_table_name, release_name) if not graph.check_status(expected_status): - logger.warn("Deployment of %s abandoned as graph had unexpected status", graph_id) + logger.warn("Deployment of %s abandoned as graph had unexpected status", graph_name) return # Update Status to DEPLOYMENT_IN_PROGRESS graph.update_status("DEPLOYMENT_IN_PROGRESS") # Create values file - values = create_values(graph_id, schema, security_groups) + values = create_values(graph_name, schema, security_groups) - values_file = "/tmp/" + graph_id + ".json" + values_file = "/tmp/" + graph_name + ".json" with open(values_file, "w") as f: f.write(json.dumps(values, indent=2)) # Deploy Graph - success = helm_client.install_chart(graph_id, values=values_file) + success = helm_client.install_chart(release_name, values=values_file) if success: - logger.info("Deployment of " + graph_id + " Succeeded") + logger.info("Deployment of " + graph_name + " Succeeded") graph.update_status("DEPLOYED") else: graph.update_status("DEPLOYMENT_FAILED") @@ -156,4 +158,4 @@ def handler(event, context): body = json.loads(record["body"]) deploy_graph(helm_client, body, security_groups) - return \ No newline at end of file + return diff --git a/lib/workers/lambdas/delete_graph.py b/lib/workers/lambdas/delete_graph.py index cb09a33..a451a8f 100644 --- a/lib/workers/lambdas/delete_graph.py +++ b/lib/workers/lambdas/delete_graph.py @@ -19,19 +19,19 @@ def uninstall_release(helm_client, body): """ Uninstalls a release from the Kubernetes Cluster """ - graph_id = body["graphId"] + release_Name = body["releaseName"] expected_status=body["expectedStatus"] # Create a Graph object to track the deletion - graph = Graph(graph_table_name, graph_id) + graph = Graph(graph_table_name, release_Name) if not graph.check_status(expected_status): - logger.warn("Graph %s had unexpected status. Abandoning delete", graph_id) + logger.warn("Graph %s had unexpected status. Abandoning delete", release_Name) return graph.update_status("DELETION_IN_PROGRESS") - uninstalled = helm_client.uninstall_chart(graph_id) + uninstalled = helm_client.uninstall_chart(release_Name) if uninstalled: graph.delete() else: diff --git a/lib/workers/lambdas/graph/__init__.py b/lib/workers/lambdas/graph/__init__.py index 35c7a63..0b68990 100644 --- a/lib/workers/lambdas/graph/__init__.py +++ b/lib/workers/lambdas/graph/__init__.py @@ -1,13 +1,17 @@ import boto3 +import logging + +logger = logging.getLogger() +logger.setLevel(logging.INFO) class Graph: """ Represents a Graph object in a DynamoDB table """ - def __init__(self, table_name, graph_id): + def __init__(self, table_name, release_name): dynamodb = boto3.resource("dynamodb") self.table = dynamodb.Table(table_name) - self.graph_id = graph_id + self.release_name = release_name def check_status(self, expected_status): """ @@ -15,14 +19,15 @@ def check_status(self, expected_status): """ response = self.table.get_item( Key={ - 'graphId': self.graph_id + "releaseName": self.release_name } ) + logger.info(response) # If the graph does not exist, it cannot have the expected status graph = response["Item"] if graph is None: - return False + return False status = graph["currentState"] @@ -34,7 +39,7 @@ def update_status(self, status): """ self.table.update_item( Key={ - "graphId": self.graph_id + "releaseName": self.release_name }, UpdateExpression="SET currentState = :state", ExpressionAttributeValues={ @@ -48,6 +53,6 @@ def delete(self): """ self.table.delete_item( Key={ - "graphId": self.graph_id + "releaseName": self.release_name } ) diff --git a/lib/workers/lambdas/kubernetes/__init__.py b/lib/workers/lambdas/kubernetes/__init__.py index cf83d0c..dd50880 100644 --- a/lib/workers/lambdas/kubernetes/__init__.py +++ b/lib/workers/lambdas/kubernetes/__init__.py @@ -6,7 +6,6 @@ standard_kubeconfig="/tmp/kubeconfig" - class HelmClient: __HELM_CMD="helm" diff --git a/package-lock.json b/package-lock.json index 5d6e8d9..8171c51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2308,6 +2308,856 @@ "aws-sdk": "^2.699.0", "glob": "^7.1.6", "yargs": "^15.3.1" + }, + "dependencies": { + "@aws-cdk/cdk-assets-schema": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cdk-assets-schema/-/cdk-assets-schema-1.46.0.tgz", + "integrity": "sha512-5YM/WHdfiiXkyN+oqPWIcrU7nQUzEVRmViiN+SGy/NZ6Tj9r30N9YygYMZO8z9sM7r20dOTl+pY9SYrclIeNUQ==", + "dev": true, + "requires": { + "semver": "^7.2.2" + } + }, + "@aws-cdk/cloud-assembly-schema": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.46.0.tgz", + "integrity": "sha512-ji92c9RCMoY426vH1RbLstUOiUYqM84qa4Ww9Nsbn6Aw3qIS1Gz603dNgcIl+rK78SOq6WqlgykSyFAFykqVgA==", + "dev": true, + "requires": { + "jsonschema": "^1.2.5", + "semver": "^7.2.2" + } + }, + "@aws-cdk/cx-api": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.46.0.tgz", + "integrity": "sha512-3U4wbywHlYLqWllCNOVGqxDee3N5PxaQHkjpC3v0vhI+LPwcJEW1Vz11k3PMG6FaPcrLrfkAn1y7HSFsPXh8Qg==", + "dev": true, + "requires": { + "@aws-cdk/cloud-assembly-schema": "1.46.0", + "semver": "^7.2.2" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "archiver": { + "version": "4.0.1", + "resolved": "https://registry.yarnpkg.com/archiver/-/archiver-4.0.1.tgz#3f722b121777e361ca9fad374ecda38e77e63c7f", + "integrity": "sha512-/YV1pU4Nhpf/rJArM23W6GTUjT0l++VbjykrCRua1TSXrn+yM8Qs7XvtwSiRse0iCe49EPNf7ktXnPsWuSb91Q==", + "dev": true, + "requires": { + "archiver-utils": "^2.1.0", + "async": "^2.6.3", + "buffer-crc32": "^0.2.1", + "glob": "^7.1.6", + "readable-stream": "^3.6.0", + "tar-stream": "^2.1.2", + "zip-stream": "^3.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "requires": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + } + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "aws-sdk": { + "version": "2.699.0", + "resolved": "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.699.0.tgz#e77b6ffa4c860882e2779c060b74fab33d42f554", + "integrity": "sha512-EC431z/+i/cJgOgnDpOJ8Fa6+p7Oo1vIvdm/uJqP9tJX3+pxi/M/tvQavfz4yAlLBFqjQwxa8nrPisby0Mr5MQ==", + "dev": true, + "requires": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "dependencies": { + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "bl": { + "version": "4.0.2", + "resolved": "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a", + "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "compress-commons": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/compress-commons/-/compress-commons-3.0.0.tgz#833944d84596e537224dd91cf92f5246823d4f1d", + "integrity": "sha512-FyDqr8TKX5/X0qo+aVfaZ+PVmNJHJeckFBlq8jZGSJOgnynhfifoyl24qaqdUdDIBe0EVTHByN6NAkqYvE/2Xg==", + "dev": true, + "requires": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^3.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^2.3.7" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "crc": { + "version": "3.8.0", + "resolved": "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "requires": { + "buffer": "^5.1.0" + } + }, + "crc32-stream": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-3.0.1.tgz#cae6eeed003b0e44d739d279de5ae63b171b4e85", + "integrity": "sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==", + "dev": true, + "requires": { + "crc": "^3.4.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=", + "dev": true + }, + "jsonschema": { + "version": "1.2.6", + "resolved": "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.6.tgz#52b0a8e9dc06bbae7295249d03e4b9faee8a0c0b", + "integrity": "sha512-SqhURKZG07JyKKeo/ir24QnS4/BV7a6gQy93bUSe4lUdNp0QNpIz2c9elWJQ9dpc5cQYY6cvCzgRwy0MQCLyqA==", + "dev": true + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "requires": { + "readable-stream": "^2.0.5" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "dev": true + }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c", + "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, + "lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88", + "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "tar-stream": { + "version": "2.1.2", + "resolved": "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.2.tgz#6d5ef1a7e5783a95ff70b69b97455a5968dc1325", + "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", + "dev": true, + "requires": { + "bl": "^4.0.1", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + }, + "dependencies": { + "sax": { + "version": "1.2.4", + "resolved": "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + } + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "15.3.1", + "resolved": "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.1" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "zip-stream": { + "version": "3.0.1", + "resolved": "https://registry.yarnpkg.com/zip-stream/-/zip-stream-3.0.1.tgz#cb8db9d324a76c09f9b76b31a12a48638b0b9708", + "integrity": "sha512-r+JdDipt93ttDjsOVPU5zaq5bAyY+3H19bDrThkvuVxC0xMQzU1PJcS6D+KrP3u96gH9XLomcHPb+2skoDjulQ==", + "dev": true, + "requires": { + "archiver-utils": "^2.1.0", + "compress-commons": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + } } }, "charenc": { @@ -2591,7 +3441,8 @@ "esprima": "^4.0.1", "estraverse": "^4.2.0", "esutils": "^2.0.2", - "optionator": "^0.8.1" + "optionator": "^0.8.1", + "source-map": "~0.6.1" }, "dependencies": { "esprima": { @@ -2934,6 +3785,7 @@ "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", "dev": true, "requires": { + "graceful-fs": "^4.1.6", "universalify": "^1.0.0" } }, @@ -2971,6 +3823,12 @@ "p-locate": "^4.1.0" } }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c", diff --git a/test/database/graph-database.test.ts b/test/database/graph-database.test.ts index a4cacd5..7e5f534 100644 --- a/test/database/graph-database.test.ts +++ b/test/database/graph-database.test.ts @@ -37,7 +37,7 @@ test("should create a database", () => { expectCDK(stack).to(haveResource("AWS::DynamoDB::Table")); }); -test("should use the graphId as a primary key", () => { +test("should use the releaseName as a primary key", () => { // Given const stack = new Stack(); @@ -48,13 +48,13 @@ test("should use the graphId as a primary key", () => { expectCDK(stack).to(haveResource("AWS::DynamoDB::Table", { "KeySchema": [ { - "AttributeName": "graphId", + "AttributeName": "releaseName", "KeyType": "HASH" } ], "AttributeDefinitions": [ { - "AttributeName": "graphId", + "AttributeName": "releaseName", "AttributeType": "S" } ] diff --git a/test/rest-api/kai-rest-api.test.ts b/test/rest-api/kai-rest-api.test.ts index 4883a0e..c9f12a9 100644 --- a/test/rest-api/kai-rest-api.test.ts +++ b/test/rest-api/kai-rest-api.test.ts @@ -108,7 +108,7 @@ test("The REST API should have a resource which can GET specific graphs", () => // Then expectCDK(stack).to(haveResource("AWS::ApiGateway::Resource", { - PathPart: "{graphId}", + PathPart: "{graphName}", ParentId: { "Ref": "TestTestRestApigraphs6F3DCBD4" } @@ -117,7 +117,7 @@ test("The REST API should have a resource which can GET specific graphs", () => expectCDK(stack).to(haveResourceLike("AWS::ApiGateway::Method", { HttpMethod: "GET", ResourceId: { - Ref: "TestTestRestApigraphsgraphId0A18A4C6" + Ref: "TestTestRestApigraphs6F3DCBD4" }, RestApiId: { Ref: "TestTestRestApiF3AB3CBC" @@ -194,7 +194,7 @@ test("The specific Graph resource should handle DELETE requests", () => { expectCDK(stack).to(haveResourceLike("AWS::ApiGateway::Method", { HttpMethod: "DELETE", ResourceId: { - Ref: "TestTestRestApigraphsgraphId0A18A4C6" + Ref: "TestTestRestApigraphsgraphNameB9AC8DA7" }, RestApiId: { Ref: "TestTestRestApiF3AB3CBC" From 8096e7b5fced41b3aa841dbcc14c1d83848ffa9e Mon Sep 17 00:00:00 2001 From: m29827 <62667809+m29827@users.noreply.github.com> Date: Tue, 18 Aug 2020 10:52:02 +0100 Subject: [PATCH 02/23] gh-26 Remove application load-balancers and target groups when cluster deleted. (#39) * gh-26 Remove application load-balancers and target groups when cluster deleted. * gh-26 Removing *.pyc files and ignoring __pycache__ directory * gh-26 Uninstall graphs is now asynchronous and uses the delete graph SQS queue to initiate deletion. * gh-26 Code review comments: reverting changes to generated Accumulo passwords. * gh-26 Uplifting cdk version * gh-26 Correcting add_graph.py * gh-26 Fixing bug caused by merging gh-35 changes. Co-authored-by: d47853 --- .gitignore | 2 + lib/app-stack.ts | 19 +- lib/platform/crhelper-policy-statement.ts | 29 + lib/platform/graph-platform.ts | 1 + lib/platform/graph-uninstaller-props.ts | 26 + lib/platform/graph-uninstaller.ts | 102 +++ lib/platform/lambdas/crhelper/__init__.py | 1 + lib/platform/lambdas/crhelper/log_helper.py | 83 ++ .../lambdas/crhelper/resource_helper.py | 330 ++++++++ lib/platform/lambdas/crhelper/utils.py | 34 + lib/platform/lambdas/uninstall_graphs.py | 83 ++ .../lambdas/uninstall_graphs_is_complete.py | 40 + lib/rest-api/kai-rest-api.ts | 26 +- lib/rest-api/lambdas/user/__init__.py | 3 +- lib/workers/worker.ts | 16 +- package-lock.json | 795 +++++++++--------- package.json | 26 +- test/worker/worker.test.ts | 4 +- 18 files changed, 1200 insertions(+), 420 deletions(-) create mode 100644 lib/platform/crhelper-policy-statement.ts create mode 100644 lib/platform/graph-uninstaller-props.ts create mode 100644 lib/platform/graph-uninstaller.ts create mode 100644 lib/platform/lambdas/crhelper/__init__.py create mode 100644 lib/platform/lambdas/crhelper/log_helper.py create mode 100644 lib/platform/lambdas/crhelper/resource_helper.py create mode 100644 lib/platform/lambdas/crhelper/utils.py create mode 100644 lib/platform/lambdas/uninstall_graphs.py create mode 100644 lib/platform/lambdas/uninstall_graphs_is_complete.py diff --git a/.gitignore b/.gitignore index 2054684..82e7f2b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ !header.js *.d.ts node_modules +__pycache__/ +*.pyc # CDK temporary file cdk.context.json diff --git a/lib/app-stack.ts b/lib/app-stack.ts index 9ab21fc..0fe0474 100644 --- a/lib/app-stack.ts +++ b/lib/app-stack.ts @@ -17,6 +17,7 @@ import * as cdk from "@aws-cdk/core"; import * as sam from "@aws-cdk/aws-sam"; import { GraphPlatForm } from "./platform/graph-platform"; +import { GraphUninstaller } from "./platform/graph-uninstaller"; import { KaiRestApi } from "./rest-api/kai-rest-api"; import { LAMBDA_LAYER_ARN, LAMBDA_LAYER_VERSION, ADD_GRAPH_TIMEOUT, DELETE_GRAPH_TIMEOUT, DELETE_GRAPH_WORKER_BATCH_SIZE, ADD_GRAPH_WORKER_BATCH_SIZE } from "./constants"; import { LayerVersion } from "@aws-cdk/aws-lambda"; @@ -72,7 +73,7 @@ export class AppStack extends cdk.Stack { batchSize: ADD_GRAPH_WORKER_BATCH_SIZE }); - new Worker(this, "DeleteGraphWorker", { + const deleteGraphWorker = new Worker(this, "DeleteGraphWorker", { cluster: platform.eksCluster, queue: kaiRest.deleteGraphQueue, kubectlLayer: kubectlLambdaLayer, @@ -81,5 +82,21 @@ export class AppStack extends cdk.Stack { timeout: DELETE_GRAPH_TIMEOUT, batchSize: DELETE_GRAPH_WORKER_BATCH_SIZE }); + + // Graph uninstaller + new GraphUninstaller(this, "GraphUninstaller", { + getGraphsFunctionArn: kaiRest.getGraphsLambda.functionArn, + deleteGraphFunctionArn: kaiRest.deleteGraphLambda.functionArn, + kubectlLayer: kubectlLambdaLayer, + timeout: cdk.Duration.seconds(30), + dependencies: [ + platform, + database, + deleteGraphWorker, + kaiRest.getGraphsLambda, + kaiRest.deleteGraphLambda, + kaiRest.deleteGraphQueue + ] + }); } } \ No newline at end of file diff --git a/lib/platform/crhelper-policy-statement.ts b/lib/platform/crhelper-policy-statement.ts new file mode 100644 index 0000000..119df00 --- /dev/null +++ b/lib/platform/crhelper-policy-statement.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2020 Crown Copyright + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { PolicyStatement } from "@aws-cdk/aws-iam"; + +export const crhelperPolicyStatement: PolicyStatement = new PolicyStatement({ + resources: ["*"], + actions: [ + "lambda:AddPermission", + "lambda:RemovePermission", + "events:PutRule", + "events:DeleteRule", + "events:PutTargets", + "events:RemoveTargets" + ] +}); diff --git a/lib/platform/graph-platform.ts b/lib/platform/graph-platform.ts index 7168d29..d5788cd 100644 --- a/lib/platform/graph-platform.ts +++ b/lib/platform/graph-platform.ts @@ -58,6 +58,7 @@ export class GraphPlatForm extends cdk.Construct { // Create cluster this._eksCluster = new eks.Cluster(this, "EksCluster", { + version: eks.KubernetesVersion.V1_16, kubectlEnabled: true, vpc: vpc, mastersRole: mastersRole, diff --git a/lib/platform/graph-uninstaller-props.ts b/lib/platform/graph-uninstaller-props.ts new file mode 100644 index 0000000..04828a3 --- /dev/null +++ b/lib/platform/graph-uninstaller-props.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2020 Crown Copyright + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Duration, IConstruct } from "@aws-cdk/core"; +import { ILayerVersion } from "@aws-cdk/aws-lambda"; + +export interface GraphUninstallerProps { + getGraphsFunctionArn: string; + deleteGraphFunctionArn: string; + kubectlLayer: ILayerVersion; + timeout: Duration; + dependencies: IConstruct[]; +} diff --git a/lib/platform/graph-uninstaller.ts b/lib/platform/graph-uninstaller.ts new file mode 100644 index 0000000..1c539f8 --- /dev/null +++ b/lib/platform/graph-uninstaller.ts @@ -0,0 +1,102 @@ +/* + * Copyright 2020 Crown Copyright + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from "path"; +import { Construct, CustomResource } from "@aws-cdk/core"; +import { GraphUninstallerProps } from "./graph-uninstaller-props"; +import { Function, Runtime, AssetCode } from "@aws-cdk/aws-lambda"; +import { PolicyStatement } from "@aws-cdk/aws-iam"; +import { Provider } from "@aws-cdk/custom-resources"; +import { crhelperPolicyStatement } from "./crhelper-policy-statement"; + +export class GraphUninstaller extends Construct { + + constructor(scope: Construct, id: string, props: GraphUninstallerProps) { + super(scope, id); + this.createConstructs(id, props); + } + + private createConstructs(id: string, props: GraphUninstallerProps) { + + const uninstallGraphsLambda = new Function(this, "UninstallGraphsLambda", { + runtime: Runtime.PYTHON_3_7, + code: new AssetCode(path.join(__dirname, "lambdas")), + handler: "uninstall_graphs.handler", + layers: [ props.kubectlLayer ], + timeout: props.timeout, + environment: { + "get_graphs_function_arn": props.getGraphsFunctionArn, + "delete_graph_function_arn": props.deleteGraphFunctionArn + } + }); + + const lambdaInvokeGetGraphsPolicyStatement: PolicyStatement = new PolicyStatement({ + resources: [ + props.getGraphsFunctionArn + ], + actions: [ + "lambda:InvokeFunction" + ] + }); + + const lambdaInvokeDeleteGraphPolicyStatement: PolicyStatement = new PolicyStatement({ + resources: [ + props.deleteGraphFunctionArn + ], + actions: [ + "lambda:InvokeFunction" + ] + }); + + if (uninstallGraphsLambda.role) { + uninstallGraphsLambda.role.addToPolicy(crhelperPolicyStatement); + uninstallGraphsLambda.role.addToPolicy(lambdaInvokeGetGraphsPolicyStatement); + uninstallGraphsLambda.role.addToPolicy(lambdaInvokeDeleteGraphPolicyStatement); + } + + const uninstallGraphsIsCompleteLambda = new Function(this, "UninstallGraphsIsCompleteLambda", { + runtime: Runtime.PYTHON_3_7, + code: new AssetCode(path.join(__dirname, "lambdas")), + handler: "uninstall_graphs_is_complete.handler", + layers: [ props.kubectlLayer ], + timeout: props.timeout, + environment: { + "get_graphs_function_arn": props.getGraphsFunctionArn + } + }); + + if (uninstallGraphsIsCompleteLambda.role) { + uninstallGraphsIsCompleteLambda.role.addToPolicy(lambdaInvokeGetGraphsPolicyStatement); + } + + const uninstallGraphsCustomResourceProvider = new Provider(this, "UninstallGraphsCustomResourceProvider", { + onEventHandler: uninstallGraphsLambda, + isCompleteHandler: uninstallGraphsIsCompleteLambda + }); + + const uninstallGraphsCustomResource = new CustomResource(this, "UninstallGraphsCustomResource", { + serviceToken: uninstallGraphsCustomResourceProvider.serviceToken + }); + + /* Ensure deletion of the uninstallGraphsCustomResource occurs before the uninstallGraphsCustomResourceProvider. */ + uninstallGraphsCustomResource.node.addDependency(uninstallGraphsCustomResourceProvider); + + /* Ensure all the dependencies required to uninstall graphs are retained until the uninstallGraphsCustomResource has been deleted successfully. */ + for (const dependency of props.dependencies) { + uninstallGraphsCustomResource.node.addDependency(dependency); + } + } +} \ No newline at end of file diff --git a/lib/platform/lambdas/crhelper/__init__.py b/lib/platform/lambdas/crhelper/__init__.py new file mode 100644 index 0000000..7ac4e3f --- /dev/null +++ b/lib/platform/lambdas/crhelper/__init__.py @@ -0,0 +1 @@ +from crhelper.resource_helper import CfnResource, SUCCESS, FAILED diff --git a/lib/platform/lambdas/crhelper/log_helper.py b/lib/platform/lambdas/crhelper/log_helper.py new file mode 100644 index 0000000..9339771 --- /dev/null +++ b/lib/platform/lambdas/crhelper/log_helper.py @@ -0,0 +1,83 @@ +from __future__ import print_function +import json +import logging + + +def _json_formatter(obj): + """Formatter for unserialisable values.""" + return str(obj) + + +class JsonFormatter(logging.Formatter): + """AWS Lambda Logging formatter. + + Formats the log message as a JSON encoded string. If the message is a + dict it will be used directly. If the message can be parsed as JSON, then + the parse d value is used in the output record. + """ + + def __init__(self, **kwargs): + super(JsonFormatter, self).__init__() + self.format_dict = { + 'timestamp': '%(asctime)s', + 'level': '%(levelname)s', + 'location': '%(name)s.%(funcName)s:%(lineno)d', + } + self.format_dict.update(kwargs) + self.default_json_formatter = kwargs.pop( + 'json_default', _json_formatter) + + def format(self, record): + record_dict = record.__dict__.copy() + record_dict['asctime'] = self.formatTime(record) + + log_dict = { + k: v % record_dict + for k, v in self.format_dict.items() + if v + } + + if isinstance(record_dict['msg'], dict): + log_dict['message'] = record_dict['msg'] + else: + log_dict['message'] = record.getMessage() + + # Attempt to decode the message as JSON, if so, merge it with the + # overall message for clarity. + try: + log_dict['message'] = json.loads(log_dict['message']) + except (TypeError, ValueError): + pass + + if record.exc_info: + # Cache the traceback text to avoid converting it multiple times + # (it's constant anyway) + # from logging.Formatter:format + if not record.exc_text: + record.exc_text = self.formatException(record.exc_info) + + if record.exc_text: + log_dict['exception'] = record.exc_text + + json_record = json.dumps(log_dict, default=self.default_json_formatter) + + if hasattr(json_record, 'decode'): # pragma: no cover + json_record = json_record.decode('utf-8') + + return json_record + + +def setup(level='DEBUG', formatter_cls=JsonFormatter, boto_level=None, **kwargs): + if formatter_cls: + for handler in logging.root.handlers: + handler.setFormatter(formatter_cls(**kwargs)) + + logging.root.setLevel(level) + + if not boto_level: + boto_level = level + + logging.getLogger('boto').setLevel(boto_level) + logging.getLogger('boto3').setLevel(boto_level) + logging.getLogger('botocore').setLevel(boto_level) + logging.getLogger('urllib3').setLevel(boto_level) diff --git a/lib/platform/lambdas/crhelper/resource_helper.py b/lib/platform/lambdas/crhelper/resource_helper.py new file mode 100644 index 0000000..83361b0 --- /dev/null +++ b/lib/platform/lambdas/crhelper/resource_helper.py @@ -0,0 +1,330 @@ +# -*- coding: utf-8 -*- +""" +TODO: +* Async mode – take a wait condition handle as an input, increases max timeout to 12 hours +* Idempotency – If a duplicate request comes in (say there was a network error in signaling back to cfn) the subsequent + request should return the already created response, will need a persistent store of some kind... +* Functional tests +""" + +from __future__ import print_function +import threading +from crhelper.utils import _send_response +from crhelper import log_helper +import logging +import random +import boto3 +import string +import json +import os +from time import sleep + +logger = logging.getLogger(__name__) + +SUCCESS = 'SUCCESS' +FAILED = 'FAILED' + + +class CfnResource(object): + + def __init__(self, json_logging=False, log_level='DEBUG', boto_level='ERROR', polling_interval=2, sleep_on_delete=120): + self._sleep_on_delete= sleep_on_delete + self._create_func = None + self._update_func = None + self._delete_func = None + self._poll_create_func = None + self._poll_update_func = None + self._poll_delete_func = None + self._timer = None + self._init_failed = None + self._json_logging = json_logging + self._log_level = log_level + self._boto_level = boto_level + self._send_response = False + self._polling_interval = polling_interval + self.Status = "" + self.Reason = "" + self.PhysicalResourceId = "" + self.StackId = "" + self.RequestId = "" + self.LogicalResourceId = "" + self.Data = {} + self._event = {} + self._context = None + self._response_url = "" + self._sam_local = os.getenv('AWS_SAM_LOCAL') + self._region = os.getenv('AWS_REGION') + try: + if not self._sam_local: + self._lambda_client = boto3.client('lambda', region_name=self._region) + self._events_client = boto3.client('events', region_name=self._region) + self._logs_client = boto3.client('logs', region_name=self._region) + if json_logging: + log_helper.setup(log_level, boto_level=boto_level, RequestType='ContainerInit') + else: + log_helper.setup(log_level, formatter_cls=None, boto_level=boto_level) + except Exception as e: + logger.error(e, exc_info=True) + self.init_failure(e) + + def __call__(self, event, context): + try: + self._log_setup(event, context) + logger.debug(event) + if not self._crhelper_init(event, context): + return + # Check for polling functions + if self._poll_enabled() and self._sam_local: + logger.info("Skipping poller functionality, as this is a local invocation") + elif self._poll_enabled(): + self._polling_init(event) + # If polling is not enabled, then we should respond + else: + logger.debug("enabling send_response") + self._send_response = True + logger.debug("_send_response: %s" % self._send_response) + if self._send_response: + if self.RequestType == 'Delete': + self._wait_for_cwlogs() + self._cfn_response(event) + except Exception as e: + logger.error(e, exc_info=True) + self._send(FAILED, str(e)) + finally: + if self._timer: + self._timer.cancel() + + def _wait_for_cwlogs(self, sleep=sleep): + time_left = int(self._context.get_remaining_time_in_millis() / 1000) - 15 + sleep_time = 0 + + if time_left > self._sleep_on_delete: + sleep_time = self._sleep_on_delete + + if sleep_time > 1: + sleep(sleep_time) + + def _log_setup(self, event, context): + if self._json_logging: + log_helper.setup(self._log_level, boto_level=self._boto_level, RequestType=event['RequestType'], + StackId=event['StackId'], RequestId=event['RequestId'], + LogicalResourceId=event['LogicalResourceId'], aws_request_id=context.aws_request_id) + else: + log_helper.setup(self._log_level, boto_level=self._boto_level, formatter_cls=None) + + def _crhelper_init(self, event, context): + self._send_response = False + self.Status = SUCCESS + self.Reason = "" + self.PhysicalResourceId = "" + self.StackId = event["StackId"] + self.RequestId = event["RequestId"] + self.LogicalResourceId = event["LogicalResourceId"] + self.Data = {} + if "CrHelperData" in event.keys(): + self.Data = event["CrHelperData"] + self.RequestType = event["RequestType"] + self._event = event + self._context = context + self._response_url = event['ResponseURL'] + if self._timer: + self._timer.cancel() + if self._init_failed: + self._send(FAILED, str(self._init_failed)) + return False + self._set_timeout() + self._wrap_function(self._get_func()) + return True + + def _polling_init(self, event): + # Setup polling on initial request + logger.debug("pid1: %s" % self.PhysicalResourceId) + if 'CrHelperPoll' not in event.keys() and self.Status != FAILED: + logger.info("Setting up polling") + self.Data["PhysicalResourceId"] = self.PhysicalResourceId + self._setup_polling() + self.PhysicalResourceId = None + logger.debug("pid2: %s" % self.PhysicalResourceId) + # if physical id is set, or there was a failure then we're done + logger.debug("pid3: %s" % self.PhysicalResourceId) + if self.PhysicalResourceId or self.Status == FAILED: + logger.info("Polling complete, removing cwe schedule") + self._remove_polling() + self._send_response = True + + def generate_physical_id(self, event): + return '_'.join([ + event['StackId'].split('/')[1], + event['LogicalResourceId'], + self._rand_string(8) + ]) + + def _cfn_response(self, event): + # Use existing PhysicalResourceId if it's in the event and no ID was set + if not self.PhysicalResourceId and "PhysicalResourceId" in event.keys(): + logger.info("PhysicalResourceId present in event, Using that for response") + self.PhysicalResourceId = event['PhysicalResourceId'] + # Generate a physical id if none is provided + elif not self.PhysicalResourceId or self.PhysicalResourceId is True: + logger.info("No physical resource id returned, generating one...") + self.PhysicalResourceId = self.generate_physical_id(event) + self._send() + + def _poll_enabled(self): + return getattr(self, "_poll_{}_func".format(self._event['RequestType'].lower())) + + def create(self, func): + self._create_func = func + return func + + def update(self, func): + self._update_func = func + return func + + def delete(self, func): + self._delete_func = func + return func + + def poll_create(self, func): + self._poll_create_func = func + return func + + def poll_update(self, func): + self._poll_update_func = func + return func + + def poll_delete(self, func): + self._poll_delete_func = func + return func + + def _wrap_function(self, func): + try: + self.PhysicalResourceId = func(self._event, self._context) if func else '' + except Exception as e: + logger.error(str(e), exc_info=True) + self.Reason = str(e) + self.Status = FAILED + + def _timeout(self): + logger.error("Execution is about to time out, sending failure message") + self._send(FAILED, "Execution timed out") + + def _set_timeout(self): + self._timer = threading.Timer((self._context.get_remaining_time_in_millis() / 1000.00) - 0.5, + self._timeout) + self._timer.start() + + def _get_func(self): + request_type = "_{}_func" + if "CrHelperPoll" in self._event.keys(): + request_type = "_poll" + request_type + return getattr(self, request_type.format(self._event['RequestType'].lower())) + + def _send(self, status=None, reason="", send_response=_send_response): + if len(str(str(self.Reason))) > 256: + self.Reason = "ERROR: (truncated) " + str(self.Reason)[len(str(self.Reason)) - 240:] + if len(str(reason)) > 256: + reason = "ERROR: (truncated) " + str(reason)[len(str(reason)) - 240:] + response_body = { + 'Status': self.Status, + 'PhysicalResourceId': str(self.PhysicalResourceId), + 'StackId': self.StackId, + 'RequestId': self.RequestId, + 'LogicalResourceId': self.LogicalResourceId, + 'Reason': str(self.Reason), + 'Data': self.Data, + } + if status: + response_body.update({'Status': status, 'Reason': reason}) + send_response(self._response_url, response_body) + + def init_failure(self, error): + self._init_failed = error + logger.error(str(error), exc_info=True) + + def _cleanup_response(self): + for k in ["CrHelperPoll", "CrHelperPermission", "CrHelperRule"]: + if k in self.Data.keys(): + del self.Data[k] + + @staticmethod + def _rand_string(l): + return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(l)) + + def _add_permission(self, rule_arn): + sid = self._event['LogicalResourceId'] + self._rand_string(8) + self._lambda_client.add_permission( + FunctionName=self._context.function_name, + StatementId=sid, + Action='lambda:InvokeFunction', + Principal='events.amazonaws.com', + SourceArn=rule_arn + ) + return sid + + def _put_rule(self): + response = self._events_client.put_rule( + Name=self._event['LogicalResourceId'] + self._rand_string(8), + ScheduleExpression='rate({} minutes)'.format(self._polling_interval), + State='ENABLED', + ) + return response["RuleArn"] + + def _put_targets(self, func_name): + region = self._event['CrHelperRule'].split(":")[3] + account_id = self._event['CrHelperRule'].split(":")[4] + partition = self._event['CrHelperRule'].split(":")[1] + rule_name = self._event['CrHelperRule'].split("/")[1] + logger.debug(self._event) + self._events_client.put_targets( + Rule=rule_name, + Targets=[ + { + 'Id': '1', + 'Arn': 'arn:%s:lambda:%s:%s:function:%s' % (partition, region, account_id, func_name), + 'Input': json.dumps(self._event) + } + ] + ) + + def _remove_targets(self, rule_arn): + self._events_client.remove_targets( + Rule=rule_arn.split("/")[1], + Ids=['1'] + ) + + def _remove_permission(self, sid): + self._lambda_client.remove_permission( + FunctionName=self._context.function_name, + StatementId=sid + ) + + def _delete_rule(self, rule_arn): + self._events_client.delete_rule( + Name=rule_arn.split("/")[1] + ) + + def _setup_polling(self): + self._event['CrHelperData'] = self.Data + self._event['CrHelperPoll'] = True + self._event['CrHelperRule'] = self._put_rule() + self._event['CrHelperPermission'] = self._add_permission(self._event['CrHelperRule']) + self._put_targets(self._context.function_name) + + def _remove_polling(self): + if 'CrHelperData' in self._event.keys(): + self._event.pop('CrHelperData') + if "PhysicalResourceId" in self.Data.keys(): + self.Data.pop("PhysicalResourceId") + if 'CrHelperRule' in self._event.keys(): + self._remove_targets(self._event['CrHelperRule']) + else: + logger.error("Cannot remove CloudWatch events rule, Rule arn not available in event") + if 'CrHelperPermission' in self._event.keys(): + self._remove_permission(self._event['CrHelperPermission']) + else: + logger.error("Cannot remove lambda events permission, permission id not available in event") + if 'CrHelperRule' in self._event.keys(): + self._delete_rule(self._event['CrHelperRule']) + else: + logger.error("Cannot remove CloudWatch events target, Rule arn not available in event") diff --git a/lib/platform/lambdas/crhelper/utils.py b/lib/platform/lambdas/crhelper/utils.py new file mode 100644 index 0000000..a0c96ed --- /dev/null +++ b/lib/platform/lambdas/crhelper/utils.py @@ -0,0 +1,34 @@ +from __future__ import print_function +import json +import logging as logging +import time +from urllib.parse import urlsplit, urlunsplit +from http.client import HTTPSConnection + +logger = logging.getLogger(__name__) + + +def _send_response(response_url, response_body): + try: + json_response_body = json.dumps(response_body) + except Exception as e: + msg = "Failed to convert response to json: {}".format(str(e)) + logger.error(msg, exc_info=True) + response_body = {'Status': 'FAILED', 'Data': {}, 'Reason': msg} + json_response_body = json.dumps(response_body) + logger.debug("CFN response URL: {}".format(response_url)) + logger.debug(json_response_body) + headers = {'content-type': '', 'content-length': str(len(json_response_body))} + split_url = urlsplit(response_url) + host = split_url.netloc + url = urlunsplit(("", "", *split_url[2:])) + while True: + try: + connection = HTTPSConnection(host) + connection.request(method="PUT", url=url, body=json_response_body, headers=headers) + response = connection.getresponse() + logger.info("CloudFormation returned status code: {}".format(response.reason)) + break + except Exception as e: + logger.error("Unexpected failure sending response to CloudFormation {}".format(e), exc_info=True) + time.sleep(5) diff --git a/lib/platform/lambdas/uninstall_graphs.py b/lib/platform/lambdas/uninstall_graphs.py new file mode 100644 index 0000000..d3c9b2d --- /dev/null +++ b/lib/platform/lambdas/uninstall_graphs.py @@ -0,0 +1,83 @@ +from crhelper import CfnResource +import json +import logging +import os +import boto3 + +logger = logging.getLogger(__name__) +helper = CfnResource(json_logging=False, log_level='DEBUG', boto_level='CRITICAL', sleep_on_delete=120) + + +try: + get_graphs_function_arn = os.getenv("get_graphs_function_arn") + delete_graph_function_arn = os.getenv("delete_graph_function_arn") + client = boto3.client("lambda") + pass +except Exception as e: + helper.init_failure(e) + + +@helper.create +def create(event, context): + logger.info("Got Create") + return None + + +@helper.update +def update(event, context): + logger.info("Got Update") + + +@helper.delete +def delete(event, context): + logger.info("Got Delete") + graphs = getGraphs() + + logger.info("Deleting graphs: {}".format(graphs)) + + for graph in graphs: + logger.info("Deleting graph: {}".format(graph)) + response = client.invoke( + FunctionName = delete_graph_function_arn, + InvocationType = "RequestResponse", + Payload = json.dumps({ + "pathParameters": { + "graphId": graph["graphId"] + } + }) + ) + responsePayloadJson = json.loads(response["Payload"].read().decode("utf-8")) + logger.info("Received responsePayloadJson: {}".format(responsePayloadJson)) + if responsePayloadJson["statusCode"] != 202: + logger.error("Unable to delete graph: {}, received status code: {}, message: {}".format( + graph["graphId"], + responsePayloadJson["statusCode"], + responsePayloadJson["body"] + ) + ) + + +@helper.poll_delete +def poll_delete(event, context): + logger.info("Got Poll Delete") + return True if (len(getGraphs()) == 0) else None + + +def getGraphs(): + logger.info("Getting graphs") + response = client.invoke( + FunctionName = get_graphs_function_arn, + InvocationType = "RequestResponse", + Payload = json.dumps({ "pathParameters": None }) + ) + responsePayloadJson = json.loads(response["Payload"].read().decode("utf-8")) + logger.info("Received responsePayloadJson: {}".format(responsePayloadJson)) + + if responsePayloadJson["statusCode"] != 200: + raise Exception("Unable to obtain listing of graphs received response code: {}".format(responsePayloadJson["statusCode"])) + + return json.loads(responsePayloadJson["body"]) + + +def handler(event, context): + helper(event, context) diff --git a/lib/platform/lambdas/uninstall_graphs_is_complete.py b/lib/platform/lambdas/uninstall_graphs_is_complete.py new file mode 100644 index 0000000..44121f5 --- /dev/null +++ b/lib/platform/lambdas/uninstall_graphs_is_complete.py @@ -0,0 +1,40 @@ +from crhelper import CfnResource +import json +import logging +import os +import boto3 + +logger = logging.getLogger(__name__) +helper = CfnResource(json_logging=False, log_level='DEBUG', boto_level='CRITICAL', sleep_on_delete=120) + + +try: + get_graphs_function_arn = os.getenv("get_graphs_function_arn") + client = boto3.client("lambda") + pass +except Exception as e: + helper.init_failure(e) + + +def getGraphs(): + logger.info("Getting graphs") + response = client.invoke( + FunctionName = get_graphs_function_arn, + InvocationType = "RequestResponse", + Payload = json.dumps({ "pathParameters": None }) + ) + responsePayloadJson = json.loads(response["Payload"].read().decode("utf-8")) + logger.info("Received responsePayloadJson: {}".format(responsePayloadJson)) + + if responsePayloadJson["statusCode"] != 200: + raise Exception("Unable to obtain listing of graphs received response code: {}".format(responsePayloadJson["statusCode"])) + + return json.loads(responsePayloadJson["body"]) + + +def handler(event, context): + if ("RequestType" in event and event["RequestType"] == "Delete"): + isComplete = (len(getGraphs()) == 0) + else: + isComplete = True + return { "IsComplete": isComplete } diff --git a/lib/rest-api/kai-rest-api.ts b/lib/rest-api/kai-rest-api.ts index eb5bf3b..50e14d3 100644 --- a/lib/rest-api/kai-rest-api.ts +++ b/lib/rest-api/kai-rest-api.ts @@ -27,6 +27,8 @@ import { KaiRestAuthorizer } from "./authentication/kai-rest-authorizer"; export class KaiRestApi extends cdk.Construct { private readonly _addGraphQueue: sqs.Queue; private readonly _deleteGraphQueue: sqs.Queue; + private readonly _getGraphsLambda: lambda.Function; + private readonly _deleteGraphLambda: lambda.Function; constructor(scope: cdk.Construct, readonly id: string, props: KaiRestApiProps) { super(scope, id); @@ -76,7 +78,7 @@ export class KaiRestApi extends cdk.Construct { visibilityTimeout: DELETE_GRAPH_TIMEOUT }); - const deleteGraphLambda = new lambda.Function(this, "DeleteGraphHandler", { + this._deleteGraphLambda = new lambda.Function(this, "DeleteGraphHandler", { runtime: lambda.Runtime.PYTHON_3_7, code: lambdas, handler: "delete_graph_request.handler", @@ -88,12 +90,12 @@ export class KaiRestApi extends cdk.Construct { } }); - props.graphTable.grantReadWriteData(deleteGraphLambda); - this.deleteGraphQueue.grantSendMessages(deleteGraphLambda); - graph.addMethod("DELETE", new api.LambdaIntegration(deleteGraphLambda), methodOptions); + props.graphTable.grantReadWriteData(this._deleteGraphLambda); + this.deleteGraphQueue.grantSendMessages(this._deleteGraphLambda); + graph.addMethod("DELETE", new api.LambdaIntegration(this._deleteGraphLambda), methodOptions); // GET handlers - const getGraphsLambda = new lambda.Function(this, "GetGraphsHandler", { + this._getGraphsLambda = new lambda.Function(this, "GetGraphsHandler", { runtime: lambda.Runtime.PYTHON_3_7, code: lambdas, handler: "get_graph_request.handler", @@ -104,9 +106,10 @@ export class KaiRestApi extends cdk.Construct { } }); - props.graphTable.grantReadData(getGraphsLambda); + props.graphTable.grantReadData(this._getGraphsLambda); // Both GET and GET all are served by the same lambda - const getGraphIntegration = new api.LambdaIntegration(getGraphsLambda); + + const getGraphIntegration = new api.LambdaIntegration(this._getGraphsLambda); graphsResource.addMethod("GET", getGraphIntegration, methodOptions); graph.addMethod("GET", getGraphIntegration, methodOptions); } @@ -118,4 +121,13 @@ export class KaiRestApi extends cdk.Construct { public get deleteGraphQueue(): sqs.Queue { return this._deleteGraphQueue; } + + public get getGraphsLambda(): lambda.Function { + return this._getGraphsLambda; + } + + public get deleteGraphLambda(): lambda.Function { + return this._deleteGraphLambda; + } } + diff --git a/lib/rest-api/lambdas/user/__init__.py b/lib/rest-api/lambdas/user/__init__.py index d54d17b..4f7e3be 100644 --- a/lib/rest-api/lambdas/user/__init__.py +++ b/lib/rest-api/lambdas/user/__init__.py @@ -22,7 +22,8 @@ def contains_duplicates(self, items): return set([item for item in items if items.count(item) > 1]) def get_requesting_cognito_user(self, request): - if ("authorizer" not in request["requestContext"] + if ("requestContext" not in request + or "authorizer" not in request["requestContext"] or "claims" not in request["requestContext"]["authorizer"] or "cognito:username" not in request["requestContext"]["authorizer"]["claims"]): return None diff --git a/lib/workers/worker.ts b/lib/workers/worker.ts index ffd4167..32a83b5 100644 --- a/lib/workers/worker.ts +++ b/lib/workers/worker.ts @@ -23,6 +23,8 @@ import { SqsEventSource } from "@aws-cdk/aws-lambda-event-sources"; export class Worker extends Construct { + private _function: lambda.Function + constructor(scope: Construct, id: string, props: WorkerProps) { super(scope, id); this.createConstructs(id, props); @@ -41,7 +43,7 @@ export class Worker extends Construct { } // Create worker from Lambda - const worker = new lambda.Function(this, id + "Lambda", { + this._function = new lambda.Function(this, id + "Lambda", { runtime: lambda.Runtime.PYTHON_3_7, code: new lambda.AssetCode(path.join(__dirname, "lambdas")), handler: props.handler, @@ -50,19 +52,19 @@ export class Worker extends Construct { environment: environment }); - worker.addEventSource(new SqsEventSource(props.queue, { + this._function.addEventSource(new SqsEventSource(props.queue, { batchSize: props.batchSize })); // Add permisssions to role - worker.addToRolePolicy(new PolicyStatement({ + this._function.addToRolePolicy(new PolicyStatement({ actions: [ "eks:DescribeCluster" ], resources: [ props.cluster.clusterArn ] })); - props.graphTable.grantReadWriteData(worker); + props.graphTable.grantReadWriteData(this._function); - const workerRole = worker.role; + const workerRole = this._function.role; if (workerRole == undefined) { throw new Error("Worker must have an associated IAM Role"); @@ -70,4 +72,8 @@ export class Worker extends Construct { props.cluster.awsAuth.addMastersRole(workerRole); } } + + public get functionArn(): string { + return this._function.functionArn; + } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8171c51..8804f15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,436 +5,455 @@ "requires": true, "dependencies": { "@aws-cdk/assert": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/assert/-/assert-1.46.0.tgz", - "integrity": "sha512-AfOy8P1RBj2eV8cHPYMa2gS0Esnk35eddTnOrhmNkgHLTAo+sgg4+CAdAsdeZ1VzLcwGH+Vb0tJ/6TSAweTRzg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/assert/-/assert-1.54.0.tgz", + "integrity": "sha512-JJ5LXeKGzZzP3epMQQlpVGxNhoj+wsqWZehdsHyciRX+ZUeX3xz37EA3zoDEI3aq/yzN+MVvbl3LjAnfsdrQaw==", "dev": true, "requires": { - "@aws-cdk/cloud-assembly-schema": "1.46.0", - "@aws-cdk/cloudformation-diff": "1.46.0", - "@aws-cdk/core": "1.46.0", - "@aws-cdk/cx-api": "1.46.0", + "@aws-cdk/cloud-assembly-schema": "1.54.0", + "@aws-cdk/cloudformation-diff": "1.54.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/cx-api": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/assets": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/assets/-/assets-1.46.0.tgz", - "integrity": "sha512-VMsPhDv3VceObgqiERKIsJL/B9R0R067KhsZZVdESxbox/lgehkDSkXJdjZI3eRoBKFv84kI4btz309m9xOFCw==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/assets/-/assets-1.54.0.tgz", + "integrity": "sha512-p8MKp2xw/Qyf9613Kf51sf2hv9Sd5bU7bOtQCBmSJ4zUpVSOIPn4HMwpIeU+dQwy8Oo+RrrtryEbn0XFn6W+jg==", "requires": { - "@aws-cdk/core": "1.46.0", - "@aws-cdk/cx-api": "1.46.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/cx-api": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-apigateway": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-apigateway/-/aws-apigateway-1.46.0.tgz", - "integrity": "sha512-ih2DnNJKRdHb5D+s+XHF1fCjEoMTrasT1Ki+nmH9EXkF957iwIjQo8skPgzlEm8+Ljo7Cj9d1hx7gMTk+3Kt1w==", - "requires": { - "@aws-cdk/assets": "1.46.0", - "@aws-cdk/aws-certificatemanager": "1.46.0", - "@aws-cdk/aws-ec2": "1.46.0", - "@aws-cdk/aws-elasticloadbalancingv2": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-lambda": "1.46.0", - "@aws-cdk/aws-logs": "1.46.0", - "@aws-cdk/aws-s3": "1.46.0", - "@aws-cdk/aws-s3-assets": "1.46.0", - "@aws-cdk/core": "1.46.0", - "@aws-cdk/cx-api": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-apigateway/-/aws-apigateway-1.54.0.tgz", + "integrity": "sha512-dRcV9pZC68jOtP/jxIl7WmtT24aeeoJiqhSCBhT9UB3qdjK1jOmMsBP41zoPGTcvmFebDoF8Xd0mxJF4iVWVtQ==", + "requires": { + "@aws-cdk/assets": "1.54.0", + "@aws-cdk/aws-certificatemanager": "1.54.0", + "@aws-cdk/aws-ec2": "1.54.0", + "@aws-cdk/aws-elasticloadbalancingv2": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-lambda": "1.54.0", + "@aws-cdk/aws-logs": "1.54.0", + "@aws-cdk/aws-s3": "1.54.0", + "@aws-cdk/aws-s3-assets": "1.54.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/cx-api": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-applicationautoscaling": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-applicationautoscaling/-/aws-applicationautoscaling-1.46.0.tgz", - "integrity": "sha512-VyHs8+gxrTkM3yj9XhA4PeXesucEvPEgY40sgRlq0fzxVEbfkqHfXDIyTvmR2loc3cbnioVvdds0CbrazJ1u8A==", - "requires": { - "@aws-cdk/aws-autoscaling-common": "1.46.0", - "@aws-cdk/aws-cloudwatch": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-applicationautoscaling/-/aws-applicationautoscaling-1.54.0.tgz", + "integrity": "sha512-KhXpgsXB4TGtkvxH4ilsoWHZgUY/ADR9hNnoipYepEt/zlhGyDzLr/O0vdehhBY4aBISQSK9EMMHRALftMKxwQ==", + "requires": { + "@aws-cdk/aws-autoscaling-common": "1.54.0", + "@aws-cdk/aws-cloudwatch": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-autoscaling": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-autoscaling/-/aws-autoscaling-1.46.0.tgz", - "integrity": "sha512-NqpgfbqBYVZnBFTBSvIxxUjfoKzYh19tL1G34S2tilUM2dntS9jL8uRWYK9Rqb0gPVXAZhpYHCHLgZnIb4GXDA==", - "requires": { - "@aws-cdk/aws-autoscaling-common": "1.46.0", - "@aws-cdk/aws-cloudwatch": "1.46.0", - "@aws-cdk/aws-ec2": "1.46.0", - "@aws-cdk/aws-elasticloadbalancing": "1.46.0", - "@aws-cdk/aws-elasticloadbalancingv2": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-sns": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-autoscaling/-/aws-autoscaling-1.54.0.tgz", + "integrity": "sha512-rjMDKxbg1iVXJO+cJw+jJ3tfEVoxZ1eMpnOARL4uTn6KmOJrZ8IMPsUNqRKFSRCnqzDTPZhVFLX0Ao8Nq+vMfw==", + "requires": { + "@aws-cdk/aws-autoscaling-common": "1.54.0", + "@aws-cdk/aws-cloudwatch": "1.54.0", + "@aws-cdk/aws-ec2": "1.54.0", + "@aws-cdk/aws-elasticloadbalancing": "1.54.0", + "@aws-cdk/aws-elasticloadbalancingv2": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-sns": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-autoscaling-common": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-autoscaling-common/-/aws-autoscaling-common-1.46.0.tgz", - "integrity": "sha512-XQwDvmt/f2KEBblaIGQ/TZH+o5Sg09pYaSz5iouiQf3LEut2Vk9A+VlQeFACNcVWYbHkeIjm2YjdUQkxNYsokQ==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-autoscaling-common/-/aws-autoscaling-common-1.54.0.tgz", + "integrity": "sha512-snCx7u0yBlsnmPH4pzefJh7MZZzMhTWjESqTXO0GpKTVRu4oaVqMeG/s1hnD5Sv3ThEMVVMyQncnNdgMSGgvkA==", "requires": { - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/core": "1.46.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-certificatemanager": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-certificatemanager/-/aws-certificatemanager-1.46.0.tgz", - "integrity": "sha512-hPPX439EXE0tIThMFArNtgUoEbh0tIg6NMmyw+zm6IKQTKj14EQU3bt0BtDFiNsNmNdoDRmu0knk4dZiDKm2gQ==", - "requires": { - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-lambda": "1.46.0", - "@aws-cdk/aws-route53": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-certificatemanager/-/aws-certificatemanager-1.54.0.tgz", + "integrity": "sha512-5AGIzpdAdgUMBDBmrFlmB0fnCLZSEM1za0Ure1FwMK0GjB0tYO2IYQqn438L7rXB7IG9HN4T7pu7GcfE7Q4pMw==", + "requires": { + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-lambda": "1.54.0", + "@aws-cdk/aws-route53": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-cloudformation": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudformation/-/aws-cloudformation-1.46.0.tgz", - "integrity": "sha512-1tZiUUdRUiZck8oGJQfgqzNBA2pkLhscnltExtOgM+AQP79FuDDe8V6Iy8inyVwXWxSenKM9GR0FF87RJNjiAQ==", - "requires": { - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-lambda": "1.46.0", - "@aws-cdk/aws-s3": "1.46.0", - "@aws-cdk/aws-sns": "1.46.0", - "@aws-cdk/core": "1.46.0", - "@aws-cdk/cx-api": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudformation/-/aws-cloudformation-1.54.0.tgz", + "integrity": "sha512-YTW0dPiIUY4wptKlQTFxAPWFLA8Fz8/fa22YCcsLE5ftdGStgSJkrikrqOKmx8eSSZ6OQGGhouRSYV1eqPioxQ==", + "requires": { + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-lambda": "1.54.0", + "@aws-cdk/aws-s3": "1.54.0", + "@aws-cdk/aws-sns": "1.54.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/cx-api": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-cloudwatch": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudwatch/-/aws-cloudwatch-1.46.0.tgz", - "integrity": "sha512-rIv2NZhkGEmrqzWegBqedaHN/07s9XUaOuRvqG6VoK3Dv1VeL+MpAL7zArWb3KueyAQuPQmdeGXSUENwZ/q9SA==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudwatch/-/aws-cloudwatch-1.54.0.tgz", + "integrity": "sha512-+L+tp+XyPqiFBy3P6osOgqqCV/155phcGtZ660BCL5I5ykE08Q9/n6yA3wRh2BU7xZhH5zTYnJ6cIuRmIFcOWw==", "requires": { - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/core": "1.46.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, + "@aws-cdk/aws-codeguruprofiler": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-codeguruprofiler/-/aws-codeguruprofiler-1.54.0.tgz", + "integrity": "sha512-VQIEjaliup+mtM+JERZuREtB8EcHJ5/zfH3bFUTeiUKvheXAIppGh9K8lq/z/PMHjsac6sH+XzozMdHj4Oe8bQ==", + "requires": { + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/core": "1.54.0" + } + }, "@aws-cdk/aws-cognito": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cognito/-/aws-cognito-1.46.0.tgz", - "integrity": "sha512-O+tJKXuCyOPm2eIqHLb+/vaj+XBFv7JrisAMazkJAf8wsDCbLySUhEg3hlUn3xgs36y1uJqZnVktClkhtJWMdQ==", - "requires": { - "@aws-cdk/aws-certificatemanager": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-lambda": "1.46.0", - "@aws-cdk/core": "1.46.0", - "@aws-cdk/custom-resources": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cognito/-/aws-cognito-1.54.0.tgz", + "integrity": "sha512-/FEI2zMcoTNACOJ5Y6jDy88lFnxPU2uXVfGISJP4fx3Z64+JmdUWTfDY/Xa5Kk+Ev2n8EddIVsJ1Ah+aVLrb7Q==", + "requires": { + "@aws-cdk/aws-certificatemanager": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-lambda": "1.54.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/custom-resources": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-dynamodb": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-dynamodb/-/aws-dynamodb-1.46.0.tgz", - "integrity": "sha512-jFGANrAHrMMKqYn63DEX4acAz7194yYgiCKkBG8YtXUkvzo/ioSC1wMTRs5W9jEj1vrLz09o6U0hs1a1Snm9Rw==", - "requires": { - "@aws-cdk/aws-applicationautoscaling": "1.46.0", - "@aws-cdk/aws-cloudwatch": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-kms": "1.46.0", - "@aws-cdk/aws-lambda": "1.46.0", - "@aws-cdk/core": "1.46.0", - "@aws-cdk/custom-resources": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-dynamodb/-/aws-dynamodb-1.54.0.tgz", + "integrity": "sha512-3BJf41r3Ncy0et1fyATVUnDnOHeOYaKAY/OOpo+5a9g59DCgUNp09XoTBFemXRwOKPOO1IDV8fX6CTmg8XXblA==", + "requires": { + "@aws-cdk/aws-applicationautoscaling": "1.54.0", + "@aws-cdk/aws-cloudwatch": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-kms": "1.54.0", + "@aws-cdk/aws-lambda": "1.54.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/custom-resources": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-ec2": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-ec2/-/aws-ec2-1.46.0.tgz", - "integrity": "sha512-P44KtG/0b5CfC4aDIQMCq356wnuVaLxZ4DR2a1jrQdDaErL7oLslysL3gzQcsfxgpMNM7IzH5pDOp95N9X66dA==", - "requires": { - "@aws-cdk/aws-cloudwatch": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-logs": "1.46.0", - "@aws-cdk/aws-s3": "1.46.0", - "@aws-cdk/aws-ssm": "1.46.0", - "@aws-cdk/cloud-assembly-schema": "1.46.0", - "@aws-cdk/core": "1.46.0", - "@aws-cdk/cx-api": "1.46.0", - "@aws-cdk/region-info": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-ec2/-/aws-ec2-1.54.0.tgz", + "integrity": "sha512-MZXDHf8jj//BBNQx+EwuocV5WH2njFQqzo2cF9tpTPPjd30prD5D8FCPi3RIdXafKdpQvTzor4cYPbYFKbdU+A==", + "requires": { + "@aws-cdk/aws-cloudwatch": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-kms": "1.54.0", + "@aws-cdk/aws-logs": "1.54.0", + "@aws-cdk/aws-s3": "1.54.0", + "@aws-cdk/aws-ssm": "1.54.0", + "@aws-cdk/cloud-assembly-schema": "1.54.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/cx-api": "1.54.0", + "@aws-cdk/region-info": "1.54.0", "constructs": "^3.0.2" } }, - "@aws-cdk/aws-eks": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-eks/-/aws-eks-1.46.0.tgz", - "integrity": "sha512-2hhrNFnaqJsjzNGK9PxhDwEgrs0URTZsJjCCO9VnFVLZmGvZXKeG5ETbdJF3XNCKrCQf7T8u975CIOe9/KWxZA==", - "requires": { - "@aws-cdk/aws-autoscaling": "1.46.0", - "@aws-cdk/aws-ec2": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-lambda": "1.46.0", - "@aws-cdk/aws-ssm": "1.46.0", - "@aws-cdk/core": "1.46.0", - "@aws-cdk/custom-resources": "1.46.0", + "@aws-cdk/aws-efs": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-efs/-/aws-efs-1.54.0.tgz", + "integrity": "sha512-kHeH5cGonYrE1vwStnB1SwAX3XDZYDOiBlGxPz5s7x8TCeMdjRWhHP5Qow+h5Rt6UOocZJ+QV8G4fbXU2yZC1g==", + "requires": { + "@aws-cdk/aws-ec2": "1.54.0", + "@aws-cdk/aws-kms": "1.54.0", + "@aws-cdk/cloud-assembly-schema": "1.54.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/cx-api": "1.54.0", "constructs": "^3.0.2" } }, + "@aws-cdk/aws-eks": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-eks/-/aws-eks-1.54.0.tgz", + "integrity": "sha512-WwGlp1yoduV7jECHBkoWYD1EXD+xJRL+uqxzvGRpPponV936XuYP88eOxFoXJqvsSyX6k+61WTl8Vz+wxmKjSQ==", + "requires": { + "@aws-cdk/aws-autoscaling": "1.54.0", + "@aws-cdk/aws-ec2": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-lambda": "1.54.0", + "@aws-cdk/aws-ssm": "1.54.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/custom-resources": "1.54.0", + "constructs": "^3.0.2", + "yaml": "1.10.0" + }, + "dependencies": { + "yaml": { + "version": "1.10.0", + "bundled": true + } + } + }, "@aws-cdk/aws-elasticloadbalancing": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-elasticloadbalancing/-/aws-elasticloadbalancing-1.46.0.tgz", - "integrity": "sha512-kGhfkNJT2O3OBE3J1sF96z7lfvoMRl/3DPDnp/pZyhXbOwoS3p6hVHa12kxWhVpaqnfJQ16NShuZ7cHdsZ51rQ==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-elasticloadbalancing/-/aws-elasticloadbalancing-1.54.0.tgz", + "integrity": "sha512-B1f8DH+9TVfAWq0ZDIpIPsoOkSavTuuWI6dF3fWGCDEHFij08frutlcXROC5iHaY4aS+TTrmUC6Iu1CmUEXAtw==", "requires": { - "@aws-cdk/aws-ec2": "1.46.0", - "@aws-cdk/core": "1.46.0", + "@aws-cdk/aws-ec2": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-elasticloadbalancingv2": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-elasticloadbalancingv2/-/aws-elasticloadbalancingv2-1.46.0.tgz", - "integrity": "sha512-bHS1h12E5NFf1cBbKGgKKXLg2OM8M9NZKyYukFeGfWZOuGwtEsVOKmQTWIy20mLI4UDnl0whR7ZEW+gMRI1/dA==", - "requires": { - "@aws-cdk/aws-certificatemanager": "1.46.0", - "@aws-cdk/aws-cloudwatch": "1.46.0", - "@aws-cdk/aws-ec2": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-lambda": "1.46.0", - "@aws-cdk/aws-s3": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-elasticloadbalancingv2/-/aws-elasticloadbalancingv2-1.54.0.tgz", + "integrity": "sha512-b/SMpM97C3bQCAAdANW90JfIxfZ1FCZn15v7bwoDyUtoO6M+HmnYfdd8wHCbHnKj/gE0UAq79x83/wwVgf8Ukw==", + "requires": { + "@aws-cdk/aws-certificatemanager": "1.54.0", + "@aws-cdk/aws-cloudwatch": "1.54.0", + "@aws-cdk/aws-ec2": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-lambda": "1.54.0", + "@aws-cdk/aws-s3": "1.54.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/region-info": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-events": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-events/-/aws-events-1.46.0.tgz", - "integrity": "sha512-aRwPiTGMT/wiBgYZa9MADull16Xwqt2UkBzDN55qPNwgbA5ZFxFivQJSyAxs4MoA/vD/JSx1sPVPCutpukkGjA==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-events/-/aws-events-1.54.0.tgz", + "integrity": "sha512-l45QoTocCC182lXAf2uozpqU7Y+P7NKtUQ3xVXV+9f9Aldjq6vPDml8MxyWGn5liKTcqRzekL4gOoQbUG8Effg==", "requires": { - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/core": "1.46.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-iam": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-iam/-/aws-iam-1.46.0.tgz", - "integrity": "sha512-D400DpnQ8u+Iz2SauL/16P13g4GT1A38rPtbs4S7I5Y9ZVlHkCf0fbwBwKrZt+Tn/GtHj4oqkchWBd9fd9TAPA==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-iam/-/aws-iam-1.54.0.tgz", + "integrity": "sha512-oS2W85oDITpaLqIWAGM+ADBW+csRDkz2n46dkdfoEq0m2Y5ZQESvXi7J9eWjcDu1ofDDDVDl3snbWJTnfW3+Hg==", "requires": { - "@aws-cdk/core": "1.46.0", - "@aws-cdk/region-info": "1.46.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/region-info": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-kinesis": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-kinesis/-/aws-kinesis-1.46.0.tgz", - "integrity": "sha512-IkQ3IEh7kPpoaku96j7tfKFfR/q0LYWBbVnwkxYY96C3LjidKYexCBwRqM+5uvIsXr7tyx3wsQMfly1H/4ArMA==", - "requires": { - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-kms": "1.46.0", - "@aws-cdk/aws-logs": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-kinesis/-/aws-kinesis-1.54.0.tgz", + "integrity": "sha512-plTQh+FjyvjUxWIXNaP6aOphY82O+quZquC0/OgSE5ZEaGOx1qD+dL4hXqQePQevwLTOmk82/2di2TbNGTsyRw==", + "requires": { + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-kms": "1.54.0", + "@aws-cdk/aws-logs": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-kms": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-kms/-/aws-kms-1.46.0.tgz", - "integrity": "sha512-+lTJ7YsFh4gy6xLb/WvG07gIERPJP1FEODkqHl0NaZ5fxTNFgCV7Y883jsmaof+Hpj4jJRfFv1D9HHwvs9DZIQ==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-kms/-/aws-kms-1.54.0.tgz", + "integrity": "sha512-9BL6bPfj0KPtVTp7sm2oplTUlVp3DqWz/NPd9xQcQEW4VPddYTxP+a/hRDSjTYQtE84Gt2+EXT1pjVBcqNpw2A==", "requires": { - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/core": "1.46.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-lambda": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-lambda/-/aws-lambda-1.46.0.tgz", - "integrity": "sha512-REgRPPbJt/UD7n2fRbpJHK17fMDA0PED+gCvlctEw2chIUsJx601FDHG74vVKtm0R7UEM2svADzNm2Tv5YnVTw==", - "requires": { - "@aws-cdk/aws-cloudwatch": "1.46.0", - "@aws-cdk/aws-ec2": "1.46.0", - "@aws-cdk/aws-events": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-logs": "1.46.0", - "@aws-cdk/aws-s3": "1.46.0", - "@aws-cdk/aws-s3-assets": "1.46.0", - "@aws-cdk/aws-sqs": "1.46.0", - "@aws-cdk/core": "1.46.0", - "@aws-cdk/cx-api": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-lambda/-/aws-lambda-1.54.0.tgz", + "integrity": "sha512-YF/s/2aDTtLSCjRBdH93DrrNdk0HoxSbanIxh25hddbuGMm7se+wS4qtmyyWL/05J/o/BGbWQWOLMUGj1gmaGQ==", + "requires": { + "@aws-cdk/aws-cloudwatch": "1.54.0", + "@aws-cdk/aws-codeguruprofiler": "1.54.0", + "@aws-cdk/aws-ec2": "1.54.0", + "@aws-cdk/aws-efs": "1.54.0", + "@aws-cdk/aws-events": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-logs": "1.54.0", + "@aws-cdk/aws-s3": "1.54.0", + "@aws-cdk/aws-s3-assets": "1.54.0", + "@aws-cdk/aws-sqs": "1.54.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/cx-api": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-lambda-event-sources": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-lambda-event-sources/-/aws-lambda-event-sources-1.46.0.tgz", - "integrity": "sha512-YXKzokwQ2nLeLbAyfP/QxuBGxRZKjQDjv0gd9Ep2jO43JrSx1OkKBqkUXEgrgrxvMpbAGkMq06OdlXLsBGuqRg==", - "requires": { - "@aws-cdk/aws-apigateway": "1.46.0", - "@aws-cdk/aws-dynamodb": "1.46.0", - "@aws-cdk/aws-events": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-kinesis": "1.46.0", - "@aws-cdk/aws-lambda": "1.46.0", - "@aws-cdk/aws-s3": "1.46.0", - "@aws-cdk/aws-s3-notifications": "1.46.0", - "@aws-cdk/aws-sns": "1.46.0", - "@aws-cdk/aws-sns-subscriptions": "1.46.0", - "@aws-cdk/aws-sqs": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-lambda-event-sources/-/aws-lambda-event-sources-1.54.0.tgz", + "integrity": "sha512-/xyJOP/Uh/9818TDF9FRldltT0sFVjroZdRt+PmwUwG9YW5EHNfVPG6g5gt3GRqDtFXWGKA5TsXz8YTYOO7Uzg==", + "requires": { + "@aws-cdk/aws-apigateway": "1.54.0", + "@aws-cdk/aws-dynamodb": "1.54.0", + "@aws-cdk/aws-events": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-kinesis": "1.54.0", + "@aws-cdk/aws-lambda": "1.54.0", + "@aws-cdk/aws-s3": "1.54.0", + "@aws-cdk/aws-s3-notifications": "1.54.0", + "@aws-cdk/aws-sns": "1.54.0", + "@aws-cdk/aws-sns-subscriptions": "1.54.0", + "@aws-cdk/aws-sqs": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-logs": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-logs/-/aws-logs-1.46.0.tgz", - "integrity": "sha512-JcP7AJXQfC+uoEJhyZ1Y6uh1sIF9swN1q/NG4bNij3jaU7tlbZj7iHgy+k2XwH+1aNA96CeRyKCD6NSgo6w/kQ==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-logs/-/aws-logs-1.54.0.tgz", + "integrity": "sha512-VjEzm0HD8rwOsH3qe4ZA37eMjQbO0ZxmyMgXtysET9hYs+i0Iwr5cZ/T/FHSY/OBc0BOHJXi/Qx7W8/WARH9eQ==", "requires": { - "@aws-cdk/aws-cloudwatch": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/core": "1.46.0", + "@aws-cdk/aws-cloudwatch": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-route53": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-route53/-/aws-route53-1.46.0.tgz", - "integrity": "sha512-YYc7sxCCf0V7Ma478dHJk2+0DmOJa9wgURdKku4cMm3G4qQmRZ2GG+z6Hsf8DBLa6wg73fHxarQGTFqBSAmnHg==", - "requires": { - "@aws-cdk/aws-ec2": "1.46.0", - "@aws-cdk/aws-logs": "1.46.0", - "@aws-cdk/cloud-assembly-schema": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-route53/-/aws-route53-1.54.0.tgz", + "integrity": "sha512-8zJk4ljSWpf0HXUtPw+aFB0BZCzewybAtKvd85kmJvKGk4GZLAJdz5EWRchksznxpwO30J+5T7DC4Q/8QuN1Pw==", + "requires": { + "@aws-cdk/aws-ec2": "1.54.0", + "@aws-cdk/aws-logs": "1.54.0", + "@aws-cdk/cloud-assembly-schema": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-s3": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3/-/aws-s3-1.46.0.tgz", - "integrity": "sha512-v9R6AK/SQHqwMwiXvou2Ww5RrqUghlCJyW4cmn2rrbwbgYRMbMLgYGcrYb3OSxenjYP68cPtw+UB+/zjsVjHMg==", - "requires": { - "@aws-cdk/aws-events": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-kms": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3/-/aws-s3-1.54.0.tgz", + "integrity": "sha512-dMcHtvoAyCTuh74fPY7Y7R+PW3T+vyUZNpDKgaj4I//Q96ybb3YcXyrptIXMNDAWVPL8GNIcEAGfpY5wYLE0qQ==", + "requires": { + "@aws-cdk/aws-events": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-kms": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-s3-assets": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3-assets/-/aws-s3-assets-1.46.0.tgz", - "integrity": "sha512-JdQkwBSgEJNmSQlBphsQ3AgflpbXnTibGcf6xFGXa/0+Qgc12GUA6KX3qdcd29CQhej3tYprK6Bne3Y2jjZT3g==", - "requires": { - "@aws-cdk/assets": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-kms": "1.46.0", - "@aws-cdk/aws-s3": "1.46.0", - "@aws-cdk/core": "1.46.0", - "@aws-cdk/cx-api": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3-assets/-/aws-s3-assets-1.54.0.tgz", + "integrity": "sha512-IMGX3mqQyF4OurYh+Pu+HeImlvcOSaeq9WfiH31NzWvHNiR7Zuh2MvRG7r1lo9VNtS4Ttk1nXJFBe2W0yTFDrA==", + "requires": { + "@aws-cdk/assets": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-kms": "1.54.0", + "@aws-cdk/aws-s3": "1.54.0", + "@aws-cdk/core": "1.54.0", + "@aws-cdk/cx-api": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-s3-notifications": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3-notifications/-/aws-s3-notifications-1.46.0.tgz", - "integrity": "sha512-SgwA1IOEaqMyfz/ijutqUZLPzDo+HggN+xVuRFxO9fgMiZYCJ92HYudVDYoxX7ce2bQuL5fWndCd915j04k4GQ==", - "requires": { - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-lambda": "1.46.0", - "@aws-cdk/aws-s3": "1.46.0", - "@aws-cdk/aws-sns": "1.46.0", - "@aws-cdk/aws-sqs": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3-notifications/-/aws-s3-notifications-1.54.0.tgz", + "integrity": "sha512-9JnqM3b9itcG8kvOFV59/UsRRoIH7h/fBHaL+e/+D6d70+zkmXEn8157NuDWotoU8+of68zaji6rq4fMy98sVw==", + "requires": { + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-lambda": "1.54.0", + "@aws-cdk/aws-s3": "1.54.0", + "@aws-cdk/aws-sns": "1.54.0", + "@aws-cdk/aws-sqs": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-sam": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sam/-/aws-sam-1.46.0.tgz", - "integrity": "sha512-6jU0CPMfJ/6dWEEdg8ulw9davQ0I/0bh29pyWmq4kIoCLmOsnkz+QYohKc1BcREU9Q1DHBeXE6r7+PP6YrQuBw==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sam/-/aws-sam-1.54.0.tgz", + "integrity": "sha512-brICLV616UiXTNOCvvlC7enwvcKaiZLtT7VEPQTf3eiJp9VDs4RfNSMefm5wIkfmHLWPHvRlexPFaZb/PeU7UA==", "requires": { - "@aws-cdk/core": "1.46.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-sns": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sns/-/aws-sns-1.46.0.tgz", - "integrity": "sha512-9rhuWQFaaMLzVGC9RkpKv6dw1qY+ARNeHyqBZUDCzO9jvo/eO5LzPOhkQPIiZi/vL1T2JEBvo26ySj/u520pbA==", - "requires": { - "@aws-cdk/aws-cloudwatch": "1.46.0", - "@aws-cdk/aws-events": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-kms": "1.46.0", - "@aws-cdk/aws-sqs": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sns/-/aws-sns-1.54.0.tgz", + "integrity": "sha512-bmP7Z0i26Yq1Ix0yaz+tjJaersLZMOj/WDhfkpaucyPlTCj/hV2BQSGUMWgl3QgadapUPZVDv9DBiqvjLlTPMA==", + "requires": { + "@aws-cdk/aws-cloudwatch": "1.54.0", + "@aws-cdk/aws-events": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-kms": "1.54.0", + "@aws-cdk/aws-sqs": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-sns-subscriptions": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sns-subscriptions/-/aws-sns-subscriptions-1.46.0.tgz", - "integrity": "sha512-QzkgFYQ82Lpt7XK1iX4CcygRvttRB0xC2phmTrs7LazZ5veb4KmUlngU8x8gEJ1dD1Evi5R6WmTN6UAac9Et1w==", - "requires": { - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-lambda": "1.46.0", - "@aws-cdk/aws-sns": "1.46.0", - "@aws-cdk/aws-sqs": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sns-subscriptions/-/aws-sns-subscriptions-1.54.0.tgz", + "integrity": "sha512-iizYWPCRbRcPu9IvZTprybFAyBYz/bxJ9qY8YNg13zei4DvmE0KSK7To6ljSY0xXiFjJt4BNVI8BsYrXYOMYrg==", + "requires": { + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-lambda": "1.54.0", + "@aws-cdk/aws-sns": "1.54.0", + "@aws-cdk/aws-sqs": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-sqs": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sqs/-/aws-sqs-1.46.0.tgz", - "integrity": "sha512-HG7rClnwzJJYbNVzpO4JL7F4V/iM/0DTmL+q4QwzsWFIV9CPPMMC3FsoPCz4H4+Lj2/bXeU+7kcI9qJzvqmejg==", - "requires": { - "@aws-cdk/aws-cloudwatch": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-kms": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sqs/-/aws-sqs-1.54.0.tgz", + "integrity": "sha512-ykW2CWbIgvKqUGjrhMCCqOYJ2HUYijTtRcRFdrPD0BrSixcwWW4LW/PpJhDgOjfAw20TS1k3ivYl4pcfg2XWQw==", + "requires": { + "@aws-cdk/aws-cloudwatch": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-kms": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-ssm": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-ssm/-/aws-ssm-1.46.0.tgz", - "integrity": "sha512-Y2pNW7wA6+916kdNXG1kgJvDiBG2nzHKyVZUTort3p8P51GeoKVx+sKmD7Unp3JW64tYQUAfKs+XP0PWV4ZwCA==", - "requires": { - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-kms": "1.46.0", - "@aws-cdk/cloud-assembly-schema": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-ssm/-/aws-ssm-1.54.0.tgz", + "integrity": "sha512-i66zvnOpEpTONI/kKeH4rBg9Lr3w5Z9N+dPJRkqZRqv/f3n2vegPzYX1o3C2eBuVeOCcbgj8775khJO8PbBlww==", + "requires": { + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-kms": "1.54.0", + "@aws-cdk/cloud-assembly-schema": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, - "@aws-cdk/cdk-assets-schema": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cdk-assets-schema/-/cdk-assets-schema-1.46.0.tgz", - "integrity": "sha512-5YM/WHdfiiXkyN+oqPWIcrU7nQUzEVRmViiN+SGy/NZ6Tj9r30N9YygYMZO8z9sM7r20dOTl+pY9SYrclIeNUQ==", - "requires": { - "semver": "^7.2.2" - }, - "dependencies": { - "semver": { - "version": "7.3.2", - "bundled": true - } - } - }, "@aws-cdk/cfnspec": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cfnspec/-/cfnspec-1.46.0.tgz", - "integrity": "sha512-oc+k7viT1wukeeH+WIbMRn/QXmS/iDGW5lkn0SIVaOKAZDmErlXGMbeenxSgdAvUysUw8gy30VYXQ5LI3JgenQ==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cfnspec/-/cfnspec-1.54.0.tgz", + "integrity": "sha512-0cel4Z9K3MZfMLuzapGN6qKTv15vQkDb/DMGRMJe7P4h/pkJHYbROv/uiWXx+b5uCUQxLjJYCxnJlJbK0tK8wA==", "dev": true, "requires": { "md5": "^2.2.1" } }, "@aws-cdk/cloud-assembly-schema": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.46.0.tgz", - "integrity": "sha512-ji92c9RCMoY426vH1RbLstUOiUYqM84qa4Ww9Nsbn6Aw3qIS1Gz603dNgcIl+rK78SOq6WqlgykSyFAFykqVgA==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.54.0.tgz", + "integrity": "sha512-9E4td0R9N/r1fjQaVdeDxVGFqoJNlXMGt0EM2wwiC5JjJj7MkdPVOO0qcvwPdZuYHZMVr/UFAHFjTQBrj6LCEA==", "requires": { "jsonschema": "^1.2.5", "semver": "^7.2.2" @@ -451,12 +470,12 @@ } }, "@aws-cdk/cloudformation-diff": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloudformation-diff/-/cloudformation-diff-1.46.0.tgz", - "integrity": "sha512-kz8OHZd9B9JwhgtSnivExh1lN9K9TgJaByAUqrzeWY9DZmmkHMQKu3hk9KjA4wVN9W5BTPzOuoIdOfZcAp55xw==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloudformation-diff/-/cloudformation-diff-1.54.0.tgz", + "integrity": "sha512-K97QZS+kPnao/E08mkWCZIdwG4M/WP8Obm1kSvTsH1QADcE5tmmEQNq+G4rVd6J3Xik0wbKtNgfh0iFcUuof/g==", "dev": true, "requires": { - "@aws-cdk/cfnspec": "1.46.0", + "@aws-cdk/cfnspec": "1.54.0", "colors": "^1.4.0", "diff": "^4.0.2", "fast-deep-equal": "^3.1.3", @@ -473,13 +492,12 @@ } }, "@aws-cdk/core": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/core/-/core-1.46.0.tgz", - "integrity": "sha512-PqK0fmt28ExMNXJiAdZU3A0jQeynd3Set+P86Tx9p2t7tI92oArh9lhuBGhoZ0sTjTUD0/ZqX4dYnH7JcFYUAg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/core/-/core-1.54.0.tgz", + "integrity": "sha512-LqHIDudkJp9//7Crfr00l2Z5U+4xkEIiRcEXXNkwEnV2iPzuQErCoDJ+JiQj2kOk3EFbbVpTBPI+6LV1nKSLuw==", "requires": { - "@aws-cdk/cdk-assets-schema": "1.46.0", - "@aws-cdk/cloud-assembly-schema": "1.46.0", - "@aws-cdk/cx-api": "1.46.0", + "@aws-cdk/cloud-assembly-schema": "1.54.0", + "@aws-cdk/cx-api": "1.54.0", "constructs": "^3.0.2", "fs-extra": "^9.0.1", "minimatch": "^3.0.4" @@ -516,7 +534,7 @@ } }, "graceful-fs": { - "version": "4.2.4", + "version": "4.2.3", "bundled": true }, "jsonfile": { @@ -541,25 +559,25 @@ } }, "@aws-cdk/custom-resources": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/custom-resources/-/custom-resources-1.46.0.tgz", - "integrity": "sha512-NJUTvY5o9pBiRSV8Sj1UTmhHCvHGUFjnAbwNl4cZLFqYWF4KYyxEmmQjPMmCIgn2FWySnMEVhVN9FmrvEznaQQ==", - "requires": { - "@aws-cdk/aws-cloudformation": "1.46.0", - "@aws-cdk/aws-iam": "1.46.0", - "@aws-cdk/aws-lambda": "1.46.0", - "@aws-cdk/aws-logs": "1.46.0", - "@aws-cdk/aws-sns": "1.46.0", - "@aws-cdk/core": "1.46.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/custom-resources/-/custom-resources-1.54.0.tgz", + "integrity": "sha512-pTZu5PH3UUX/53fFuIQamidmswaC2JMw97KBXWpFLz1WBPA2Jvb2Y67yiy814HZdz9WdscccIQIcCFh8ms4c7g==", + "requires": { + "@aws-cdk/aws-cloudformation": "1.54.0", + "@aws-cdk/aws-iam": "1.54.0", + "@aws-cdk/aws-lambda": "1.54.0", + "@aws-cdk/aws-logs": "1.54.0", + "@aws-cdk/aws-sns": "1.54.0", + "@aws-cdk/core": "1.54.0", "constructs": "^3.0.2" } }, "@aws-cdk/cx-api": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.46.0.tgz", - "integrity": "sha512-3U4wbywHlYLqWllCNOVGqxDee3N5PxaQHkjpC3v0vhI+LPwcJEW1Vz11k3PMG6FaPcrLrfkAn1y7HSFsPXh8Qg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.54.0.tgz", + "integrity": "sha512-LVskfK1wcL7IhiyY74cQM1JZn4y08IGKvPEldnQVx+OlvB4BDsXRIT4pyRgnGYNWDI5yaQqi+M2D5zqqaLcN+g==", "requires": { - "@aws-cdk/cloud-assembly-schema": "1.46.0", + "@aws-cdk/cloud-assembly-schema": "1.54.0", "semver": "^7.2.2" }, "dependencies": { @@ -570,9 +588,9 @@ } }, "@aws-cdk/region-info": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/region-info/-/region-info-1.46.0.tgz", - "integrity": "sha512-EJYZCi2WVUnjPPlxJko66Vzc3LbAFA4dkdC9TDV+IxuBqGVts/7oHJf/Ca9RVglkbOkov7p78OTBWmONqncDEg==" + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/region-info/-/region-info-1.54.0.tgz", + "integrity": "sha512-nEhNfDRY7AZOt8ebRdGjVPBRu1X25UTqsHbzgPBk+4Ov4CfgAi06onoacGgyTw4WzLikAdVTtRHJULmI158clg==" }, "@babel/code-frame": { "version": "7.10.1", @@ -1946,20 +1964,19 @@ "dev": true }, "aws-cdk": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-1.46.0.tgz", - "integrity": "sha512-D0yUKA2TmlFAy00fezPjokgKhlDr8+axUY2Z1NR3U9J6ZvOnzTRWBab8rVPGoKZJ4WkQy4s637ahUx2C3mg+Kw==", - "dev": true, - "requires": { - "@aws-cdk/cdk-assets-schema": "1.46.0", - "@aws-cdk/cloud-assembly-schema": "1.46.0", - "@aws-cdk/cloudformation-diff": "1.46.0", - "@aws-cdk/cx-api": "1.46.0", - "@aws-cdk/region-info": "1.46.0", - "archiver": "^4.0.1", - "aws-sdk": "^2.699.0", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-1.54.0.tgz", + "integrity": "sha512-nHtf/c4bMShNvnkyV2Jpvq7v6fGhfELsdABChXX3Yjal37Jso8iOq3LJV1hxusad9Dt+79cNKc8P2axNjLuGdQ==", + "dev": true, + "requires": { + "@aws-cdk/cloud-assembly-schema": "1.54.0", + "@aws-cdk/cloudformation-diff": "1.54.0", + "@aws-cdk/cx-api": "1.54.0", + "@aws-cdk/region-info": "1.54.0", + "archiver": "^4.0.2", + "aws-sdk": "^2.715.0", "camelcase": "^6.0.0", - "cdk-assets": "1.46.0", + "cdk-assets": "1.54.0", "colors": "^1.4.0", "decamelize": "^4.0.0", "fs-extra": "^9.0.1", @@ -1971,34 +1988,25 @@ "semver": "^7.2.2", "source-map-support": "^0.5.19", "table": "^5.4.6", - "uuid": "^8.1.0", + "uuid": "^8.2.0", "wrap-ansi": "^7.0.0", "yaml": "^1.10.0", "yargs": "^15.3.1" }, "dependencies": { - "@aws-cdk/cdk-assets-schema": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cdk-assets-schema/-/cdk-assets-schema-1.46.0.tgz", - "integrity": "sha512-5YM/WHdfiiXkyN+oqPWIcrU7nQUzEVRmViiN+SGy/NZ6Tj9r30N9YygYMZO8z9sM7r20dOTl+pY9SYrclIeNUQ==", - "dev": true, - "requires": { - "semver": "^7.2.2" - } - }, "@aws-cdk/cfnspec": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cfnspec/-/cfnspec-1.46.0.tgz", - "integrity": "sha512-oc+k7viT1wukeeH+WIbMRn/QXmS/iDGW5lkn0SIVaOKAZDmErlXGMbeenxSgdAvUysUw8gy30VYXQ5LI3JgenQ==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cfnspec/-/cfnspec-1.54.0.tgz", + "integrity": "sha512-0cel4Z9K3MZfMLuzapGN6qKTv15vQkDb/DMGRMJe7P4h/pkJHYbROv/uiWXx+b5uCUQxLjJYCxnJlJbK0tK8wA==", "dev": true, "requires": { "md5": "^2.2.1" } }, "@aws-cdk/cloud-assembly-schema": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.46.0.tgz", - "integrity": "sha512-ji92c9RCMoY426vH1RbLstUOiUYqM84qa4Ww9Nsbn6Aw3qIS1Gz603dNgcIl+rK78SOq6WqlgykSyFAFykqVgA==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.54.0.tgz", + "integrity": "sha512-9E4td0R9N/r1fjQaVdeDxVGFqoJNlXMGt0EM2wwiC5JjJj7MkdPVOO0qcvwPdZuYHZMVr/UFAHFjTQBrj6LCEA==", "dev": true, "requires": { "jsonschema": "^1.2.5", @@ -2006,12 +2014,12 @@ } }, "@aws-cdk/cloudformation-diff": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloudformation-diff/-/cloudformation-diff-1.46.0.tgz", - "integrity": "sha512-kz8OHZd9B9JwhgtSnivExh1lN9K9TgJaByAUqrzeWY9DZmmkHMQKu3hk9KjA4wVN9W5BTPzOuoIdOfZcAp55xw==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloudformation-diff/-/cloudformation-diff-1.54.0.tgz", + "integrity": "sha512-K97QZS+kPnao/E08mkWCZIdwG4M/WP8Obm1kSvTsH1QADcE5tmmEQNq+G4rVd6J3Xik0wbKtNgfh0iFcUuof/g==", "dev": true, "requires": { - "@aws-cdk/cfnspec": "1.46.0", + "@aws-cdk/cfnspec": "1.54.0", "colors": "^1.4.0", "diff": "^4.0.2", "fast-deep-equal": "^3.1.3", @@ -2020,19 +2028,19 @@ } }, "@aws-cdk/cx-api": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.46.0.tgz", - "integrity": "sha512-3U4wbywHlYLqWllCNOVGqxDee3N5PxaQHkjpC3v0vhI+LPwcJEW1Vz11k3PMG6FaPcrLrfkAn1y7HSFsPXh8Qg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.54.0.tgz", + "integrity": "sha512-LVskfK1wcL7IhiyY74cQM1JZn4y08IGKvPEldnQVx+OlvB4BDsXRIT4pyRgnGYNWDI5yaQqi+M2D5zqqaLcN+g==", "dev": true, "requires": { - "@aws-cdk/cloud-assembly-schema": "1.46.0", + "@aws-cdk/cloud-assembly-schema": "1.54.0", "semver": "^7.2.2" } }, "@aws-cdk/region-info": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/region-info/-/region-info-1.46.0.tgz", - "integrity": "sha512-EJYZCi2WVUnjPPlxJko66Vzc3LbAFA4dkdC9TDV+IxuBqGVts/7oHJf/Ca9RVglkbOkov7p78OTBWmONqncDEg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/region-info/-/region-info-1.54.0.tgz", + "integrity": "sha512-nEhNfDRY7AZOt8ebRdGjVPBRu1X25UTqsHbzgPBk+4Ov4CfgAi06onoacGgyTw4WzLikAdVTtRHJULmI158clg==", "dev": true }, "@types/color-name": { @@ -2060,6 +2068,14 @@ "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "dependencies": { + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + } } }, "ansi-regex": { @@ -2078,13 +2094,13 @@ } }, "archiver": { - "version": "4.0.1", - "resolved": "https://registry.yarnpkg.com/archiver/-/archiver-4.0.1.tgz#3f722b121777e361ca9fad374ecda38e77e63c7f", - "integrity": "sha512-/YV1pU4Nhpf/rJArM23W6GTUjT0l++VbjykrCRua1TSXrn+yM8Qs7XvtwSiRse0iCe49EPNf7ktXnPsWuSb91Q==", + "version": "4.0.2", + "resolved": "https://registry.yarnpkg.com/archiver/-/archiver-4.0.2.tgz#43c72865eadb4ddaaa2fb74852527b6a450d927c", + "integrity": "sha512-B9IZjlGwaxF33UN4oPbfBkyA4V1SxNLeIhR1qY8sRXSsbdUkEHrrOvwlYFPx+8uQeCe9M+FG6KgO+imDmQ79CQ==", "dev": true, "requires": { "archiver-utils": "^2.1.0", - "async": "^2.6.3", + "async": "^3.2.0", "buffer-crc32": "^0.2.1", "glob": "^7.1.6", "readable-stream": "^3.6.0", @@ -2151,13 +2167,10 @@ "dev": true }, "async": { - "version": "2.6.3", - "resolved": "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", + "dev": true }, "at-least-node": { "version": "1.0.0", @@ -2166,9 +2179,9 @@ "dev": true }, "aws-sdk": { - "version": "2.699.0", - "resolved": "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.699.0.tgz#e77b6ffa4c860882e2779c060b74fab33d42f554", - "integrity": "sha512-EC431z/+i/cJgOgnDpOJ8Fa6+p7Oo1vIvdm/uJqP9tJX3+pxi/M/tvQavfz4yAlLBFqjQwxa8nrPisby0Mr5MQ==", + "version": "2.715.0", + "resolved": "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.715.0.tgz#b890892098e0a4d9e7189ed341267d4a9a6e856b", + "integrity": "sha512-O6ytb66IXFCowp0Ng2bSPM6v/cZVOhjJWFTR1CG4ieG4IroAaVgB3YQKkfPKA0Cy9B/Ovlsm7B737iuroKsd0w==", "dev": true, "requires": { "buffer": "4.9.2", @@ -2297,15 +2310,15 @@ "dev": true }, "cdk-assets": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/cdk-assets/-/cdk-assets-1.46.0.tgz", - "integrity": "sha512-mirLzi1YoMHR/mfZ9lWOtnuSS6pC/5g5Xfvg4yXxOSjQu32C/ktktQVG30KJZ4zJZAxZjN0G9KJfAXoyQIsbvQ==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/cdk-assets/-/cdk-assets-1.54.0.tgz", + "integrity": "sha512-NI3Aajt/SBO21Pe/H+61403cwDAUq3XKlrJdp8zvf8wz9KdGzZMkoRmpjq7hnKBy5F8FiywMI7e2xpE8Yb8LLQ==", "dev": true, "requires": { - "@aws-cdk/cdk-assets-schema": "1.46.0", - "@aws-cdk/cx-api": "1.46.0", - "archiver": "^4.0.1", - "aws-sdk": "^2.699.0", + "@aws-cdk/cloud-assembly-schema": "1.54.0", + "@aws-cdk/cx-api": "1.54.0", + "archiver": "^4.0.2", + "aws-sdk": "^2.715.0", "glob": "^7.1.6", "yargs": "^15.3.1" }, @@ -3629,9 +3642,9 @@ } }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.3", + "resolved": "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, "heap": { @@ -3824,9 +3837,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash.defaults": { @@ -4413,9 +4426,9 @@ "dev": true }, "uuid": { - "version": "8.1.0", - "resolved": "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d", - "integrity": "sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==", + "version": "8.2.0", + "resolved": "https://registry.yarnpkg.com/uuid/-/uuid-8.2.0.tgz#cb10dd6b118e2dada7d0cd9730ba7417c93d920e", + "integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q==", "dev": true }, "which-module": { @@ -5066,9 +5079,9 @@ "dev": true }, "constructs": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-3.0.3.tgz", - "integrity": "sha512-JrYLpTlz92Un1jxkwoGiOiGoDjzIWtxo64sLC5FD4mQN1H9mAqZNvgxWYWaJIiWUXNkl5L5sO3GFf6peTj7UMQ==" + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-3.0.4.tgz", + "integrity": "sha512-CDvg7gMjphE3DFX4pzkF6j73NREbR8npPFW8Mx/CLRnMR035+Y1o1HrXIsNSss/dq3ZUnNTU9jKyd3fL9EOlfw==" }, "convert-source-map": { "version": "1.7.0", diff --git a/package.json b/package.json index fc35012..be0ac5a 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,12 @@ "cdk": "cdk" }, "devDependencies": { - "@aws-cdk/assert": "1.46.0", + "@aws-cdk/assert": "1.54.0", "@types/jest": "^26.0.0", "@types/node": "14.0.13", "@typescript-eslint/eslint-plugin": "^3.3.0", "@typescript-eslint/parser": "^3.3.0", - "aws-cdk": "1.46.0", + "aws-cdk": "1.54.0", "eslint": "^7.3.0", "eslint-plugin-notice": "^0.9.10", "jest": "^26.0.1", @@ -29,16 +29,16 @@ "typescript": "~3.9.5" }, "dependencies": { - "@aws-cdk/aws-apigateway": "^1.46.0", - "@aws-cdk/aws-dynamodb": "^1.46.0", - "@aws-cdk/aws-cognito": "^1.46.0", - "@aws-cdk/aws-ec2": "^1.46.0", - "@aws-cdk/aws-eks": "^1.46.0", - "@aws-cdk/aws-iam": "^1.46.0", - "@aws-cdk/aws-lambda": "^1.46.0", - "@aws-cdk/aws-lambda-event-sources": "^1.46.0", - "@aws-cdk/aws-sam": "^1.46.0", - "@aws-cdk/aws-sqs": "^1.46.0", - "@aws-cdk/core": "^1.46.0" + "@aws-cdk/aws-apigateway": "^1.54.0", + "@aws-cdk/aws-dynamodb": "^1.54.0", + "@aws-cdk/aws-cognito": "^1.54.0", + "@aws-cdk/aws-ec2": "^1.54.0", + "@aws-cdk/aws-eks": "^1.54.0", + "@aws-cdk/aws-iam": "^1.54.0", + "@aws-cdk/aws-lambda": "^1.54.0", + "@aws-cdk/aws-lambda-event-sources": "^1.54.0", + "@aws-cdk/aws-sam": "^1.54.0", + "@aws-cdk/aws-sqs": "^1.54.0", + "@aws-cdk/core": "^1.54.0" } } diff --git a/test/worker/worker.test.ts b/test/worker/worker.test.ts index a742524..b3fd88c 100644 --- a/test/worker/worker.test.ts +++ b/test/worker/worker.test.ts @@ -16,7 +16,7 @@ import { expect as expectCDK, haveResource, haveResourceLike } from "@aws-cdk/assert"; import * as cdk from "@aws-cdk/core"; -import { Cluster } from "@aws-cdk/aws-eks"; +import { Cluster, KubernetesVersion } from "@aws-cdk/aws-eks"; import { Queue } from "@aws-cdk/aws-sqs"; import { LayerVersion } from "@aws-cdk/aws-lambda"; import { LAMBDA_LAYER_ARN } from "../../lib/constants"; @@ -27,7 +27,7 @@ function createWorker(stack: cdk.Stack, extraSGs?: string, handler = "testHandle if (extraSGs !== undefined) { stack.node.setContext("extraIngressSecurityGroups", extraSGs); } - const donorCluster = new Cluster(stack, "testCluster"); + const donorCluster = new Cluster(stack, "testCluster", { version: KubernetesVersion.V1_16 }); const donorQueue = new Queue(stack, "testQueue"); const table = new Table(stack, "test", { partitionKey: {name: "test", type: AttributeType.STRING} From aa4b085c1daf9be1daf643747b6ef37b103beee5 Mon Sep 17 00:00:00 2001 From: macenturalxl1 Date: Thu, 20 Aug 2020 15:34:26 +0100 Subject: [PATCH 03/23] Moved all to infrastructure directory --- .travis.yml | 1 + {bin => infrastructure/bin}/kai.ts | 0 cdk.json => infrastructure/cdk.json | 0 header.js => infrastructure/header.js | 0 jest.config.js => infrastructure/jest.config.js | 0 {lib => infrastructure/lib}/app-stack.ts | 0 {lib => infrastructure/lib}/authentication/user-pool-config.ts | 0 {lib => infrastructure/lib}/authentication/user-pool.ts | 0 {lib => infrastructure/lib}/constants.ts | 0 {lib => infrastructure/lib}/database/graph-database-props.ts | 0 {lib => infrastructure/lib}/database/graph-database.ts | 0 {lib => infrastructure/lib}/platform/alb-policy-statement.ts | 0 .../lib}/platform/crhelper-policy-statement.ts | 0 {lib => infrastructure/lib}/platform/graph-platform.ts | 0 {lib => infrastructure/lib}/platform/graph-uninstaller-props.ts | 0 {lib => infrastructure/lib}/platform/graph-uninstaller.ts | 0 .../lib}/platform/lambdas/crhelper/__init__.py | 0 .../lib}/platform/lambdas/crhelper/log_helper.py | 0 .../lib}/platform/lambdas/crhelper/resource_helper.py | 0 {lib => infrastructure/lib}/platform/lambdas/crhelper/utils.py | 0 {lib => infrastructure/lib}/platform/lambdas/uninstall_graphs.py | 0 .../lib}/platform/lambdas/uninstall_graphs_is_complete.py | 0 {lib => infrastructure/lib}/platform/node-group-config.ts | 0 {lib => infrastructure/lib}/rest-api/README.md | 0 .../lib}/rest-api/authentication/kai-rest-authorizer-props.ts | 0 .../lib}/rest-api/authentication/kai-rest-authorizer.ts | 0 {lib => infrastructure/lib}/rest-api/kai-rest-api-props.ts | 0 {lib => infrastructure/lib}/rest-api/kai-rest-api.ts | 0 .../lib}/rest-api/lambdas/add_graph_request.py | 0 .../lib}/rest-api/lambdas/delete_graph_request.py | 0 .../lib}/rest-api/lambdas/get_graph_request.py | 0 {lib => infrastructure/lib}/rest-api/lambdas/graph/__init__.py | 0 {lib => infrastructure/lib}/rest-api/lambdas/user/__init__.py | 0 {lib => infrastructure/lib}/workers/lambdas/add_graph.py | 0 {lib => infrastructure/lib}/workers/lambdas/delete_graph.py | 0 {lib => infrastructure/lib}/workers/lambdas/graph/__init__.py | 0 .../lib}/workers/lambdas/kubernetes/__init__.py | 0 {lib => infrastructure/lib}/workers/worker-props.ts | 0 {lib => infrastructure/lib}/workers/worker.ts | 0 package-lock.json => infrastructure/package-lock.json | 0 package.json => infrastructure/package.json | 0 .../test}/authentication/user-pool-config.test.ts | 0 {test => infrastructure/test}/authentication/user-pool.test.ts | 0 {test => infrastructure/test}/database/graph-database.test.ts | 0 {test => infrastructure/test}/platform/graph-platform.test.ts | 0 .../test}/rest-api/authentication/kai-rest-authorizer.test.ts | 0 {test => infrastructure/test}/rest-api/kai-rest-api.test.ts | 0 {test => infrastructure/test}/worker/worker.test.ts | 0 tsconfig.json => infrastructure/tsconfig.json | 0 49 files changed, 1 insertion(+) rename {bin => infrastructure/bin}/kai.ts (100%) rename cdk.json => infrastructure/cdk.json (100%) rename header.js => infrastructure/header.js (100%) rename jest.config.js => infrastructure/jest.config.js (100%) rename {lib => infrastructure/lib}/app-stack.ts (100%) rename {lib => infrastructure/lib}/authentication/user-pool-config.ts (100%) rename {lib => infrastructure/lib}/authentication/user-pool.ts (100%) rename {lib => infrastructure/lib}/constants.ts (100%) rename {lib => infrastructure/lib}/database/graph-database-props.ts (100%) rename {lib => infrastructure/lib}/database/graph-database.ts (100%) rename {lib => infrastructure/lib}/platform/alb-policy-statement.ts (100%) rename {lib => infrastructure/lib}/platform/crhelper-policy-statement.ts (100%) rename {lib => infrastructure/lib}/platform/graph-platform.ts (100%) rename {lib => infrastructure/lib}/platform/graph-uninstaller-props.ts (100%) rename {lib => infrastructure/lib}/platform/graph-uninstaller.ts (100%) rename {lib => infrastructure/lib}/platform/lambdas/crhelper/__init__.py (100%) rename {lib => infrastructure/lib}/platform/lambdas/crhelper/log_helper.py (100%) rename {lib => infrastructure/lib}/platform/lambdas/crhelper/resource_helper.py (100%) rename {lib => infrastructure/lib}/platform/lambdas/crhelper/utils.py (100%) rename {lib => infrastructure/lib}/platform/lambdas/uninstall_graphs.py (100%) rename {lib => infrastructure/lib}/platform/lambdas/uninstall_graphs_is_complete.py (100%) rename {lib => infrastructure/lib}/platform/node-group-config.ts (100%) rename {lib => infrastructure/lib}/rest-api/README.md (100%) rename {lib => infrastructure/lib}/rest-api/authentication/kai-rest-authorizer-props.ts (100%) rename {lib => infrastructure/lib}/rest-api/authentication/kai-rest-authorizer.ts (100%) rename {lib => infrastructure/lib}/rest-api/kai-rest-api-props.ts (100%) rename {lib => infrastructure/lib}/rest-api/kai-rest-api.ts (100%) rename {lib => infrastructure/lib}/rest-api/lambdas/add_graph_request.py (100%) rename {lib => infrastructure/lib}/rest-api/lambdas/delete_graph_request.py (100%) rename {lib => infrastructure/lib}/rest-api/lambdas/get_graph_request.py (100%) rename {lib => infrastructure/lib}/rest-api/lambdas/graph/__init__.py (100%) rename {lib => infrastructure/lib}/rest-api/lambdas/user/__init__.py (100%) rename {lib => infrastructure/lib}/workers/lambdas/add_graph.py (100%) rename {lib => infrastructure/lib}/workers/lambdas/delete_graph.py (100%) rename {lib => infrastructure/lib}/workers/lambdas/graph/__init__.py (100%) rename {lib => infrastructure/lib}/workers/lambdas/kubernetes/__init__.py (100%) rename {lib => infrastructure/lib}/workers/worker-props.ts (100%) rename {lib => infrastructure/lib}/workers/worker.ts (100%) rename package-lock.json => infrastructure/package-lock.json (100%) rename package.json => infrastructure/package.json (100%) rename {test => infrastructure/test}/authentication/user-pool-config.test.ts (100%) rename {test => infrastructure/test}/authentication/user-pool.test.ts (100%) rename {test => infrastructure/test}/database/graph-database.test.ts (100%) rename {test => infrastructure/test}/platform/graph-platform.test.ts (100%) rename {test => infrastructure/test}/rest-api/authentication/kai-rest-authorizer.test.ts (100%) rename {test => infrastructure/test}/rest-api/kai-rest-api.test.ts (100%) rename {test => infrastructure/test}/worker/worker.test.ts (100%) rename tsconfig.json => infrastructure/tsconfig.json (100%) diff --git a/.travis.yml b/.travis.yml index a28c4d1..f5cdf11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,6 @@ node_js: - node script: + - cd infrastructure/ - npm run lint - npm run test \ No newline at end of file diff --git a/bin/kai.ts b/infrastructure/bin/kai.ts similarity index 100% rename from bin/kai.ts rename to infrastructure/bin/kai.ts diff --git a/cdk.json b/infrastructure/cdk.json similarity index 100% rename from cdk.json rename to infrastructure/cdk.json diff --git a/header.js b/infrastructure/header.js similarity index 100% rename from header.js rename to infrastructure/header.js diff --git a/jest.config.js b/infrastructure/jest.config.js similarity index 100% rename from jest.config.js rename to infrastructure/jest.config.js diff --git a/lib/app-stack.ts b/infrastructure/lib/app-stack.ts similarity index 100% rename from lib/app-stack.ts rename to infrastructure/lib/app-stack.ts diff --git a/lib/authentication/user-pool-config.ts b/infrastructure/lib/authentication/user-pool-config.ts similarity index 100% rename from lib/authentication/user-pool-config.ts rename to infrastructure/lib/authentication/user-pool-config.ts diff --git a/lib/authentication/user-pool.ts b/infrastructure/lib/authentication/user-pool.ts similarity index 100% rename from lib/authentication/user-pool.ts rename to infrastructure/lib/authentication/user-pool.ts diff --git a/lib/constants.ts b/infrastructure/lib/constants.ts similarity index 100% rename from lib/constants.ts rename to infrastructure/lib/constants.ts diff --git a/lib/database/graph-database-props.ts b/infrastructure/lib/database/graph-database-props.ts similarity index 100% rename from lib/database/graph-database-props.ts rename to infrastructure/lib/database/graph-database-props.ts diff --git a/lib/database/graph-database.ts b/infrastructure/lib/database/graph-database.ts similarity index 100% rename from lib/database/graph-database.ts rename to infrastructure/lib/database/graph-database.ts diff --git a/lib/platform/alb-policy-statement.ts b/infrastructure/lib/platform/alb-policy-statement.ts similarity index 100% rename from lib/platform/alb-policy-statement.ts rename to infrastructure/lib/platform/alb-policy-statement.ts diff --git a/lib/platform/crhelper-policy-statement.ts b/infrastructure/lib/platform/crhelper-policy-statement.ts similarity index 100% rename from lib/platform/crhelper-policy-statement.ts rename to infrastructure/lib/platform/crhelper-policy-statement.ts diff --git a/lib/platform/graph-platform.ts b/infrastructure/lib/platform/graph-platform.ts similarity index 100% rename from lib/platform/graph-platform.ts rename to infrastructure/lib/platform/graph-platform.ts diff --git a/lib/platform/graph-uninstaller-props.ts b/infrastructure/lib/platform/graph-uninstaller-props.ts similarity index 100% rename from lib/platform/graph-uninstaller-props.ts rename to infrastructure/lib/platform/graph-uninstaller-props.ts diff --git a/lib/platform/graph-uninstaller.ts b/infrastructure/lib/platform/graph-uninstaller.ts similarity index 100% rename from lib/platform/graph-uninstaller.ts rename to infrastructure/lib/platform/graph-uninstaller.ts diff --git a/lib/platform/lambdas/crhelper/__init__.py b/infrastructure/lib/platform/lambdas/crhelper/__init__.py similarity index 100% rename from lib/platform/lambdas/crhelper/__init__.py rename to infrastructure/lib/platform/lambdas/crhelper/__init__.py diff --git a/lib/platform/lambdas/crhelper/log_helper.py b/infrastructure/lib/platform/lambdas/crhelper/log_helper.py similarity index 100% rename from lib/platform/lambdas/crhelper/log_helper.py rename to infrastructure/lib/platform/lambdas/crhelper/log_helper.py diff --git a/lib/platform/lambdas/crhelper/resource_helper.py b/infrastructure/lib/platform/lambdas/crhelper/resource_helper.py similarity index 100% rename from lib/platform/lambdas/crhelper/resource_helper.py rename to infrastructure/lib/platform/lambdas/crhelper/resource_helper.py diff --git a/lib/platform/lambdas/crhelper/utils.py b/infrastructure/lib/platform/lambdas/crhelper/utils.py similarity index 100% rename from lib/platform/lambdas/crhelper/utils.py rename to infrastructure/lib/platform/lambdas/crhelper/utils.py diff --git a/lib/platform/lambdas/uninstall_graphs.py b/infrastructure/lib/platform/lambdas/uninstall_graphs.py similarity index 100% rename from lib/platform/lambdas/uninstall_graphs.py rename to infrastructure/lib/platform/lambdas/uninstall_graphs.py diff --git a/lib/platform/lambdas/uninstall_graphs_is_complete.py b/infrastructure/lib/platform/lambdas/uninstall_graphs_is_complete.py similarity index 100% rename from lib/platform/lambdas/uninstall_graphs_is_complete.py rename to infrastructure/lib/platform/lambdas/uninstall_graphs_is_complete.py diff --git a/lib/platform/node-group-config.ts b/infrastructure/lib/platform/node-group-config.ts similarity index 100% rename from lib/platform/node-group-config.ts rename to infrastructure/lib/platform/node-group-config.ts diff --git a/lib/rest-api/README.md b/infrastructure/lib/rest-api/README.md similarity index 100% rename from lib/rest-api/README.md rename to infrastructure/lib/rest-api/README.md diff --git a/lib/rest-api/authentication/kai-rest-authorizer-props.ts b/infrastructure/lib/rest-api/authentication/kai-rest-authorizer-props.ts similarity index 100% rename from lib/rest-api/authentication/kai-rest-authorizer-props.ts rename to infrastructure/lib/rest-api/authentication/kai-rest-authorizer-props.ts diff --git a/lib/rest-api/authentication/kai-rest-authorizer.ts b/infrastructure/lib/rest-api/authentication/kai-rest-authorizer.ts similarity index 100% rename from lib/rest-api/authentication/kai-rest-authorizer.ts rename to infrastructure/lib/rest-api/authentication/kai-rest-authorizer.ts diff --git a/lib/rest-api/kai-rest-api-props.ts b/infrastructure/lib/rest-api/kai-rest-api-props.ts similarity index 100% rename from lib/rest-api/kai-rest-api-props.ts rename to infrastructure/lib/rest-api/kai-rest-api-props.ts diff --git a/lib/rest-api/kai-rest-api.ts b/infrastructure/lib/rest-api/kai-rest-api.ts similarity index 100% rename from lib/rest-api/kai-rest-api.ts rename to infrastructure/lib/rest-api/kai-rest-api.ts diff --git a/lib/rest-api/lambdas/add_graph_request.py b/infrastructure/lib/rest-api/lambdas/add_graph_request.py similarity index 100% rename from lib/rest-api/lambdas/add_graph_request.py rename to infrastructure/lib/rest-api/lambdas/add_graph_request.py diff --git a/lib/rest-api/lambdas/delete_graph_request.py b/infrastructure/lib/rest-api/lambdas/delete_graph_request.py similarity index 100% rename from lib/rest-api/lambdas/delete_graph_request.py rename to infrastructure/lib/rest-api/lambdas/delete_graph_request.py diff --git a/lib/rest-api/lambdas/get_graph_request.py b/infrastructure/lib/rest-api/lambdas/get_graph_request.py similarity index 100% rename from lib/rest-api/lambdas/get_graph_request.py rename to infrastructure/lib/rest-api/lambdas/get_graph_request.py diff --git a/lib/rest-api/lambdas/graph/__init__.py b/infrastructure/lib/rest-api/lambdas/graph/__init__.py similarity index 100% rename from lib/rest-api/lambdas/graph/__init__.py rename to infrastructure/lib/rest-api/lambdas/graph/__init__.py diff --git a/lib/rest-api/lambdas/user/__init__.py b/infrastructure/lib/rest-api/lambdas/user/__init__.py similarity index 100% rename from lib/rest-api/lambdas/user/__init__.py rename to infrastructure/lib/rest-api/lambdas/user/__init__.py diff --git a/lib/workers/lambdas/add_graph.py b/infrastructure/lib/workers/lambdas/add_graph.py similarity index 100% rename from lib/workers/lambdas/add_graph.py rename to infrastructure/lib/workers/lambdas/add_graph.py diff --git a/lib/workers/lambdas/delete_graph.py b/infrastructure/lib/workers/lambdas/delete_graph.py similarity index 100% rename from lib/workers/lambdas/delete_graph.py rename to infrastructure/lib/workers/lambdas/delete_graph.py diff --git a/lib/workers/lambdas/graph/__init__.py b/infrastructure/lib/workers/lambdas/graph/__init__.py similarity index 100% rename from lib/workers/lambdas/graph/__init__.py rename to infrastructure/lib/workers/lambdas/graph/__init__.py diff --git a/lib/workers/lambdas/kubernetes/__init__.py b/infrastructure/lib/workers/lambdas/kubernetes/__init__.py similarity index 100% rename from lib/workers/lambdas/kubernetes/__init__.py rename to infrastructure/lib/workers/lambdas/kubernetes/__init__.py diff --git a/lib/workers/worker-props.ts b/infrastructure/lib/workers/worker-props.ts similarity index 100% rename from lib/workers/worker-props.ts rename to infrastructure/lib/workers/worker-props.ts diff --git a/lib/workers/worker.ts b/infrastructure/lib/workers/worker.ts similarity index 100% rename from lib/workers/worker.ts rename to infrastructure/lib/workers/worker.ts diff --git a/package-lock.json b/infrastructure/package-lock.json similarity index 100% rename from package-lock.json rename to infrastructure/package-lock.json diff --git a/package.json b/infrastructure/package.json similarity index 100% rename from package.json rename to infrastructure/package.json diff --git a/test/authentication/user-pool-config.test.ts b/infrastructure/test/authentication/user-pool-config.test.ts similarity index 100% rename from test/authentication/user-pool-config.test.ts rename to infrastructure/test/authentication/user-pool-config.test.ts diff --git a/test/authentication/user-pool.test.ts b/infrastructure/test/authentication/user-pool.test.ts similarity index 100% rename from test/authentication/user-pool.test.ts rename to infrastructure/test/authentication/user-pool.test.ts diff --git a/test/database/graph-database.test.ts b/infrastructure/test/database/graph-database.test.ts similarity index 100% rename from test/database/graph-database.test.ts rename to infrastructure/test/database/graph-database.test.ts diff --git a/test/platform/graph-platform.test.ts b/infrastructure/test/platform/graph-platform.test.ts similarity index 100% rename from test/platform/graph-platform.test.ts rename to infrastructure/test/platform/graph-platform.test.ts diff --git a/test/rest-api/authentication/kai-rest-authorizer.test.ts b/infrastructure/test/rest-api/authentication/kai-rest-authorizer.test.ts similarity index 100% rename from test/rest-api/authentication/kai-rest-authorizer.test.ts rename to infrastructure/test/rest-api/authentication/kai-rest-authorizer.test.ts diff --git a/test/rest-api/kai-rest-api.test.ts b/infrastructure/test/rest-api/kai-rest-api.test.ts similarity index 100% rename from test/rest-api/kai-rest-api.test.ts rename to infrastructure/test/rest-api/kai-rest-api.test.ts diff --git a/test/worker/worker.test.ts b/infrastructure/test/worker/worker.test.ts similarity index 100% rename from test/worker/worker.test.ts rename to infrastructure/test/worker/worker.test.ts diff --git a/tsconfig.json b/infrastructure/tsconfig.json similarity index 100% rename from tsconfig.json rename to infrastructure/tsconfig.json From b9014b020844491ca5c8515176fe4cdc7f4080c9 Mon Sep 17 00:00:00 2001 From: macenturalxl1 Date: Thu, 20 Aug 2020 16:07:09 +0100 Subject: [PATCH 04/23] Travis fix --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f5cdf11..221a33f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,5 @@ node_js: - node script: - - cd infrastructure/ - - npm run lint - - npm run test \ No newline at end of file + - cd infrastructure/ && npm run lint + - cd infrastructure/ && npm run test \ No newline at end of file From 5fd565c6f35f11b8f90cc1d3712b1c3cdd014965 Mon Sep 17 00:00:00 2001 From: macenturalxl1 Date: Thu, 20 Aug 2020 16:12:24 +0100 Subject: [PATCH 05/23] Travis fix --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 221a33f..2c39d86 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,6 @@ node_js: - node script: + - cd infrastructure/ && npm install - cd infrastructure/ && npm run lint - cd infrastructure/ && npm run test \ No newline at end of file From 3d38b58ba8b34386d992c27517b158929a39b3c4 Mon Sep 17 00:00:00 2001 From: macenturalxl1 Date: Thu, 20 Aug 2020 16:16:55 +0100 Subject: [PATCH 06/23] Travis fix --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c39d86..69eb388 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,10 @@ dist: bionic node_js: - node +before_script: + - cd infrastructure/ + script: - - cd infrastructure/ && npm install - - cd infrastructure/ && npm run lint - - cd infrastructure/ && npm run test \ No newline at end of file + - npm install + - npm run lint + - npm run test \ No newline at end of file From 719e3c01d3ae42920883bbc390bd3d07d11a1ce2 Mon Sep 17 00:00:00 2001 From: macenturalxl1 Date: Thu, 20 Aug 2020 16:19:47 +0100 Subject: [PATCH 07/23] Travis fix --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 69eb388..b4f02fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,12 @@ dist: bionic node_js: - node -before_script: +before_install: - cd infrastructure/ -script: +install: - npm install + +script: - npm run lint - npm run test \ No newline at end of file From 32e05cfcfd4bee42a35dbf4bbec0d709e72c936a Mon Sep 17 00:00:00 2001 From: macenturalxl1 Date: Thu, 20 Aug 2020 16:24:54 +0100 Subject: [PATCH 08/23] Travis (final) fix --- .eslintignore => infrastructure/.eslintignore | 0 .eslintrc => infrastructure/.eslintrc | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .eslintignore => infrastructure/.eslintignore (100%) rename .eslintrc => infrastructure/.eslintrc (100%) diff --git a/.eslintignore b/infrastructure/.eslintignore similarity index 100% rename from .eslintignore rename to infrastructure/.eslintignore diff --git a/.eslintrc b/infrastructure/.eslintrc similarity index 100% rename from .eslintrc rename to infrastructure/.eslintrc From cbf581547d5b94326ded97dbb6f61edfaf915279 Mon Sep 17 00:00:00 2001 From: m29827 <62667809+m29827@users.noreply.github.com> Date: Thu, 27 Aug 2020 16:41:21 +0100 Subject: [PATCH 09/23] Gh 40 remove ebs volumes graph deleted / uninstalled (#49) * gh-26 Remove application load-balancers and target groups when cluster deleted. * gh-26 Removing *.pyc files and ignoring __pycache__ directory * gh-40 Remove EBS volumes when cluster deleted. * gh-40 Remove EBS Volumes when graph uninstalled / deleted. * gh-40 Remove volumes with kubectl instead of the aws-sdk --- infrastructure/lib/app-stack.ts | 27 +- infrastructure/lib/constants.ts | 2 +- infrastructure/lib/platform/graph-platform.ts | 2 +- .../lib/platform/lambdas/uninstall_graphs.py | 4 +- infrastructure/lib/rest-api/README.md | 12 +- infrastructure/lib/rest-api/kai-rest-api.ts | 2 - .../lib/rest-api/lambdas/get_graph_request.py | 2 +- .../lib/rest-api/lambdas/graph/__init__.py | 4 +- .../lib/rest-api/lambdas/user/__init__.py | 4 +- .../lib/workers/lambdas/delete_graph.py | 21 +- .../workers/lambdas/kubernetes/__init__.py | 85 +- infrastructure/lib/workers/worker-props.ts | 2 + infrastructure/lib/workers/worker.ts | 10 +- infrastructure/package-lock.json | 772 +++++++++--------- infrastructure/package.json | 26 +- infrastructure/test/worker/worker.test.ts | 9 +- 16 files changed, 521 insertions(+), 463 deletions(-) diff --git a/infrastructure/lib/app-stack.ts b/infrastructure/lib/app-stack.ts index 0fe0474..8857ef2 100644 --- a/infrastructure/lib/app-stack.ts +++ b/infrastructure/lib/app-stack.ts @@ -25,6 +25,7 @@ import { GraphDatabase } from "./database/graph-database"; import { Worker } from "./workers/worker"; import { KaiUserPool } from "./authentication/user-pool"; import { GraphDatabaseProps } from "./database/graph-database-props"; +import { PolicyStatement } from "@aws-cdk/aws-iam"; // The main stack for Kai export class AppStack extends cdk.Stack { @@ -62,6 +63,21 @@ export class AppStack extends cdk.Stack { const layerVersionArn = samApp.getAtt("Outputs.LayerVersionArn").toString(); const kubectlLambdaLayer = LayerVersion.fromLayerVersionArn(this, "KubectlLambdaLayer", layerVersionArn); + // Describe EKS cluster policy statement + const describeClusterPolicyStatement = new PolicyStatement({ + actions: [ "eks:DescribeCluster" ], + resources: [ platform.eksCluster.clusterArn ] + }); + + // Manage EBS Volumes policy statement + const manageVolumesPolicyStatement = new PolicyStatement({ + resources: ["*"], + actions: [ + "ec2:DescribeVolumes", + "ec2:DeleteVolume" + ] + }); + // Workers new Worker(this, "AddGraphWorker", { cluster: platform.eksCluster, @@ -70,7 +86,10 @@ export class AppStack extends cdk.Stack { graphTable: database.table, handler: "add_graph.handler", timeout: ADD_GRAPH_TIMEOUT, - batchSize: ADD_GRAPH_WORKER_BATCH_SIZE + batchSize: ADD_GRAPH_WORKER_BATCH_SIZE, + policyStatements: [ + describeClusterPolicyStatement + ] }); const deleteGraphWorker = new Worker(this, "DeleteGraphWorker", { @@ -80,7 +99,11 @@ export class AppStack extends cdk.Stack { graphTable: database.table, handler: "delete_graph.handler", timeout: DELETE_GRAPH_TIMEOUT, - batchSize: DELETE_GRAPH_WORKER_BATCH_SIZE + batchSize: DELETE_GRAPH_WORKER_BATCH_SIZE, + policyStatements: [ + describeClusterPolicyStatement, + manageVolumesPolicyStatement + ] }); // Graph uninstaller diff --git a/infrastructure/lib/constants.ts b/infrastructure/lib/constants.ts index f9979e8..d53cf05 100644 --- a/infrastructure/lib/constants.ts +++ b/infrastructure/lib/constants.ts @@ -27,4 +27,4 @@ const TIMEOUT_FOR_ADDING_GRAPH_IN_MINUTES = 5; // how long it should take for on const TIMEOUT_FOR_DELETING_GRAPH_IN_MINUTES = 2; // how long it should take for one graph to be deleted export const DELETE_GRAPH_TIMEOUT = Duration.minutes(TIMEOUT_FOR_DELETING_GRAPH_IN_MINUTES * DELETE_GRAPH_WORKER_BATCH_SIZE); -export const ADD_GRAPH_TIMEOUT = Duration.minutes(TIMEOUT_FOR_ADDING_GRAPH_IN_MINUTES * ADD_GRAPH_WORKER_BATCH_SIZE); \ No newline at end of file +export const ADD_GRAPH_TIMEOUT = Duration.minutes(TIMEOUT_FOR_ADDING_GRAPH_IN_MINUTES * ADD_GRAPH_WORKER_BATCH_SIZE); diff --git a/infrastructure/lib/platform/graph-platform.ts b/infrastructure/lib/platform/graph-platform.ts index d5788cd..038c0f6 100644 --- a/infrastructure/lib/platform/graph-platform.ts +++ b/infrastructure/lib/platform/graph-platform.ts @@ -58,7 +58,7 @@ export class GraphPlatForm extends cdk.Construct { // Create cluster this._eksCluster = new eks.Cluster(this, "EksCluster", { - version: eks.KubernetesVersion.V1_16, + version: eks.KubernetesVersion.V1_17, kubectlEnabled: true, vpc: vpc, mastersRole: mastersRole, diff --git a/infrastructure/lib/platform/lambdas/uninstall_graphs.py b/infrastructure/lib/platform/lambdas/uninstall_graphs.py index d3c9b2d..349e08c 100644 --- a/infrastructure/lib/platform/lambdas/uninstall_graphs.py +++ b/infrastructure/lib/platform/lambdas/uninstall_graphs.py @@ -42,7 +42,7 @@ def delete(event, context): InvocationType = "RequestResponse", Payload = json.dumps({ "pathParameters": { - "graphId": graph["graphId"] + "graphName": graph["graphName"] } }) ) @@ -50,7 +50,7 @@ def delete(event, context): logger.info("Received responsePayloadJson: {}".format(responsePayloadJson)) if responsePayloadJson["statusCode"] != 202: logger.error("Unable to delete graph: {}, received status code: {}, message: {}".format( - graph["graphId"], + graph["graphName"], responsePayloadJson["statusCode"], responsePayloadJson["body"] ) diff --git a/infrastructure/lib/rest-api/README.md b/infrastructure/lib/rest-api/README.md index 0dae986..c92bf46 100644 --- a/infrastructure/lib/rest-api/README.md +++ b/infrastructure/lib/rest-api/README.md @@ -50,7 +50,7 @@ curl -H "Authorization: " https://.execute-api. The Graphs resource enables creation, deletion and retrieval of Graphs managed by Kai. #### GET /graphs -Retrieves all graphs objects from the backend database. At present this only includes the graphId and its current state but this is likely to change as the project grows. +Retrieves all graphs objects from the backend database. At present this only includes the graphName and its current state but this is likely to change as the project grows. A graph can be in different states. At present these states can be: * DEPLOYMENT_QUEUED @@ -77,7 +77,7 @@ Example response: ] ``` -#### GET /graphs/{graphId} +#### GET /graphs/{graphName} Retrieves a single graph from the backend database. If the Graph Id is not found, a 404 response is sent. If the requesting user is not a configured administrator of the graph a 403 response is returned. Example response: @@ -89,12 +89,12 @@ Example response: ``` #### POST /graphs -Creates and deploys a new graph. This endpoint is asynchronous meaning it will return before deploying a graph which takes around 5 minutes. At present, you need to provide a Gaffer schema which is split into two parts: elements and types, as well as a graphId which must be unique. This endpoint will respond with a simple 201 return code. If the user requests a graph which is already created, A 400 response will be sent, along with an error message. There is a constraint in gaffer-docker that graph ids have to be lowercase alphanumerics. We hope to address this in a bugfix to allow uppercase alphanumerics too. By default only the creating user has administration access to the graph through the REST API. If you wish to specify additional users with administration privileges they can be listed in an optional "administrators" property. If an attempt is made to configure users who are not members of the Cognito User Pool a 400 response will be returned. +Creates and deploys a new graph. This endpoint is asynchronous meaning it will return before deploying a graph which takes around 5 minutes. At present, you need to provide a Gaffer schema which is split into two parts: elements and types, as well as a graphName which must be unique. This endpoint will respond with a simple 201 return code. If the user requests a graph which is already created, A 400 response will be sent, along with an error message. There is a constraint in gaffer-docker that graph names have to be lowercase alphanumerics. We hope to address this in a bugfix to allow uppercase alphanumerics too. By default only the creating user has administration access to the graph through the REST API. If you wish to specify additional users with administration privileges they can be listed in an optional "administrators" property. If an attempt is made to configure users who are not members of the Cognito User Pool a 400 response will be returned. Example request body: ```json { - "graphId": "basic", + "graphName": "basic", "administrators": [], "schema": { "elements": { @@ -135,5 +135,5 @@ Example request body: } ``` -#### DELETE /graphs/{graphId} -Deletes a graph deployment from the platform. This endpoint is asynchronous meaning that it will respond before the graph deployment is removed. Once the graph deployment is removed, the graph will be removed from the backend database. If the requested graphId is not present or is not in the backend database at the start, a 400 error is returned. If the user is not an administrator a 403 response is returned. Otherwise a 202 status code is returned. +#### DELETE /graphs/{graphName} +Deletes a graph deployment from the platform. This endpoint is asynchronous meaning that it will respond before the graph deployment is removed. Once the graph deployment is removed, the graph will be removed from the backend database. If the requested graphName is not present or is not in the backend database at the start, a 400 error is returned. If the user is not an administrator a 403 response is returned. Otherwise a 202 status code is returned. diff --git a/infrastructure/lib/rest-api/kai-rest-api.ts b/infrastructure/lib/rest-api/kai-rest-api.ts index 50e14d3..1cf2b74 100644 --- a/infrastructure/lib/rest-api/kai-rest-api.ts +++ b/infrastructure/lib/rest-api/kai-rest-api.ts @@ -108,7 +108,6 @@ export class KaiRestApi extends cdk.Construct { props.graphTable.grantReadData(this._getGraphsLambda); // Both GET and GET all are served by the same lambda - const getGraphIntegration = new api.LambdaIntegration(this._getGraphsLambda); graphsResource.addMethod("GET", getGraphIntegration, methodOptions); graph.addMethod("GET", getGraphIntegration, methodOptions); @@ -130,4 +129,3 @@ export class KaiRestApi extends cdk.Construct { return this._deleteGraphLambda; } } - diff --git a/infrastructure/lib/rest-api/lambdas/get_graph_request.py b/infrastructure/lib/rest-api/lambdas/get_graph_request.py index 99d8b0d..123dd82 100644 --- a/infrastructure/lib/rest-api/lambdas/get_graph_request.py +++ b/infrastructure/lib/rest-api/lambdas/get_graph_request.py @@ -38,7 +38,7 @@ def handler(event, context): try: return { "statusCode": 200, - "body": json.dumps(graph.get_graph(graph.format_graph_name(graph_name))) + "body": json.dumps(graph.get_graph(graph_name)) } except Exception as e: return { diff --git a/infrastructure/lib/rest-api/lambdas/graph/__init__.py b/infrastructure/lib/rest-api/lambdas/graph/__init__.py index 908665d..3bc9788 100644 --- a/infrastructure/lib/rest-api/lambdas/graph/__init__.py +++ b/infrastructure/lib/rest-api/lambdas/graph/__init__.py @@ -25,13 +25,13 @@ def get_all_graphs(self, requesting_user): return list(filter(lambda graph: requesting_user in graph["administrators"], graphs)) - def get_graph(self, release_name): + def get_graph(self, graph_name): """ Gets a specific graph from Dynamodb table """ response = self.table.get_item( Key={ - "releaseName": release_name + "releaseName": format_graph_name(graph_name) } ) if "Item" in response: diff --git a/infrastructure/lib/rest-api/lambdas/user/__init__.py b/infrastructure/lib/rest-api/lambdas/user/__init__.py index 4f7e3be..d55a30a 100644 --- a/infrastructure/lib/rest-api/lambdas/user/__init__.py +++ b/infrastructure/lib/rest-api/lambdas/user/__init__.py @@ -29,13 +29,13 @@ def get_requesting_cognito_user(self, request): return None return request["requestContext"]["authorizer"]["claims"]["cognito:username"] - def is_authorized(self, user, graphId): + def is_authorized(self, user, graphName): # If Authenticated through AWS account treat as admin for all graphs if (user is None): return True # Otherwise check the list of administrators configured on the graph try: - graph_record = self.graph.get_graph(graphId) + graph_record = self.graph.get_graph(graphName) return user in graph_record["administrators"] except Exception as e: return False diff --git a/infrastructure/lib/workers/lambdas/delete_graph.py b/infrastructure/lib/workers/lambdas/delete_graph.py index a451a8f..d3f5008 100644 --- a/infrastructure/lib/workers/lambdas/delete_graph.py +++ b/infrastructure/lib/workers/lambdas/delete_graph.py @@ -1,10 +1,9 @@ import os -import subprocess -import boto3 import json import logging +import time from graph import Graph -from kubernetes import HelmClient +from kubernetes import HelmClient, KubernetesClient logger = logging.getLogger() logger.setLevel(logging.INFO) @@ -15,29 +14,30 @@ graph_table_name = os.getenv("graph_table_name") -def uninstall_release(helm_client, body): +def uninstall_release(helm_client, kubernetes_client, body): """ Uninstalls a release from the Kubernetes Cluster """ - release_Name = body["releaseName"] + release_name = body["releaseName"] expected_status=body["expectedStatus"] # Create a Graph object to track the deletion - graph = Graph(graph_table_name, release_Name) + graph = Graph(graph_table_name, release_name) if not graph.check_status(expected_status): - logger.warn("Graph %s had unexpected status. Abandoning delete", release_Name) + logger.warn("Graph %s had unexpected status. Abandoning delete", release_name) return graph.update_status("DELETION_IN_PROGRESS") - uninstalled = helm_client.uninstall_chart(release_Name) + uninstalled = helm_client.uninstall_chart(release_name) if uninstalled: + kubernetes_client.delete_volumes(release_name); graph.delete() else: graph.update_status("DELETION_FAILED") - + def handler(event, context): """ The entrypoint for the Delete Graph Handler @@ -45,6 +45,7 @@ def handler(event, context): logger.info(event) helm_client = HelmClient(cluster_name) + kubernetes_client = KubernetesClient(cluster_name) for record in event["Records"]: body = json.loads(record["body"]) - uninstall_release(helm_client, body) \ No newline at end of file + uninstall_release(helm_client, kubernetes_client, body) diff --git a/infrastructure/lib/workers/lambdas/kubernetes/__init__.py b/infrastructure/lib/workers/lambdas/kubernetes/__init__.py index dd50880..a261ccc 100644 --- a/infrastructure/lib/workers/lambdas/kubernetes/__init__.py +++ b/infrastructure/lib/workers/lambdas/kubernetes/__init__.py @@ -6,22 +6,51 @@ standard_kubeconfig="/tmp/kubeconfig" -class HelmClient: - __HELM_CMD="helm" - def __init__(self, cluster_name, kubeconfig=standard_kubeconfig): +class CommandHelper: + @staticmethod + def run_command(cmd, release_name): + succeeded=False + try: + subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True, cwd="/tmp") + succeeded=True + except subprocess.CalledProcessError as err: + logger.error("Error during excution of command: %s against release name: %s", cmd, release_name) + logger.error(err.output) + + return succeeded + + +class KubeConfigurator: + def run_once(f): + def wrapper(*args, **kwargs): + if not wrapper.has_run: + wrapper.has_run = True + return f(*args, **kwargs) + wrapper.has_run = False + return wrapper + + @run_once + def update_kube_config(self, cluster_name, kubeconfig=standard_kubeconfig): + logger.info("Configuring kubectl for cluster: %s", cluster_name); subprocess.check_call([ 'aws', 'eks', 'update-kubeconfig', '--name', cluster_name, '--kubeconfig', kubeconfig ]) - self.cluster_name = cluster_name + + +class HelmClient: + __HELM_CMD="helm" + + def __init__(self, cluster_name, kubeconfig=standard_kubeconfig): + KubeConfigurator().update_kube_config(cluster_name=cluster_name, kubeconfig=kubeconfig) self.kubeconfig = kubeconfig - def __run(self, instruction, release, values=None, chart=None, repo=None): + def __run(self, instruction, release_name, values=None, chart=None, repo=None): """ Runs a Helm command and returns True if it succeeds and False if it fails """ - cmd = [ self.__HELM_CMD, instruction, release ] + cmd = [ self.__HELM_CMD, instruction, release_name ] if chart is not None: cmd.append(chart) if repo is not None: @@ -30,26 +59,42 @@ def __run(self, instruction, release, values=None, chart=None, repo=None): cmd.extend(["--values", values]) cmd.extend(["--kubeconfig", self.kubeconfig]) - succeeded=False - try: - subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True, cwd="/tmp") - succeeded=True - except subprocess.CalledProcessError as err: - logger.error("Error during %s of %s", instruction, release) - logger.error(err.output) - - return succeeded + return CommandHelper.run_command(cmd, release_name) - def install_chart(self, release, values=None, chart="gaffer", repo="https://gchq.github.io/gaffer-docker"): + def install_chart(self, release_name, values=None, chart="gaffer", repo="https://gchq.github.io/gaffer-docker"): """ Installs a Helm chart and returns True if it Succeeds and False if it fails """ - return self.__run(instruction="install", release=release, values=values, chart=chart, repo=repo) + return self.__run(instruction="install", release_name=release_name, values=values, chart=chart, repo=repo) - def uninstall_chart(self, release): + def uninstall_chart(self, release_name): """ Uninstalls a Helm release and returns True if it Succeeds and False if it fails """ - return self.__run(instruction="uninstall", release=release) + return self.__run(instruction="uninstall", release_name=release_name) + + +class KubernetesClient: + __KUBECTL_CMD="kubectl" + + def __init__(self, cluster_name, kubeconfig=standard_kubeconfig): + KubeConfigurator().update_kube_config(cluster_name=cluster_name, kubeconfig=kubeconfig) + self.kubeconfig = kubeconfig + + def delete_volumes(self, release_name): + """ + Deletes the Persistent Volume Claims associated to a release_name + """ + # HDFS Datanodes & Namenode + self.__delete_volumes(release_name=release_name, selectors=["app.kubernetes.io/instance={}".format(release_name)]) + + # Zookeeper + self.__delete_volumes(release_name=release_name, selectors=["release={}".format(release_name)]) + + def __delete_volumes(self, release_name, selectors): + cmd = [ self.__KUBECTL_CMD, "delete", "pvc", "--kubeconfig", self.kubeconfig ] + for selector in selectors: + cmd.append("--selector") + cmd.append(selector) - \ No newline at end of file + CommandHelper.run_command(cmd, release_name) diff --git a/infrastructure/lib/workers/worker-props.ts b/infrastructure/lib/workers/worker-props.ts index 5770e00..152d321 100644 --- a/infrastructure/lib/workers/worker-props.ts +++ b/infrastructure/lib/workers/worker-props.ts @@ -19,6 +19,7 @@ import { ILayerVersion } from "@aws-cdk/aws-lambda"; import { Cluster } from "@aws-cdk/aws-eks"; import { Duration } from "@aws-cdk/core"; import { Table } from "@aws-cdk/aws-dynamodb"; +import { PolicyStatement } from "@aws-cdk/aws-iam"; export interface WorkerProps { queue: Queue; @@ -28,4 +29,5 @@ export interface WorkerProps { handler: string; timeout: Duration; batchSize: number; + policyStatements: PolicyStatement[]; } \ No newline at end of file diff --git a/infrastructure/lib/workers/worker.ts b/infrastructure/lib/workers/worker.ts index 32a83b5..4b4ea90 100644 --- a/infrastructure/lib/workers/worker.ts +++ b/infrastructure/lib/workers/worker.ts @@ -18,7 +18,6 @@ import { Construct } from "@aws-cdk/core"; import { WorkerProps } from "./worker-props"; import * as lambda from "@aws-cdk/aws-lambda"; import * as path from "path"; -import { PolicyStatement } from "@aws-cdk/aws-iam"; import { SqsEventSource } from "@aws-cdk/aws-lambda-event-sources"; export class Worker extends Construct { @@ -56,11 +55,10 @@ export class Worker extends Construct { batchSize: props.batchSize })); - // Add permisssions to role - this._function.addToRolePolicy(new PolicyStatement({ - actions: [ "eks:DescribeCluster" ], - resources: [ props.cluster.clusterArn ] - })); + // Add policy statements to role + for (const policyStatement of props.policyStatements) { + this._function.addToRolePolicy(policyStatement); + } props.graphTable.grantReadWriteData(this._function); diff --git a/infrastructure/package-lock.json b/infrastructure/package-lock.json index 8804f15..eee2a91 100644 --- a/infrastructure/package-lock.json +++ b/infrastructure/package-lock.json @@ -5,201 +5,202 @@ "requires": true, "dependencies": { "@aws-cdk/assert": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/assert/-/assert-1.54.0.tgz", - "integrity": "sha512-JJ5LXeKGzZzP3epMQQlpVGxNhoj+wsqWZehdsHyciRX+ZUeX3xz37EA3zoDEI3aq/yzN+MVvbl3LjAnfsdrQaw==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/assert/-/assert-1.59.0.tgz", + "integrity": "sha512-60AWONWt47O46GXOIa80jaUHwLG20pHM2gee0rwM1DHP4nOBcDBgRAIHP9qSCkASXSi/VuDbaXZPYpHohI0BUw==", "dev": true, "requires": { - "@aws-cdk/cloud-assembly-schema": "1.54.0", - "@aws-cdk/cloudformation-diff": "1.54.0", - "@aws-cdk/core": "1.54.0", - "@aws-cdk/cx-api": "1.54.0", + "@aws-cdk/cloud-assembly-schema": "1.59.0", + "@aws-cdk/cloudformation-diff": "1.59.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/cx-api": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/assets": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/assets/-/assets-1.54.0.tgz", - "integrity": "sha512-p8MKp2xw/Qyf9613Kf51sf2hv9Sd5bU7bOtQCBmSJ4zUpVSOIPn4HMwpIeU+dQwy8Oo+RrrtryEbn0XFn6W+jg==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/assets/-/assets-1.59.0.tgz", + "integrity": "sha512-CKkySASphv4kyszBYthKqSO9wiSBWRJc7woHM7+i1fy3pOGeUoyaHl/CpUyU/sT1eWmtOmGzMm0bI53vumH4ZQ==", "requires": { - "@aws-cdk/core": "1.54.0", - "@aws-cdk/cx-api": "1.54.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/cx-api": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-apigateway": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-apigateway/-/aws-apigateway-1.54.0.tgz", - "integrity": "sha512-dRcV9pZC68jOtP/jxIl7WmtT24aeeoJiqhSCBhT9UB3qdjK1jOmMsBP41zoPGTcvmFebDoF8Xd0mxJF4iVWVtQ==", - "requires": { - "@aws-cdk/assets": "1.54.0", - "@aws-cdk/aws-certificatemanager": "1.54.0", - "@aws-cdk/aws-ec2": "1.54.0", - "@aws-cdk/aws-elasticloadbalancingv2": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-lambda": "1.54.0", - "@aws-cdk/aws-logs": "1.54.0", - "@aws-cdk/aws-s3": "1.54.0", - "@aws-cdk/aws-s3-assets": "1.54.0", - "@aws-cdk/core": "1.54.0", - "@aws-cdk/cx-api": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-apigateway/-/aws-apigateway-1.59.0.tgz", + "integrity": "sha512-FUvc3OlnDoFn6hUTfORFUyrNsnkZ01v5fu0fhC8NDw+KC3Gb0ZrW1Rrs2sfKLSMB14Aks1YpgR240hsQEesD8g==", + "requires": { + "@aws-cdk/assets": "1.59.0", + "@aws-cdk/aws-certificatemanager": "1.59.0", + "@aws-cdk/aws-ec2": "1.59.0", + "@aws-cdk/aws-elasticloadbalancingv2": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-lambda": "1.59.0", + "@aws-cdk/aws-logs": "1.59.0", + "@aws-cdk/aws-s3": "1.59.0", + "@aws-cdk/aws-s3-assets": "1.59.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/cx-api": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-applicationautoscaling": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-applicationautoscaling/-/aws-applicationautoscaling-1.54.0.tgz", - "integrity": "sha512-KhXpgsXB4TGtkvxH4ilsoWHZgUY/ADR9hNnoipYepEt/zlhGyDzLr/O0vdehhBY4aBISQSK9EMMHRALftMKxwQ==", - "requires": { - "@aws-cdk/aws-autoscaling-common": "1.54.0", - "@aws-cdk/aws-cloudwatch": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-applicationautoscaling/-/aws-applicationautoscaling-1.59.0.tgz", + "integrity": "sha512-LdHqp9DPuH0i7K0873dvbf9/JG60LjuIbX+lynhvI6/0tokKtlTiZIegutMrH2Ck+ex6EHnnIgSgRHJ3whN3MA==", + "requires": { + "@aws-cdk/aws-autoscaling-common": "1.59.0", + "@aws-cdk/aws-cloudwatch": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-autoscaling": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-autoscaling/-/aws-autoscaling-1.54.0.tgz", - "integrity": "sha512-rjMDKxbg1iVXJO+cJw+jJ3tfEVoxZ1eMpnOARL4uTn6KmOJrZ8IMPsUNqRKFSRCnqzDTPZhVFLX0Ao8Nq+vMfw==", - "requires": { - "@aws-cdk/aws-autoscaling-common": "1.54.0", - "@aws-cdk/aws-cloudwatch": "1.54.0", - "@aws-cdk/aws-ec2": "1.54.0", - "@aws-cdk/aws-elasticloadbalancing": "1.54.0", - "@aws-cdk/aws-elasticloadbalancingv2": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-sns": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-autoscaling/-/aws-autoscaling-1.59.0.tgz", + "integrity": "sha512-dKK44VABZnIueEvo9BTYjFsZWUBndwJEPK2tflhX4yVWsM62gl4YjvhSmnTuoOdCbZwza3UL5oklJo4JNSTgpg==", + "requires": { + "@aws-cdk/aws-autoscaling-common": "1.59.0", + "@aws-cdk/aws-cloudwatch": "1.59.0", + "@aws-cdk/aws-ec2": "1.59.0", + "@aws-cdk/aws-elasticloadbalancing": "1.59.0", + "@aws-cdk/aws-elasticloadbalancingv2": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-sns": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-autoscaling-common": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-autoscaling-common/-/aws-autoscaling-common-1.54.0.tgz", - "integrity": "sha512-snCx7u0yBlsnmPH4pzefJh7MZZzMhTWjESqTXO0GpKTVRu4oaVqMeG/s1hnD5Sv3ThEMVVMyQncnNdgMSGgvkA==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-autoscaling-common/-/aws-autoscaling-common-1.59.0.tgz", + "integrity": "sha512-15HHOkZapAFctHfcNoMjjyA/yzDFVGpo18ce9kvB0GLDpexGjk/am4WCfrGXwUkUUDl1h5G4sNb9SrqtSDFeNA==", "requires": { - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/core": "1.54.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-certificatemanager": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-certificatemanager/-/aws-certificatemanager-1.54.0.tgz", - "integrity": "sha512-5AGIzpdAdgUMBDBmrFlmB0fnCLZSEM1za0Ure1FwMK0GjB0tYO2IYQqn438L7rXB7IG9HN4T7pu7GcfE7Q4pMw==", - "requires": { - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-lambda": "1.54.0", - "@aws-cdk/aws-route53": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-certificatemanager/-/aws-certificatemanager-1.59.0.tgz", + "integrity": "sha512-IyUG8vQM2Nat8m6Yp+v5k+WtnDRJQPSmRNNwEasnztgCy1GD5Xc+zZRKBq1qwPRzFo2eO/YryBXQl/c6Nz9Jgw==", + "requires": { + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-lambda": "1.59.0", + "@aws-cdk/aws-route53": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-cloudformation": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudformation/-/aws-cloudformation-1.54.0.tgz", - "integrity": "sha512-YTW0dPiIUY4wptKlQTFxAPWFLA8Fz8/fa22YCcsLE5ftdGStgSJkrikrqOKmx8eSSZ6OQGGhouRSYV1eqPioxQ==", - "requires": { - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-lambda": "1.54.0", - "@aws-cdk/aws-s3": "1.54.0", - "@aws-cdk/aws-sns": "1.54.0", - "@aws-cdk/core": "1.54.0", - "@aws-cdk/cx-api": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudformation/-/aws-cloudformation-1.59.0.tgz", + "integrity": "sha512-spIFELZPkbuqcPAFSm1P4He0X/aXaBNzWosIBNCJmZBxuz8NdZWhG27mE1bKdubiCylH2fDeu9Rqz3kIVdu2aw==", + "requires": { + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-lambda": "1.59.0", + "@aws-cdk/aws-s3": "1.59.0", + "@aws-cdk/aws-sns": "1.59.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/cx-api": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-cloudwatch": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudwatch/-/aws-cloudwatch-1.54.0.tgz", - "integrity": "sha512-+L+tp+XyPqiFBy3P6osOgqqCV/155phcGtZ660BCL5I5ykE08Q9/n6yA3wRh2BU7xZhH5zTYnJ6cIuRmIFcOWw==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cloudwatch/-/aws-cloudwatch-1.59.0.tgz", + "integrity": "sha512-dNy8lj7nlZw4EJGIXbruVlmrunD+isici+H6w+Y7Tsb43WYtK3MqBchcMZ0nFZly1DBd+J4O2a4ZsaqJQX3SSA==", "requires": { - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/core": "1.54.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-codeguruprofiler": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-codeguruprofiler/-/aws-codeguruprofiler-1.54.0.tgz", - "integrity": "sha512-VQIEjaliup+mtM+JERZuREtB8EcHJ5/zfH3bFUTeiUKvheXAIppGh9K8lq/z/PMHjsac6sH+XzozMdHj4Oe8bQ==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-codeguruprofiler/-/aws-codeguruprofiler-1.59.0.tgz", + "integrity": "sha512-fiiLKmjekVlIDbo5Qcxj9v2EQPus/FfBOH0r4biOJlBsjTtJQD/f5ufizy8tF1NCAIeP8oTrrRZ9OkI4owyQyg==", "requires": { - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/core": "1.54.0" + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/core": "1.59.0" } }, "@aws-cdk/aws-cognito": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cognito/-/aws-cognito-1.54.0.tgz", - "integrity": "sha512-/FEI2zMcoTNACOJ5Y6jDy88lFnxPU2uXVfGISJP4fx3Z64+JmdUWTfDY/Xa5Kk+Ev2n8EddIVsJ1Ah+aVLrb7Q==", - "requires": { - "@aws-cdk/aws-certificatemanager": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-lambda": "1.54.0", - "@aws-cdk/core": "1.54.0", - "@aws-cdk/custom-resources": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-cognito/-/aws-cognito-1.59.0.tgz", + "integrity": "sha512-PZojwlnDUncKnwJVQzY3UrDzmB56Xmp5Hf9e8C6TCNdVTNCeX+4qEaCgN9Mmmm/vFM+KWMn5QUOj+BQqA4wi+w==", + "requires": { + "@aws-cdk/aws-certificatemanager": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-lambda": "1.59.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/custom-resources": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-dynamodb": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-dynamodb/-/aws-dynamodb-1.54.0.tgz", - "integrity": "sha512-3BJf41r3Ncy0et1fyATVUnDnOHeOYaKAY/OOpo+5a9g59DCgUNp09XoTBFemXRwOKPOO1IDV8fX6CTmg8XXblA==", - "requires": { - "@aws-cdk/aws-applicationautoscaling": "1.54.0", - "@aws-cdk/aws-cloudwatch": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-kms": "1.54.0", - "@aws-cdk/aws-lambda": "1.54.0", - "@aws-cdk/core": "1.54.0", - "@aws-cdk/custom-resources": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-dynamodb/-/aws-dynamodb-1.59.0.tgz", + "integrity": "sha512-nFvhwOk0049nSuNDKYlNGo3qkIuDzS8HF6qSYzOsSoxbpdPejrRYOB5GTPC0DZ7Nc5XtUvei7HWn9C6lfigIxw==", + "requires": { + "@aws-cdk/aws-applicationautoscaling": "1.59.0", + "@aws-cdk/aws-cloudwatch": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-kms": "1.59.0", + "@aws-cdk/aws-lambda": "1.59.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/custom-resources": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-ec2": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-ec2/-/aws-ec2-1.54.0.tgz", - "integrity": "sha512-MZXDHf8jj//BBNQx+EwuocV5WH2njFQqzo2cF9tpTPPjd30prD5D8FCPi3RIdXafKdpQvTzor4cYPbYFKbdU+A==", - "requires": { - "@aws-cdk/aws-cloudwatch": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-kms": "1.54.0", - "@aws-cdk/aws-logs": "1.54.0", - "@aws-cdk/aws-s3": "1.54.0", - "@aws-cdk/aws-ssm": "1.54.0", - "@aws-cdk/cloud-assembly-schema": "1.54.0", - "@aws-cdk/core": "1.54.0", - "@aws-cdk/cx-api": "1.54.0", - "@aws-cdk/region-info": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-ec2/-/aws-ec2-1.59.0.tgz", + "integrity": "sha512-+jeY3Ju4QyJ60oroLSzWd9UNvJ3wBINwpVi8xKmAoxnkoKazIP9TUQO+e10t8fay7nZ4aZyceO0LauOIiqIipg==", + "requires": { + "@aws-cdk/aws-cloudwatch": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-kms": "1.59.0", + "@aws-cdk/aws-logs": "1.59.0", + "@aws-cdk/aws-s3": "1.59.0", + "@aws-cdk/aws-s3-assets": "1.59.0", + "@aws-cdk/aws-ssm": "1.59.0", + "@aws-cdk/cloud-assembly-schema": "1.59.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/cx-api": "1.59.0", + "@aws-cdk/region-info": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-efs": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-efs/-/aws-efs-1.54.0.tgz", - "integrity": "sha512-kHeH5cGonYrE1vwStnB1SwAX3XDZYDOiBlGxPz5s7x8TCeMdjRWhHP5Qow+h5Rt6UOocZJ+QV8G4fbXU2yZC1g==", - "requires": { - "@aws-cdk/aws-ec2": "1.54.0", - "@aws-cdk/aws-kms": "1.54.0", - "@aws-cdk/cloud-assembly-schema": "1.54.0", - "@aws-cdk/core": "1.54.0", - "@aws-cdk/cx-api": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-efs/-/aws-efs-1.59.0.tgz", + "integrity": "sha512-Z09Sz0bMJcQY8hjl1BHzmSoLBAcfDqtv1KDjyjsLIzcMcJHGJ4eOJPY5BLmrKOtQ7EuH1BoiT58qp0YIwhvtTQ==", + "requires": { + "@aws-cdk/aws-ec2": "1.59.0", + "@aws-cdk/aws-kms": "1.59.0", + "@aws-cdk/cloud-assembly-schema": "1.59.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/cx-api": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-eks": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-eks/-/aws-eks-1.54.0.tgz", - "integrity": "sha512-WwGlp1yoduV7jECHBkoWYD1EXD+xJRL+uqxzvGRpPponV936XuYP88eOxFoXJqvsSyX6k+61WTl8Vz+wxmKjSQ==", - "requires": { - "@aws-cdk/aws-autoscaling": "1.54.0", - "@aws-cdk/aws-ec2": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-lambda": "1.54.0", - "@aws-cdk/aws-ssm": "1.54.0", - "@aws-cdk/core": "1.54.0", - "@aws-cdk/custom-resources": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-eks/-/aws-eks-1.59.0.tgz", + "integrity": "sha512-pzWEFOTCFs6sFJpau9cc3yNAVOv/Hwwdsbru/8Jt2HRwjoIo8pEPLK7Xl+wrf6k74ClIvbWNnT1QrZwXwrutKQ==", + "requires": { + "@aws-cdk/aws-autoscaling": "1.59.0", + "@aws-cdk/aws-ec2": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-lambda": "1.59.0", + "@aws-cdk/aws-ssm": "1.59.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/custom-resources": "1.59.0", "constructs": "^3.0.2", "yaml": "1.10.0" }, @@ -211,249 +212,250 @@ } }, "@aws-cdk/aws-elasticloadbalancing": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-elasticloadbalancing/-/aws-elasticloadbalancing-1.54.0.tgz", - "integrity": "sha512-B1f8DH+9TVfAWq0ZDIpIPsoOkSavTuuWI6dF3fWGCDEHFij08frutlcXROC5iHaY4aS+TTrmUC6Iu1CmUEXAtw==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-elasticloadbalancing/-/aws-elasticloadbalancing-1.59.0.tgz", + "integrity": "sha512-7rzZvpKrQ8xfzs6tQdpohTrwLwMky3gWfOX4b1usZPgZuLuA1764CLahofl6uCaTsegVPV55nL0yVsFJvvN7Zw==", "requires": { - "@aws-cdk/aws-ec2": "1.54.0", - "@aws-cdk/core": "1.54.0", + "@aws-cdk/aws-ec2": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-elasticloadbalancingv2": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-elasticloadbalancingv2/-/aws-elasticloadbalancingv2-1.54.0.tgz", - "integrity": "sha512-b/SMpM97C3bQCAAdANW90JfIxfZ1FCZn15v7bwoDyUtoO6M+HmnYfdd8wHCbHnKj/gE0UAq79x83/wwVgf8Ukw==", - "requires": { - "@aws-cdk/aws-certificatemanager": "1.54.0", - "@aws-cdk/aws-cloudwatch": "1.54.0", - "@aws-cdk/aws-ec2": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-lambda": "1.54.0", - "@aws-cdk/aws-s3": "1.54.0", - "@aws-cdk/core": "1.54.0", - "@aws-cdk/region-info": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-elasticloadbalancingv2/-/aws-elasticloadbalancingv2-1.59.0.tgz", + "integrity": "sha512-a5hgfXVUFIZXn3YCRAJFVvP8/eRbMEDjL07W01DP3+pBZePwdvaiJ6oh7trs666ThTAlgxkEqtrtqLMluNLaig==", + "requires": { + "@aws-cdk/aws-certificatemanager": "1.59.0", + "@aws-cdk/aws-cloudwatch": "1.59.0", + "@aws-cdk/aws-ec2": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-lambda": "1.59.0", + "@aws-cdk/aws-s3": "1.59.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/region-info": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-events": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-events/-/aws-events-1.54.0.tgz", - "integrity": "sha512-l45QoTocCC182lXAf2uozpqU7Y+P7NKtUQ3xVXV+9f9Aldjq6vPDml8MxyWGn5liKTcqRzekL4gOoQbUG8Effg==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-events/-/aws-events-1.59.0.tgz", + "integrity": "sha512-WEgJgwcziiab1uIe2Ag7UODJPKnb/FkwyaLnm8IFBb/4Y6XL6tajmq+MKSmHARzUWnKZbAl3gV/Lioh+VM2udg==", "requires": { - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/core": "1.54.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-iam": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-iam/-/aws-iam-1.54.0.tgz", - "integrity": "sha512-oS2W85oDITpaLqIWAGM+ADBW+csRDkz2n46dkdfoEq0m2Y5ZQESvXi7J9eWjcDu1ofDDDVDl3snbWJTnfW3+Hg==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-iam/-/aws-iam-1.59.0.tgz", + "integrity": "sha512-CpUX0k6ynHuWIQsdOfp7a6wJYwwfpbP144mfJfNj0yPiCO6k2vHsup1zfTC6m6vn44cNrUTwnQ0t1r+iWdvzdw==", "requires": { - "@aws-cdk/core": "1.54.0", - "@aws-cdk/region-info": "1.54.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/region-info": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-kinesis": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-kinesis/-/aws-kinesis-1.54.0.tgz", - "integrity": "sha512-plTQh+FjyvjUxWIXNaP6aOphY82O+quZquC0/OgSE5ZEaGOx1qD+dL4hXqQePQevwLTOmk82/2di2TbNGTsyRw==", - "requires": { - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-kms": "1.54.0", - "@aws-cdk/aws-logs": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-kinesis/-/aws-kinesis-1.59.0.tgz", + "integrity": "sha512-M1UOkq4n6r0hxAYTJO2R9BhdOqu19mbT9Hwyl6Xg32kpIKN61XoZjMXJdX6WtOKar3Pst1yjA0fbPgQXMZUfaA==", + "requires": { + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-kms": "1.59.0", + "@aws-cdk/aws-logs": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-kms": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-kms/-/aws-kms-1.54.0.tgz", - "integrity": "sha512-9BL6bPfj0KPtVTp7sm2oplTUlVp3DqWz/NPd9xQcQEW4VPddYTxP+a/hRDSjTYQtE84Gt2+EXT1pjVBcqNpw2A==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-kms/-/aws-kms-1.59.0.tgz", + "integrity": "sha512-5qUc1DShsJ1AjZnkC0aRpmp9bgwwFO8/WOaXefhGN+VR3TFfwt8Ad9anG2zakXMw5DmYhhvdnj+9uu5M06XVJw==", "requires": { - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/core": "1.54.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-lambda": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-lambda/-/aws-lambda-1.54.0.tgz", - "integrity": "sha512-YF/s/2aDTtLSCjRBdH93DrrNdk0HoxSbanIxh25hddbuGMm7se+wS4qtmyyWL/05J/o/BGbWQWOLMUGj1gmaGQ==", - "requires": { - "@aws-cdk/aws-cloudwatch": "1.54.0", - "@aws-cdk/aws-codeguruprofiler": "1.54.0", - "@aws-cdk/aws-ec2": "1.54.0", - "@aws-cdk/aws-efs": "1.54.0", - "@aws-cdk/aws-events": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-logs": "1.54.0", - "@aws-cdk/aws-s3": "1.54.0", - "@aws-cdk/aws-s3-assets": "1.54.0", - "@aws-cdk/aws-sqs": "1.54.0", - "@aws-cdk/core": "1.54.0", - "@aws-cdk/cx-api": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-lambda/-/aws-lambda-1.59.0.tgz", + "integrity": "sha512-f2uq/9S/xcHSYQG19jYD75ac/puRfoQ9yYWyRIhmkx4NXD2r9NF1WGYRMNVO+zVpGqt7/yJlAwz3wTvFfGijAA==", + "requires": { + "@aws-cdk/aws-applicationautoscaling": "1.59.0", + "@aws-cdk/aws-cloudwatch": "1.59.0", + "@aws-cdk/aws-codeguruprofiler": "1.59.0", + "@aws-cdk/aws-ec2": "1.59.0", + "@aws-cdk/aws-efs": "1.59.0", + "@aws-cdk/aws-events": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-logs": "1.59.0", + "@aws-cdk/aws-s3": "1.59.0", + "@aws-cdk/aws-s3-assets": "1.59.0", + "@aws-cdk/aws-sqs": "1.59.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/cx-api": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-lambda-event-sources": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-lambda-event-sources/-/aws-lambda-event-sources-1.54.0.tgz", - "integrity": "sha512-/xyJOP/Uh/9818TDF9FRldltT0sFVjroZdRt+PmwUwG9YW5EHNfVPG6g5gt3GRqDtFXWGKA5TsXz8YTYOO7Uzg==", - "requires": { - "@aws-cdk/aws-apigateway": "1.54.0", - "@aws-cdk/aws-dynamodb": "1.54.0", - "@aws-cdk/aws-events": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-kinesis": "1.54.0", - "@aws-cdk/aws-lambda": "1.54.0", - "@aws-cdk/aws-s3": "1.54.0", - "@aws-cdk/aws-s3-notifications": "1.54.0", - "@aws-cdk/aws-sns": "1.54.0", - "@aws-cdk/aws-sns-subscriptions": "1.54.0", - "@aws-cdk/aws-sqs": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-lambda-event-sources/-/aws-lambda-event-sources-1.59.0.tgz", + "integrity": "sha512-7kZVipCuAIoYJPHNoGbD8zHSydHRwXcwyfQeuCK1hCpIK3R9mmScRXE07Ll36UXyGL6N1qxwowyukyF98sO3GA==", + "requires": { + "@aws-cdk/aws-apigateway": "1.59.0", + "@aws-cdk/aws-dynamodb": "1.59.0", + "@aws-cdk/aws-events": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-kinesis": "1.59.0", + "@aws-cdk/aws-lambda": "1.59.0", + "@aws-cdk/aws-s3": "1.59.0", + "@aws-cdk/aws-s3-notifications": "1.59.0", + "@aws-cdk/aws-sns": "1.59.0", + "@aws-cdk/aws-sns-subscriptions": "1.59.0", + "@aws-cdk/aws-sqs": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-logs": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-logs/-/aws-logs-1.54.0.tgz", - "integrity": "sha512-VjEzm0HD8rwOsH3qe4ZA37eMjQbO0ZxmyMgXtysET9hYs+i0Iwr5cZ/T/FHSY/OBc0BOHJXi/Qx7W8/WARH9eQ==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-logs/-/aws-logs-1.59.0.tgz", + "integrity": "sha512-dpMx7W8KBa2MRd3Km+o+SNvUp600hS1XikvEtKTP/Z2rlWlx023u03qJ6WTcv2E1rZpoEq0V7PELzvBiaWzvmQ==", "requires": { - "@aws-cdk/aws-cloudwatch": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/core": "1.54.0", + "@aws-cdk/aws-cloudwatch": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-route53": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-route53/-/aws-route53-1.54.0.tgz", - "integrity": "sha512-8zJk4ljSWpf0HXUtPw+aFB0BZCzewybAtKvd85kmJvKGk4GZLAJdz5EWRchksznxpwO30J+5T7DC4Q/8QuN1Pw==", - "requires": { - "@aws-cdk/aws-ec2": "1.54.0", - "@aws-cdk/aws-logs": "1.54.0", - "@aws-cdk/cloud-assembly-schema": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-route53/-/aws-route53-1.59.0.tgz", + "integrity": "sha512-NrnZ+CndRBqkGuW9mdxv8AVeT+cxkZQg1J0VcB1slgyR8tFca75EUQmcXhWu/4fLyrrU/5KUeKM4IMYxzMe7tw==", + "requires": { + "@aws-cdk/aws-ec2": "1.59.0", + "@aws-cdk/aws-logs": "1.59.0", + "@aws-cdk/cloud-assembly-schema": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-s3": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3/-/aws-s3-1.54.0.tgz", - "integrity": "sha512-dMcHtvoAyCTuh74fPY7Y7R+PW3T+vyUZNpDKgaj4I//Q96ybb3YcXyrptIXMNDAWVPL8GNIcEAGfpY5wYLE0qQ==", - "requires": { - "@aws-cdk/aws-events": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-kms": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3/-/aws-s3-1.59.0.tgz", + "integrity": "sha512-hdxCBvxVqhzrJ7q6zPV3M6Xcvx0FOrZfIq/608ZnV9uASMUqoWoyssKC8BPxLuQmJg9lMHNOTvz4JEo/tmcINQ==", + "requires": { + "@aws-cdk/aws-events": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-kms": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-s3-assets": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3-assets/-/aws-s3-assets-1.54.0.tgz", - "integrity": "sha512-IMGX3mqQyF4OurYh+Pu+HeImlvcOSaeq9WfiH31NzWvHNiR7Zuh2MvRG7r1lo9VNtS4Ttk1nXJFBe2W0yTFDrA==", - "requires": { - "@aws-cdk/assets": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-kms": "1.54.0", - "@aws-cdk/aws-s3": "1.54.0", - "@aws-cdk/core": "1.54.0", - "@aws-cdk/cx-api": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3-assets/-/aws-s3-assets-1.59.0.tgz", + "integrity": "sha512-rkmLi9vXLgYUuPy4a1pnBXxVdDP0KklFmT8zkVhy2jb0ZOPHdQAWNskyU03O6hEfWHTAdjNA+f8OlrfOxYXZ+g==", + "requires": { + "@aws-cdk/assets": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-kms": "1.59.0", + "@aws-cdk/aws-s3": "1.59.0", + "@aws-cdk/core": "1.59.0", + "@aws-cdk/cx-api": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-s3-notifications": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3-notifications/-/aws-s3-notifications-1.54.0.tgz", - "integrity": "sha512-9JnqM3b9itcG8kvOFV59/UsRRoIH7h/fBHaL+e/+D6d70+zkmXEn8157NuDWotoU8+of68zaji6rq4fMy98sVw==", - "requires": { - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-lambda": "1.54.0", - "@aws-cdk/aws-s3": "1.54.0", - "@aws-cdk/aws-sns": "1.54.0", - "@aws-cdk/aws-sqs": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-s3-notifications/-/aws-s3-notifications-1.59.0.tgz", + "integrity": "sha512-W3kDM2cFP5WaNUMNyy7UgfpzzWfqm9pLAlj059ytqrk/Xy7xi0yH4ByiuwFOsgHJ1RF8ggWNrSEh4swo38msjw==", + "requires": { + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-lambda": "1.59.0", + "@aws-cdk/aws-s3": "1.59.0", + "@aws-cdk/aws-sns": "1.59.0", + "@aws-cdk/aws-sqs": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-sam": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sam/-/aws-sam-1.54.0.tgz", - "integrity": "sha512-brICLV616UiXTNOCvvlC7enwvcKaiZLtT7VEPQTf3eiJp9VDs4RfNSMefm5wIkfmHLWPHvRlexPFaZb/PeU7UA==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sam/-/aws-sam-1.59.0.tgz", + "integrity": "sha512-TgZYIVFY9Xr3MbgV0St3N1p8lDVPXJZvCbBr0CrGRdsWJdQwDt+ZwWoy0bCfKV2v0A1JUj3jPkWl3VobbDUFtw==", "requires": { - "@aws-cdk/core": "1.54.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-sns": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sns/-/aws-sns-1.54.0.tgz", - "integrity": "sha512-bmP7Z0i26Yq1Ix0yaz+tjJaersLZMOj/WDhfkpaucyPlTCj/hV2BQSGUMWgl3QgadapUPZVDv9DBiqvjLlTPMA==", - "requires": { - "@aws-cdk/aws-cloudwatch": "1.54.0", - "@aws-cdk/aws-events": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-kms": "1.54.0", - "@aws-cdk/aws-sqs": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sns/-/aws-sns-1.59.0.tgz", + "integrity": "sha512-q2iOd7rgkrhZ4MqXxEp3JwcxOos2RczY/U9slhmSZdkE/3eBsJr17Qor0q1Gic2Z3jazC1t8QpHxaEbLck3vYQ==", + "requires": { + "@aws-cdk/aws-cloudwatch": "1.59.0", + "@aws-cdk/aws-events": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-kms": "1.59.0", + "@aws-cdk/aws-sqs": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-sns-subscriptions": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sns-subscriptions/-/aws-sns-subscriptions-1.54.0.tgz", - "integrity": "sha512-iizYWPCRbRcPu9IvZTprybFAyBYz/bxJ9qY8YNg13zei4DvmE0KSK7To6ljSY0xXiFjJt4BNVI8BsYrXYOMYrg==", - "requires": { - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-lambda": "1.54.0", - "@aws-cdk/aws-sns": "1.54.0", - "@aws-cdk/aws-sqs": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sns-subscriptions/-/aws-sns-subscriptions-1.59.0.tgz", + "integrity": "sha512-ew06PMC8Nyo++rMGoKdA87HNcctyoR0oatNjw3483QUUYRxgio/kLWrbcFX4+iwxnVwKKOOD6aTZmQWb7u9/8w==", + "requires": { + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-lambda": "1.59.0", + "@aws-cdk/aws-sns": "1.59.0", + "@aws-cdk/aws-sqs": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-sqs": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sqs/-/aws-sqs-1.54.0.tgz", - "integrity": "sha512-ykW2CWbIgvKqUGjrhMCCqOYJ2HUYijTtRcRFdrPD0BrSixcwWW4LW/PpJhDgOjfAw20TS1k3ivYl4pcfg2XWQw==", - "requires": { - "@aws-cdk/aws-cloudwatch": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-kms": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-sqs/-/aws-sqs-1.59.0.tgz", + "integrity": "sha512-8eT1CxxvE2EUqAADiBGmPi4WCJndjrcPAUlc+LODa5qFV4wdANKdZyfYDJ9an4yzJObPe6IdtigLvn3jI7Hxfg==", + "requires": { + "@aws-cdk/aws-cloudwatch": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-kms": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/aws-ssm": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/aws-ssm/-/aws-ssm-1.54.0.tgz", - "integrity": "sha512-i66zvnOpEpTONI/kKeH4rBg9Lr3w5Z9N+dPJRkqZRqv/f3n2vegPzYX1o3C2eBuVeOCcbgj8775khJO8PbBlww==", - "requires": { - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-kms": "1.54.0", - "@aws-cdk/cloud-assembly-schema": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-ssm/-/aws-ssm-1.59.0.tgz", + "integrity": "sha512-oGHTXoRqG5nuCgcjsA1tq/Z5snCUXXWU1vBioL/8selhukV4HEMm5O03FJiPAEQ31U196PkrRqludG8JJMv8pA==", + "requires": { + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-kms": "1.59.0", + "@aws-cdk/cloud-assembly-schema": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/cfnspec": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cfnspec/-/cfnspec-1.54.0.tgz", - "integrity": "sha512-0cel4Z9K3MZfMLuzapGN6qKTv15vQkDb/DMGRMJe7P4h/pkJHYbROv/uiWXx+b5uCUQxLjJYCxnJlJbK0tK8wA==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cfnspec/-/cfnspec-1.59.0.tgz", + "integrity": "sha512-Th296cIiWQk2HTtUoA3Tk/2IoMscb+kQrHTmyvh4VIUfoIR6F3gJXTN5+M4hnV4JOHI7QzVlXFTlveL9zENN4w==", "dev": true, "requires": { "md5": "^2.2.1" } }, "@aws-cdk/cloud-assembly-schema": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.54.0.tgz", - "integrity": "sha512-9E4td0R9N/r1fjQaVdeDxVGFqoJNlXMGt0EM2wwiC5JjJj7MkdPVOO0qcvwPdZuYHZMVr/UFAHFjTQBrj6LCEA==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.59.0.tgz", + "integrity": "sha512-RG3jWYmDeqB66U4U+vOoQGjib0wjBwMew3aMElWItcNV8uOfZTPIUUQYItr6/Z78lmyi/qzVjtOWWWzVczEXfA==", "requires": { "jsonschema": "^1.2.5", "semver": "^7.2.2" @@ -470,12 +472,12 @@ } }, "@aws-cdk/cloudformation-diff": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloudformation-diff/-/cloudformation-diff-1.54.0.tgz", - "integrity": "sha512-K97QZS+kPnao/E08mkWCZIdwG4M/WP8Obm1kSvTsH1QADcE5tmmEQNq+G4rVd6J3Xik0wbKtNgfh0iFcUuof/g==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloudformation-diff/-/cloudformation-diff-1.59.0.tgz", + "integrity": "sha512-0PeDQuvKHp54//claazbnIYvtUlKmW8IAoRLADgU9DyMCHBxyqNNsJrKdC3OxKlgJJiRRVgVZ2qQkmVlbmB73A==", "dev": true, "requires": { - "@aws-cdk/cfnspec": "1.54.0", + "@aws-cdk/cfnspec": "1.59.0", "colors": "^1.4.0", "diff": "^4.0.2", "fast-deep-equal": "^3.1.3", @@ -492,12 +494,12 @@ } }, "@aws-cdk/core": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/core/-/core-1.54.0.tgz", - "integrity": "sha512-LqHIDudkJp9//7Crfr00l2Z5U+4xkEIiRcEXXNkwEnV2iPzuQErCoDJ+JiQj2kOk3EFbbVpTBPI+6LV1nKSLuw==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/core/-/core-1.59.0.tgz", + "integrity": "sha512-BULP1wGAFBbYUk3CNWW39wX3ZVtA/hS5XxfOU0upo7rNOTk2QqgS8JW4YdAJFNQ0Nd5GrUYSeNfXhM92qFfnkA==", "requires": { - "@aws-cdk/cloud-assembly-schema": "1.54.0", - "@aws-cdk/cx-api": "1.54.0", + "@aws-cdk/cloud-assembly-schema": "1.59.0", + "@aws-cdk/cx-api": "1.59.0", "constructs": "^3.0.2", "fs-extra": "^9.0.1", "minimatch": "^3.0.4" @@ -559,25 +561,25 @@ } }, "@aws-cdk/custom-resources": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/custom-resources/-/custom-resources-1.54.0.tgz", - "integrity": "sha512-pTZu5PH3UUX/53fFuIQamidmswaC2JMw97KBXWpFLz1WBPA2Jvb2Y67yiy814HZdz9WdscccIQIcCFh8ms4c7g==", - "requires": { - "@aws-cdk/aws-cloudformation": "1.54.0", - "@aws-cdk/aws-iam": "1.54.0", - "@aws-cdk/aws-lambda": "1.54.0", - "@aws-cdk/aws-logs": "1.54.0", - "@aws-cdk/aws-sns": "1.54.0", - "@aws-cdk/core": "1.54.0", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/custom-resources/-/custom-resources-1.59.0.tgz", + "integrity": "sha512-nhxyzFTikODnszfbRUfYwlGSXNx9MUBcu8L2EpSBeSDkBSisMM+rstKbW2HcmhiTPZurEsA7Nmx049R4rEhkMg==", + "requires": { + "@aws-cdk/aws-cloudformation": "1.59.0", + "@aws-cdk/aws-iam": "1.59.0", + "@aws-cdk/aws-lambda": "1.59.0", + "@aws-cdk/aws-logs": "1.59.0", + "@aws-cdk/aws-sns": "1.59.0", + "@aws-cdk/core": "1.59.0", "constructs": "^3.0.2" } }, "@aws-cdk/cx-api": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.54.0.tgz", - "integrity": "sha512-LVskfK1wcL7IhiyY74cQM1JZn4y08IGKvPEldnQVx+OlvB4BDsXRIT4pyRgnGYNWDI5yaQqi+M2D5zqqaLcN+g==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.59.0.tgz", + "integrity": "sha512-3FHhwiZGR4dus7SrJ04IQ8nb5zVidX2j5Ahs75oCz9vyNze4/qLbVJBe/0KHBgHwfKrQ3b05J8ytxgZXeezFKw==", "requires": { - "@aws-cdk/cloud-assembly-schema": "1.54.0", + "@aws-cdk/cloud-assembly-schema": "1.59.0", "semver": "^7.2.2" }, "dependencies": { @@ -588,9 +590,9 @@ } }, "@aws-cdk/region-info": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/region-info/-/region-info-1.54.0.tgz", - "integrity": "sha512-nEhNfDRY7AZOt8ebRdGjVPBRu1X25UTqsHbzgPBk+4Ov4CfgAi06onoacGgyTw4WzLikAdVTtRHJULmI158clg==" + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/region-info/-/region-info-1.59.0.tgz", + "integrity": "sha512-RNvLd670L8oXiOKO7QBoenJN8uIKJ54g5YTLlcDve6H1M0g7+IhmY2FNnDlJXnAU3yTNXUQo+ZflbvrYs7L4GQ==" }, "@babel/code-frame": { "version": "7.10.1", @@ -1964,19 +1966,19 @@ "dev": true }, "aws-cdk": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-1.54.0.tgz", - "integrity": "sha512-nHtf/c4bMShNvnkyV2Jpvq7v6fGhfELsdABChXX3Yjal37Jso8iOq3LJV1hxusad9Dt+79cNKc8P2axNjLuGdQ==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-1.59.0.tgz", + "integrity": "sha512-RSGcn2ZfRQ83R8uR1Ooo3eiKlVSmeI2EDLaoRoaVyDtAeQIe1u/TrFk6R18Xslhr+079ip7Ii6w20/RaYpnrpg==", "dev": true, "requires": { - "@aws-cdk/cloud-assembly-schema": "1.54.0", - "@aws-cdk/cloudformation-diff": "1.54.0", - "@aws-cdk/cx-api": "1.54.0", - "@aws-cdk/region-info": "1.54.0", + "@aws-cdk/cloud-assembly-schema": "1.59.0", + "@aws-cdk/cloudformation-diff": "1.59.0", + "@aws-cdk/cx-api": "1.59.0", + "@aws-cdk/region-info": "1.59.0", "archiver": "^4.0.2", "aws-sdk": "^2.715.0", "camelcase": "^6.0.0", - "cdk-assets": "1.54.0", + "cdk-assets": "1.59.0", "colors": "^1.4.0", "decamelize": "^4.0.0", "fs-extra": "^9.0.1", @@ -1995,18 +1997,18 @@ }, "dependencies": { "@aws-cdk/cfnspec": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cfnspec/-/cfnspec-1.54.0.tgz", - "integrity": "sha512-0cel4Z9K3MZfMLuzapGN6qKTv15vQkDb/DMGRMJe7P4h/pkJHYbROv/uiWXx+b5uCUQxLjJYCxnJlJbK0tK8wA==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cfnspec/-/cfnspec-1.59.0.tgz", + "integrity": "sha512-Th296cIiWQk2HTtUoA3Tk/2IoMscb+kQrHTmyvh4VIUfoIR6F3gJXTN5+M4hnV4JOHI7QzVlXFTlveL9zENN4w==", "dev": true, "requires": { "md5": "^2.2.1" } }, "@aws-cdk/cloud-assembly-schema": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.54.0.tgz", - "integrity": "sha512-9E4td0R9N/r1fjQaVdeDxVGFqoJNlXMGt0EM2wwiC5JjJj7MkdPVOO0qcvwPdZuYHZMVr/UFAHFjTQBrj6LCEA==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.59.0.tgz", + "integrity": "sha512-RG3jWYmDeqB66U4U+vOoQGjib0wjBwMew3aMElWItcNV8uOfZTPIUUQYItr6/Z78lmyi/qzVjtOWWWzVczEXfA==", "dev": true, "requires": { "jsonschema": "^1.2.5", @@ -2014,12 +2016,12 @@ } }, "@aws-cdk/cloudformation-diff": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloudformation-diff/-/cloudformation-diff-1.54.0.tgz", - "integrity": "sha512-K97QZS+kPnao/E08mkWCZIdwG4M/WP8Obm1kSvTsH1QADcE5tmmEQNq+G4rVd6J3Xik0wbKtNgfh0iFcUuof/g==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloudformation-diff/-/cloudformation-diff-1.59.0.tgz", + "integrity": "sha512-0PeDQuvKHp54//claazbnIYvtUlKmW8IAoRLADgU9DyMCHBxyqNNsJrKdC3OxKlgJJiRRVgVZ2qQkmVlbmB73A==", "dev": true, "requires": { - "@aws-cdk/cfnspec": "1.54.0", + "@aws-cdk/cfnspec": "1.59.0", "colors": "^1.4.0", "diff": "^4.0.2", "fast-deep-equal": "^3.1.3", @@ -2028,19 +2030,19 @@ } }, "@aws-cdk/cx-api": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.54.0.tgz", - "integrity": "sha512-LVskfK1wcL7IhiyY74cQM1JZn4y08IGKvPEldnQVx+OlvB4BDsXRIT4pyRgnGYNWDI5yaQqi+M2D5zqqaLcN+g==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.59.0.tgz", + "integrity": "sha512-3FHhwiZGR4dus7SrJ04IQ8nb5zVidX2j5Ahs75oCz9vyNze4/qLbVJBe/0KHBgHwfKrQ3b05J8ytxgZXeezFKw==", "dev": true, "requires": { - "@aws-cdk/cloud-assembly-schema": "1.54.0", + "@aws-cdk/cloud-assembly-schema": "1.59.0", "semver": "^7.2.2" } }, "@aws-cdk/region-info": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/region-info/-/region-info-1.54.0.tgz", - "integrity": "sha512-nEhNfDRY7AZOt8ebRdGjVPBRu1X25UTqsHbzgPBk+4Ov4CfgAi06onoacGgyTw4WzLikAdVTtRHJULmI158clg==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/region-info/-/region-info-1.59.0.tgz", + "integrity": "sha512-RNvLd670L8oXiOKO7QBoenJN8uIKJ54g5YTLlcDve6H1M0g7+IhmY2FNnDlJXnAU3yTNXUQo+ZflbvrYs7L4GQ==", "dev": true }, "@types/color-name": { @@ -2310,32 +2312,23 @@ "dev": true }, "cdk-assets": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/cdk-assets/-/cdk-assets-1.54.0.tgz", - "integrity": "sha512-NI3Aajt/SBO21Pe/H+61403cwDAUq3XKlrJdp8zvf8wz9KdGzZMkoRmpjq7hnKBy5F8FiywMI7e2xpE8Yb8LLQ==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/cdk-assets/-/cdk-assets-1.59.0.tgz", + "integrity": "sha512-kckw91EOhT5depR4SsarrASjqS+uF1KUFJAH0TqqQG8qJbPaHYYQt2on/UWvVXPQEl8TOb7CK9Uq+2vuzL4Y1w==", "dev": true, "requires": { - "@aws-cdk/cloud-assembly-schema": "1.54.0", - "@aws-cdk/cx-api": "1.54.0", + "@aws-cdk/cloud-assembly-schema": "1.59.0", + "@aws-cdk/cx-api": "1.59.0", "archiver": "^4.0.2", "aws-sdk": "^2.715.0", "glob": "^7.1.6", "yargs": "^15.3.1" }, "dependencies": { - "@aws-cdk/cdk-assets-schema": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cdk-assets-schema/-/cdk-assets-schema-1.46.0.tgz", - "integrity": "sha512-5YM/WHdfiiXkyN+oqPWIcrU7nQUzEVRmViiN+SGy/NZ6Tj9r30N9YygYMZO8z9sM7r20dOTl+pY9SYrclIeNUQ==", - "dev": true, - "requires": { - "semver": "^7.2.2" - } - }, "@aws-cdk/cloud-assembly-schema": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.46.0.tgz", - "integrity": "sha512-ji92c9RCMoY426vH1RbLstUOiUYqM84qa4Ww9Nsbn6Aw3qIS1Gz603dNgcIl+rK78SOq6WqlgykSyFAFykqVgA==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.59.0.tgz", + "integrity": "sha512-RG3jWYmDeqB66U4U+vOoQGjib0wjBwMew3aMElWItcNV8uOfZTPIUUQYItr6/Z78lmyi/qzVjtOWWWzVczEXfA==", "dev": true, "requires": { "jsonschema": "^1.2.5", @@ -2343,12 +2336,12 @@ } }, "@aws-cdk/cx-api": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.46.0.tgz", - "integrity": "sha512-3U4wbywHlYLqWllCNOVGqxDee3N5PxaQHkjpC3v0vhI+LPwcJEW1Vz11k3PMG6FaPcrLrfkAn1y7HSFsPXh8Qg==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.59.0.tgz", + "integrity": "sha512-3FHhwiZGR4dus7SrJ04IQ8nb5zVidX2j5Ahs75oCz9vyNze4/qLbVJBe/0KHBgHwfKrQ3b05J8ytxgZXeezFKw==", "dev": true, "requires": { - "@aws-cdk/cloud-assembly-schema": "1.46.0", + "@aws-cdk/cloud-assembly-schema": "1.59.0", "semver": "^7.2.2" } }, @@ -2375,13 +2368,13 @@ } }, "archiver": { - "version": "4.0.1", - "resolved": "https://registry.yarnpkg.com/archiver/-/archiver-4.0.1.tgz#3f722b121777e361ca9fad374ecda38e77e63c7f", - "integrity": "sha512-/YV1pU4Nhpf/rJArM23W6GTUjT0l++VbjykrCRua1TSXrn+yM8Qs7XvtwSiRse0iCe49EPNf7ktXnPsWuSb91Q==", + "version": "4.0.2", + "resolved": "https://registry.yarnpkg.com/archiver/-/archiver-4.0.2.tgz#43c72865eadb4ddaaa2fb74852527b6a450d927c", + "integrity": "sha512-B9IZjlGwaxF33UN4oPbfBkyA4V1SxNLeIhR1qY8sRXSsbdUkEHrrOvwlYFPx+8uQeCe9M+FG6KgO+imDmQ79CQ==", "dev": true, "requires": { "archiver-utils": "^2.1.0", - "async": "^2.6.3", + "async": "^3.2.0", "buffer-crc32": "^0.2.1", "glob": "^7.1.6", "readable-stream": "^3.6.0", @@ -2436,18 +2429,15 @@ } }, "async": { - "version": "2.6.3", - "resolved": "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } + "version": "3.2.0", + "resolved": "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", + "dev": true }, "aws-sdk": { - "version": "2.699.0", - "resolved": "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.699.0.tgz#e77b6ffa4c860882e2779c060b74fab33d42f554", - "integrity": "sha512-EC431z/+i/cJgOgnDpOJ8Fa6+p7Oo1vIvdm/uJqP9tJX3+pxi/M/tvQavfz4yAlLBFqjQwxa8nrPisby0Mr5MQ==", + "version": "2.715.0", + "resolved": "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.715.0.tgz#b890892098e0a4d9e7189ed341267d4a9a6e856b", + "integrity": "sha512-O6ytb66IXFCowp0Ng2bSPM6v/cZVOhjJWFTR1CG4ieG4IroAaVgB3YQKkfPKA0Cy9B/Ovlsm7B737iuroKsd0w==", "dev": true, "requires": { "buffer": "4.9.2", @@ -2724,9 +2714,9 @@ } }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.3", + "resolved": "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, "ieee754": { @@ -2793,12 +2783,6 @@ "p-locate": "^4.1.0" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c", @@ -8374,14 +8358,14 @@ } }, "md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", "dev": true, "requires": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" } }, "merge-stream": { diff --git a/infrastructure/package.json b/infrastructure/package.json index be0ac5a..5142ecd 100644 --- a/infrastructure/package.json +++ b/infrastructure/package.json @@ -15,12 +15,12 @@ "cdk": "cdk" }, "devDependencies": { - "@aws-cdk/assert": "1.54.0", + "@aws-cdk/assert": "1.59.0", "@types/jest": "^26.0.0", "@types/node": "14.0.13", "@typescript-eslint/eslint-plugin": "^3.3.0", "@typescript-eslint/parser": "^3.3.0", - "aws-cdk": "1.54.0", + "aws-cdk": "1.59.0", "eslint": "^7.3.0", "eslint-plugin-notice": "^0.9.10", "jest": "^26.0.1", @@ -29,16 +29,16 @@ "typescript": "~3.9.5" }, "dependencies": { - "@aws-cdk/aws-apigateway": "^1.54.0", - "@aws-cdk/aws-dynamodb": "^1.54.0", - "@aws-cdk/aws-cognito": "^1.54.0", - "@aws-cdk/aws-ec2": "^1.54.0", - "@aws-cdk/aws-eks": "^1.54.0", - "@aws-cdk/aws-iam": "^1.54.0", - "@aws-cdk/aws-lambda": "^1.54.0", - "@aws-cdk/aws-lambda-event-sources": "^1.54.0", - "@aws-cdk/aws-sam": "^1.54.0", - "@aws-cdk/aws-sqs": "^1.54.0", - "@aws-cdk/core": "^1.54.0" + "@aws-cdk/aws-apigateway": "^1.59.0", + "@aws-cdk/aws-dynamodb": "^1.59.0", + "@aws-cdk/aws-cognito": "^1.59.0", + "@aws-cdk/aws-ec2": "^1.59.0", + "@aws-cdk/aws-eks": "^1.59.0", + "@aws-cdk/aws-iam": "^1.59.0", + "@aws-cdk/aws-lambda": "^1.59.0", + "@aws-cdk/aws-lambda-event-sources": "^1.59.0", + "@aws-cdk/aws-sam": "^1.59.0", + "@aws-cdk/aws-sqs": "^1.59.0", + "@aws-cdk/core": "^1.59.0" } } diff --git a/infrastructure/test/worker/worker.test.ts b/infrastructure/test/worker/worker.test.ts index b3fd88c..de9981f 100644 --- a/infrastructure/test/worker/worker.test.ts +++ b/infrastructure/test/worker/worker.test.ts @@ -22,6 +22,7 @@ import { LayerVersion } from "@aws-cdk/aws-lambda"; import { LAMBDA_LAYER_ARN } from "../../lib/constants"; import { Worker } from "../../lib/workers/worker"; import { Table, AttributeType } from "@aws-cdk/aws-dynamodb"; +import { PolicyStatement } from "@aws-cdk/aws-iam"; function createWorker(stack: cdk.Stack, extraSGs?: string, handler = "testHandler", timeout = cdk.Duration.minutes(10), batchSize = 3): Worker { if (extraSGs !== undefined) { @@ -41,7 +42,13 @@ function createWorker(stack: cdk.Stack, extraSGs?: string, handler = "testHandle graphTable: table, handler: handler, timeout: timeout, - batchSize: batchSize + batchSize: batchSize, + policyStatements: [ + new PolicyStatement({ + actions: [ "eks:DescribeCluster" ], + resources: [ donorCluster.clusterArn ] + }) + ] }); } From 8a791c51687fab2c3d63094f53a2a0094ffee7d0 Mon Sep 17 00:00:00 2001 From: K-itKat20 Date: Tue, 25 Aug 2020 15:49:44 +0100 Subject: [PATCH 10/23] Add Graphs base UI --- ui/src/components/AddGraph/AddGraph.tsx | 170 ++++++++++++++++++++++++ ui/src/components/Navigation/Routes.tsx | 7 +- 2 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 ui/src/components/AddGraph/AddGraph.tsx diff --git a/ui/src/components/AddGraph/AddGraph.tsx b/ui/src/components/AddGraph/AddGraph.tsx new file mode 100644 index 0000000..ab74cd7 --- /dev/null +++ b/ui/src/components/AddGraph/AddGraph.tsx @@ -0,0 +1,170 @@ +import React from 'react'; +import {Button, Container, CssBaseline, Grid, IconButton, makeStyles, TextField, Typography} from "@material-ui/core"; +import {Schema} from '../../domain/schema'; +import {Notifications} from '../../domain/notifications'; +import {CreateGraphRepo} from '../../rest/repositories/create-graph-repo'; +import {Alert} from "@material-ui/lab"; +import InsertDriveFileOutlinedIcon from '@material-ui/icons/InsertDriveFileOutlined'; +import DeleteOutlineOutlinedIcon from '@material-ui/icons/DeleteOutlineOutlined'; + +interface IState { + newGraph: { + graphId: string, + administrators: Array, + schemaJson: string, + } + notifications: Notifications, +} + +export default class AddGraph extends React.Component<{}, IState> { + constructor(props: object) { + super(props); + this.state = { + newGraph: { + graphId: "", + administrators: [], + schemaJson: "", + }, + notifications: new Notifications(), + } + + } + + private classes: any = makeStyles((theme) => ({ + root: { + width: '100%', + marginTop: 40 + }, + paper: { + marginTop: theme.spacing(2), + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + }, + avatar: { + margin: theme.spacing(1), + backgroundColor: theme.palette.secondary.main, + }, + form: { + width: '100%', // Fix IE 11 issue. + marginTop: theme.spacing(3), + }, + submit: { + margin: theme.spacing(3, 0, 2), + }, + button: { + margin: "10px", + } + })); + + private async submitNewGraph() { + const {graphId, administrators, schemaJson} = this.state.newGraph; + const schema = new Schema(schemaJson); + const notifications: Notifications = schema.validation(); + + if (notifications.isEmpty()) { + await new CreateGraphRepo().create(graphId, administrators, schema); + + } else { + this.setState({notifications: notifications}); + } + } + + public render() { + return ( + + + + + + {!this.state.notifications.isEmpty() && + Error(s): {this.state.notifications.errorMessage()}} +
+ +
+ + + + + + { + this.setState({ + newGraph: { + ...this.state.newGraph, + graphId: event.target.value + } + }); + }} + /> + + + + + + + + + + + + + + { + this.setState({ + newGraph: { + ...this.state.newGraph, + schemaJson: event.target.value + } + }); + }} + /> + + +
+
+
+ + + +
+ ) + } +} diff --git a/ui/src/components/Navigation/Routes.tsx b/ui/src/components/Navigation/Routes.tsx index c7d0303..4892b29 100644 --- a/ui/src/components/Navigation/Routes.tsx +++ b/ui/src/components/Navigation/Routes.tsx @@ -1,11 +1,12 @@ import Page1 from "../Page1"; import Page2 from "../Page2"; +import AddGraph from "../AddGraph/AddGraph"; const Routes = [ { - path: '/Page1', - sidebarName: 'Page 1', - component: Page1 + path: '/AddGraph', + sidebarName: 'Add Graph', + component: AddGraph }, { path: '/Page2', From e2c31ed03d9773641602e74ee3b1be83ece89bb3 Mon Sep 17 00:00:00 2001 From: jpelbertrios Date: Wed, 26 Aug 2020 12:43:28 +0100 Subject: [PATCH 11/23] Add ViewGraph component --- ui/src/components/Navigation/Routes.tsx | 10 +-- ui/src/components/ViewGraph/ViewGraph.tsx | 79 +++++++++++++++++++++++ 2 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 ui/src/components/ViewGraph/ViewGraph.tsx diff --git a/ui/src/components/Navigation/Routes.tsx b/ui/src/components/Navigation/Routes.tsx index 4892b29..cc33b2c 100644 --- a/ui/src/components/Navigation/Routes.tsx +++ b/ui/src/components/Navigation/Routes.tsx @@ -1,5 +1,5 @@ -import Page1 from "../Page1"; -import Page2 from "../Page2"; + +import ViewGraph from "../ViewGraph/ViewGraph"; import AddGraph from "../AddGraph/AddGraph"; const Routes = [ @@ -9,9 +9,9 @@ const Routes = [ component: AddGraph }, { - path: '/Page2', - sidebarName: 'Page 2', - component: Page2 + path: '/ViewGraph', + sidebarName: 'View Graph', + component: ViewGraph }, ]; diff --git a/ui/src/components/ViewGraph/ViewGraph.tsx b/ui/src/components/ViewGraph/ViewGraph.tsx new file mode 100644 index 0000000..05b2a52 --- /dev/null +++ b/ui/src/components/ViewGraph/ViewGraph.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import {makeStyles} from '@material-ui/core/styles'; +import { Grid, Box, Button, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton } from '@material-ui/core' +import { Graph } from '../../domain/graph'; +import { DeleteGraphRepo } from '../../rest/repositories/delete-graph-repo'; +import { GetAllGraphsRepo } from '../../rest/repositories/get-all-graphs-repo'; +import CreateIcon from '@material-ui/icons/Create'; +import DeleteOutlineOutlinedIcon from '@material-ui/icons/DeleteOutlineOutlined'; + +interface IState { + graphs: Graph[], + selectedRow: any, +} + +export default class ViewGraph extends React.Component<{}, IState> { + constructor(props: Object) { + super(props); + this.state = { + graphs: [], + selectedRow: '', + } + } + + public async componentDidMount() { + try { + this.setState({ graphs: await new GetAllGraphsRepo().getAll() }); + } catch (e) { + console.log(e.message); + } + } + + private classes: any = makeStyles({ + table: { + minWidth: 650, + }, + }); + + private getStripedStyle = (index:any) => { + return { background: index % 2 ? '#fafafa' : '#fafafa' }; + } + + public render() { + return ( + + + + + + + Graph Name + Current State + Update + + + + {this.state.graphs.map((graph: Graph, index) => ( + this.setState({selectedRow: graph.getId()})} style = {{ ...this.getStripedStyle(index)}}> + + {graph.getId()} + {graph.getStatus()} + + + + + + + + + + ))} + +
+
+
+ + ); + } +} From c27781a8af26968a770666eeb10c85ebcc106313 Mon Sep 17 00:00:00 2001 From: K-itKat20 Date: Wed, 26 Aug 2020 16:56:03 +0100 Subject: [PATCH 12/23] File dropzone --- ui/package-lock.json | 33 +++++++++++++ ui/package.json | 1 + ui/src/components/AddGraph/AddGraph.tsx | 66 ++++++++++++++++++++++--- ui/src/components/App.tsx | 2 +- 4 files changed, 94 insertions(+), 8 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 98bd6b4..dec8933 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -3183,6 +3183,11 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, + "attr-accept": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.1.tgz", + "integrity": "sha512-GpefLMsbH5ojNgfTW+OBin2xKzuHfyeNA+qCktzZojBhbA/lPZdCFMWdwk5ajb989Ok7ZT+EADqvW3TAFNMjhA==" + }, "autoprefixer": { "version": "9.8.5", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.5.tgz", @@ -7236,6 +7241,14 @@ "schema-utils": "^2.5.0" } }, + "file-selector": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.12.tgz", + "integrity": "sha512-Kx7RTzxyQipHuiqyZGf+Nz4vY9R1XGxuQl/hLoJwq+J4avk/9wxxgZyHKtbyIPJmbD4A66DWGYfyykWNpcYutQ==", + "requires": { + "tslib": "^1.9.0" + } + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -10325,6 +10338,16 @@ "react-double-scrollbar": "0.0.15" } }, + "material-ui-dropzone": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/material-ui-dropzone/-/material-ui-dropzone-3.4.0.tgz", + "integrity": "sha512-c+H0+dQ65+252KJA3oIfcOXou78/+1nHAAakSZxmN9TudtZMoWnXXuUc8JRdM2zCy5p9kopUw5rG9o4/6ISPig==", + "requires": { + "@babel/runtime": "^7.4.4", + "clsx": "^1.0.2", + "react-dropzone": "^10.2.1" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -13753,6 +13776,16 @@ "resolved": "https://registry.npmjs.org/react-double-scrollbar/-/react-double-scrollbar-0.0.15.tgz", "integrity": "sha1-6RWrjLO5WYdwdfSUNt6/2wQoj+Q=" }, + "react-dropzone": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-10.2.2.tgz", + "integrity": "sha512-U5EKckXVt6IrEyhMMsgmHQiWTGLudhajPPG77KFSvgsMqNEHSyGpqWvOMc5+DhEah/vH4E1n+J5weBNLd5VtyA==", + "requires": { + "attr-accept": "^2.0.0", + "file-selector": "^0.1.12", + "prop-types": "^15.7.2" + } + }, "react-error-overlay": { "version": "6.0.7", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz", diff --git a/ui/package.json b/ui/package.json index 352724f..901f4f4 100644 --- a/ui/package.json +++ b/ui/package.json @@ -27,6 +27,7 @@ "express": "^4.17.1", "json": "^9.0.6", "material-table": "^1.68.1", + "material-ui-dropzone": "^3.4.0", "npm-check-updates": "^7.1.1", "react": "^16.13.1", "react-dom": "^16.13.1", diff --git a/ui/src/components/AddGraph/AddGraph.tsx b/ui/src/components/AddGraph/AddGraph.tsx index ab74cd7..c892cd9 100644 --- a/ui/src/components/AddGraph/AddGraph.tsx +++ b/ui/src/components/AddGraph/AddGraph.tsx @@ -1,13 +1,29 @@ import React from 'react'; -import {Button, Container, CssBaseline, Grid, IconButton, makeStyles, TextField, Typography} from "@material-ui/core"; +import { + Button, + Container, + CssBaseline, + Dialog, DialogActions, DialogContent, + Grid, + IconButton, + makeStyles, Slide, + TextField, + Typography +} from "@material-ui/core"; import {Schema} from '../../domain/schema'; import {Notifications} from '../../domain/notifications'; import {CreateGraphRepo} from '../../rest/repositories/create-graph-repo'; import {Alert} from "@material-ui/lab"; import InsertDriveFileOutlinedIcon from '@material-ui/icons/InsertDriveFileOutlined'; import DeleteOutlineOutlinedIcon from '@material-ui/icons/DeleteOutlineOutlined'; +import {DropzoneArea} from 'material-ui-dropzone' +import ClearIcon from "@material-ui/icons/Clear"; +import {DropzoneDialog} from 'material-ui-dropzone' +import {TransitionProps} from "@material-ui/core/transitions"; interface IState { + dialogIsOpen: boolean, + files: Array, newGraph: { graphId: string, administrators: Array, @@ -15,11 +31,19 @@ interface IState { } notifications: Notifications, } +const Transition = React.forwardRef(function Transition( + props: TransitionProps & { children?: React.ReactElement }, + ref: React.Ref, +) { + return ; +}); export default class AddGraph extends React.Component<{}, IState> { constructor(props: object) { super(props); this.state = { + dialogIsOpen: false, + files: [], newGraph: { graphId: "", administrators: [], @@ -54,7 +78,11 @@ export default class AddGraph extends React.Component<{}, IState> { }, button: { margin: "10px", - } + }, + previewChip: { + minWidth: 160, + maxWidth: 210 + }, })); private async submitNewGraph() { @@ -69,8 +97,22 @@ export default class AddGraph extends React.Component<{}, IState> { this.setState({notifications: notifications}); } } + private async setFiles(files: Array){ + this.setState({ + files: files + }); + } + private async checkFiles(files: Array){ + + } public render() { + const openDialogBox = () => { + this.setState({ dialogIsOpen: true }); + }; + const closeDialogBox = () => { + this.setState({ dialogIsOpen: false }); + }; return ( { justify="flex-end" alignItems="center" > - + - - - + + + + + @@ -154,7 +205,8 @@ export default class AddGraph extends React.Component<{}, IState> { justify="center" alignItems="center"> - + ) } } diff --git a/ui/src/components/Navigation/NavigationAppbar.tsx b/ui/src/components/Navigation/NavigationAppbar.tsx index 760d503..8c8cdb1 100644 --- a/ui/src/components/Navigation/NavigationAppbar.tsx +++ b/ui/src/components/Navigation/NavigationAppbar.tsx @@ -2,8 +2,12 @@ import React, { useState } from 'react'; import { NavLink, withRouter } from 'react-router-dom'; import Routes from './Routes'; import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; -import { AppBar, Toolbar, Typography, MenuList, MenuItem, ListItemText, Link, Grid, Box } from '@material-ui/core'; +import { AppBar, Toolbar, Typography, MenuList, MenuItem, ListItemText, Link, Grid, Box, Drawer, Divider, List, ListItem, ListItemIcon, IconButton} from '@material-ui/core'; +import { green } from '@material-ui/core/colors'; +import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; +import VisibilityIcon from '@material-ui/icons/Visibility'; +const drawerWidth = 240; const useStyles = makeStyles((theme: Theme) => createStyles({ root: {}, @@ -15,7 +19,20 @@ const useStyles = makeStyles((theme: Theme) => }, drawer: { width: 240, + flexShrink: 0, }, + icon: { + color: '#696666', + margin: '20px' + }, + drawerPaper: { + width: drawerWidth, + }, + drawerContainer: { + overflow: 'auto', + }, + // necessary for content to be below app bar + toolbar: theme.mixins.toolbar, drawerHeader: { display: 'flex', alignItems: 'center', @@ -33,6 +50,9 @@ const useStyles = makeStyles((theme: Theme) => easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, }), + backgroundColor: "#607D8B", + boxShadow: "0px 0px 0px 0px", + zIndex: theme.zIndex.drawer + 1, }, listItem: {}, listItemText: { @@ -65,16 +85,28 @@ const NavigationAppbar: React.FC = (props: any) => { } return ( -
- + - - Kai + + Graph As Service + + + + + + +
+ - {Routes.map((prop, key) => { return ( { ); - })} + })} - - + + +
+
+
-
+ ); }; -export default withRouter(NavigationAppbar); \ No newline at end of file +export default withRouter(NavigationAppbar); + diff --git a/ui/src/components/ViewGraph/ViewGraph.tsx b/ui/src/components/ViewGraph/ViewGraph.tsx index 05b2a52..ae588c5 100644 --- a/ui/src/components/ViewGraph/ViewGraph.tsx +++ b/ui/src/components/ViewGraph/ViewGraph.tsx @@ -1,6 +1,6 @@ import React from 'react'; import {makeStyles} from '@material-ui/core/styles'; -import { Grid, Box, Button, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton } from '@material-ui/core' +import { Grid, Box, Button, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton, Toolbar } from '@material-ui/core' import { Graph } from '../../domain/graph'; import { DeleteGraphRepo } from '../../rest/repositories/delete-graph-repo'; import { GetAllGraphsRepo } from '../../rest/repositories/get-all-graphs-repo'; @@ -30,6 +30,10 @@ export default class ViewGraph extends React.Component<{}, IState> { } private classes: any = makeStyles({ + root: { + width: '100%', + marginTop: 40 + }, table: { minWidth: 650, }, @@ -41,38 +45,43 @@ export default class ViewGraph extends React.Component<{}, IState> { public render() { return ( - - - - - - - Graph Name - Current State - Update - - - - {this.state.graphs.map((graph: Graph, index) => ( - this.setState({selectedRow: graph.getId()})} style = {{ ...this.getStripedStyle(index)}}> - - {graph.getId()} - {graph.getStatus()} - - - - - - - - +
+ + + +
+ + + + Graph Name + Current State + Update - ))} - -
-
-
+ + + + {this.state.graphs.map((graph: Graph, index) => ( + this.setState({selectedRow: graph.getId()})} style = {{ ...this.getStripedStyle(index)}}> + + {graph.getId()} + {graph.getStatus()} + + + + + + + + + + ))} + + + + + + ); } From 6798148f2548b2bf3133eb96fff6eeef4bf28d45 Mon Sep 17 00:00:00 2001 From: jpelbertrios Date: Thu, 27 Aug 2020 16:40:24 +0100 Subject: [PATCH 14/23] update navbar app menu --- .../Navigation/NavigationAppbar.tsx | 117 +++++++++++------- ui/src/components/Navigation/Routes.tsx | 10 +- 2 files changed, 78 insertions(+), 49 deletions(-) diff --git a/ui/src/components/Navigation/NavigationAppbar.tsx b/ui/src/components/Navigation/NavigationAppbar.tsx index 8c8cdb1..38bf7e6 100644 --- a/ui/src/components/Navigation/NavigationAppbar.tsx +++ b/ui/src/components/Navigation/NavigationAppbar.tsx @@ -2,10 +2,12 @@ import React, { useState } from 'react'; import { NavLink, withRouter } from 'react-router-dom'; import Routes from './Routes'; import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; -import { AppBar, Toolbar, Typography, MenuList, MenuItem, ListItemText, Link, Grid, Box, Drawer, Divider, List, ListItem, ListItemIcon, IconButton} from '@material-ui/core'; +import { AppBar, Toolbar, Typography, MenuList, MenuItem, ListItemText,Drawer, Divider, ListItem, List, ListItemIcon, Avatar, ListItemAvatar} from '@material-ui/core'; import { green } from '@material-ui/core/colors'; import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; import VisibilityIcon from '@material-ui/icons/Visibility'; +import LocalLibraryIcon from '@material-ui/icons/LocalLibrary'; +import PersonIcon from '@material-ui/icons/Person'; const drawerWidth = 240; const useStyles = makeStyles((theme: Theme) => @@ -54,10 +56,12 @@ const useStyles = makeStyles((theme: Theme) => boxShadow: "0px 0px 0px 0px", zIndex: theme.zIndex.drawer + 1, }, - listItem: {}, + listItem: { + color: '#696666' + }, listItemText: { '& span, & svg': { - fontSize: '20px', + fontSize: '20px' } }, }), @@ -83,52 +87,69 @@ const NavigationAppbar: React.FC = (props: any) => { const activeRoute = (routeName: any) => { return props.location.pathname === routeName ? true : false; } - + const getAvataricon = (sidebarName: any) => { + switch(sidebarName) { + case 'Add Graph': + return (); + case 'View Graph': + return (); + case 'User Guide': + return (); + default: + return null; + } + } return ( -
- - - - Graph As Service - - - - - - - - -
- - - {Routes.map((prop, key) => { - return ( - - - - - - - - - ); - })} - - - -
-
- -
- +
+ + + + Graph As Service + + + + + + +
+ + + + + + + + + + + + + + {Routes.map((prop, key) => { + return ( + + + + {getAvataricon(prop.sidebarName)} + + + + + ); + })} + + +
+
+ +
); }; diff --git a/ui/src/components/Navigation/Routes.tsx b/ui/src/components/Navigation/Routes.tsx index cc33b2c..c9b4609 100644 --- a/ui/src/components/Navigation/Routes.tsx +++ b/ui/src/components/Navigation/Routes.tsx @@ -1,6 +1,8 @@ import ViewGraph from "../ViewGraph/ViewGraph"; import AddGraph from "../AddGraph/AddGraph"; +import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; +import VisibilityIcon from '@material-ui/icons/Visibility'; const Routes = [ { @@ -13,6 +15,12 @@ const Routes = [ sidebarName: 'View Graph', component: ViewGraph }, + { + path: '#', + sidebarName: 'User Guide', + }, + ]; -export default Routes; \ No newline at end of file +export default Routes; + From 05623fa042ae09334026a2a2f21e48bbb5a7bcbf Mon Sep 17 00:00:00 2001 From: K-itKat20 Date: Wed, 2 Sep 2020 10:48:27 +0100 Subject: [PATCH 15/23] Unit tests for add graph --- ui/package-lock.json | 63 ++++++--- ui/package.json | 5 +- ui/src/components/AddGraph/AddGraph.tsx | 2 +- ui/test/components/AddGraph.test.tsx | 127 ++++++++++++++++++ .../__snapshots__/AddGraph.test.tsx.snap | 3 + 5 files changed, 182 insertions(+), 18 deletions(-) create mode 100644 ui/test/components/AddGraph.test.tsx create mode 100644 ui/test/components/__snapshots__/AddGraph.test.tsx.snap diff --git a/ui/package-lock.json b/ui/package-lock.json index dec8933..796b383 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1690,7 +1690,8 @@ "@sheerun/mutationobserver-shim": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", - "integrity": "sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw==" + "integrity": "sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw==", + "dev": true }, "@sindresorhus/is": { "version": "0.14.0", @@ -1818,6 +1819,7 @@ "version": "6.16.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-6.16.0.tgz", "integrity": "sha512-lBD88ssxqEfz0wFL6MeUyyWZfV/2cjEZZV3YRpb2IoJRej/4f1jB0TzqIOznTpfR1r34CNesrubxwIlAQ8zgPA==", + "dev": true, "requires": { "@babel/runtime": "^7.8.4", "@sheerun/mutationobserver-shim": "^0.3.2", @@ -1832,6 +1834,7 @@ "version": "25.5.0", "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", @@ -1843,6 +1846,7 @@ "version": "15.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==", + "dev": true, "requires": { "@types/yargs-parser": "*" } @@ -1850,12 +1854,14 @@ "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true }, "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, "requires": { "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" @@ -1865,6 +1871,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1874,6 +1881,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -1881,17 +1889,20 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "pretty-format": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dev": true, "requires": { "@jest/types": "^25.5.0", "ansi-regex": "^5.0.0", @@ -1900,9 +1911,10 @@ } }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -1929,6 +1941,7 @@ "version": "9.5.0", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-9.5.0.tgz", "integrity": "sha512-di1b+D0p+rfeboHO5W7gTVeZDIK5+maEgstrZbWZSSvxDyfDRkkyBE1AJR5Psd6doNldluXlCWqXriUfqu/9Qg==", + "dev": true, "requires": { "@babel/runtime": "^7.8.4", "@testing-library/dom": "^6.15.0", @@ -2358,6 +2371,7 @@ "version": "6.14.0", "resolved": "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-6.14.0.tgz", "integrity": "sha512-sMl7OSv0AvMOqn1UJ6j1unPMIHRXen0Ita1ujnMX912rrOcawe4f7wu0Zt9GIQhBhJvH2BaibqFgQ3lP+Pj2hA==", + "dev": true, "requires": { "pretty-format": "^24.3.0" } @@ -2366,6 +2380,7 @@ "version": "9.1.3", "resolved": "https://registry.npmjs.org/@types/testing-library__react/-/testing-library__react-9.1.3.tgz", "integrity": "sha512-iCdNPKU3IsYwRK9JieSYAiX0+aYDXOGAmrC/3/M7AqqSDKnWWVv07X+Zk1uFSL7cMTUYzv4lQRfohucEocn5/w==", + "dev": true, "requires": { "@types/react-dom": "*", "@types/testing-library__dom": "*", @@ -2376,6 +2391,7 @@ "version": "25.5.0", "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", @@ -2387,6 +2403,7 @@ "version": "15.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==", + "dev": true, "requires": { "@types/yargs-parser": "*" } @@ -2394,12 +2411,14 @@ "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true }, "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, "requires": { "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" @@ -2409,6 +2428,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2418,6 +2438,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -2425,17 +2446,20 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "pretty-format": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dev": true, "requires": { "@jest/types": "^25.5.0", "ansi-regex": "^5.0.0", @@ -2444,9 +2468,10 @@ } }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -2992,6 +3017,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, "requires": { "@babel/runtime": "^7.10.2", "@babel/runtime-corejs3": "^7.10.2" @@ -5865,7 +5891,8 @@ "dom-accessibility-api": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.3.0.tgz", - "integrity": "sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA==" + "integrity": "sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA==", + "dev": true }, "dom-converter": { "version": "0.2.0", @@ -13941,6 +13968,11 @@ "scheduler": "^0.19.1" } }, + "react-testing-library": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/react-testing-library/-/react-testing-library-8.0.1.tgz", + "integrity": "sha512-Gq4JC9r3prA4hYwo7afcbHHMFckO29+5Nrh2KblAEPuK/DWaU0bJE1vtpAgLhzhY9bBirmcgjjIHljHEwGAXKw==" + }, "react-transition-group": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", @@ -16746,7 +16778,8 @@ "wait-for-expect": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-3.0.2.tgz", - "integrity": "sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag==" + "integrity": "sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag==", + "dev": true }, "walker": { "version": "1.0.7", diff --git a/ui/package.json b/ui/package.json index 901f4f4..47bb2ff 100644 --- a/ui/package.json +++ b/ui/package.json @@ -17,7 +17,6 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.56", "@testing-library/jest-dom": "^4.2.4", - "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", "@types/node": "^12.12.54", "@types/react": "^16.9.46", @@ -33,11 +32,13 @@ "react-dom": "^16.13.1", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", - "react-scripts": "3.4.1" + "react-scripts": "3.4.1", + "react-testing-library": "latest" }, "devDependencies": { "@babel/preset-env": "^7.11.0", "@babel/preset-react": "^7.10.4", + "@testing-library/react": "^9.5.0", "@types/body-parser": "^1.19.0", "@types/enzyme": "^3.10.5", "@types/enzyme-adapter-react-16": "^1.0.6", diff --git a/ui/src/components/AddGraph/AddGraph.tsx b/ui/src/components/AddGraph/AddGraph.tsx index 4c8b4bc..09812c9 100644 --- a/ui/src/components/AddGraph/AddGraph.tsx +++ b/ui/src/components/AddGraph/AddGraph.tsx @@ -241,7 +241,7 @@ export default class AddGraph extends React.Component<{}, IState> { ); +describe('When AddGraph mounts', () => { + + it('should have a Graph Id text field', () => { + const textfield = wrapper.find('input'); + expect(textfield.at(0).props().name).toBe("graphId"); + }); + it('should have a Schema text area', () => { + const textfield = wrapper.find('textarea'); + expect(textfield.props().id).toBe("schema"); + }); + it('should have icon button', () => { + const fileButton = wrapper.find('button').at(0).find('svg'); + expect(fileButton).toHaveLength(1); + }); + it('should have a Submit button', () => { + const submitButton = wrapper.find('button').at(2).text(); + expect(submitButton).toBe("Add Graph"); + }); + + +}); +describe('Dropzone behaviour', () => { + function flushPromises(ui: JSX.Element, container: any) { + return new Promise(resolve => + setImmediate(() => { + render(ui, {container}); + resolve(container) + }) + ) + } + + function dispatchEvt(node: any, type: any, data: any) { + const event = new Event(type, {bubbles: true}); + Object.assign(event, data); + fireEvent(node, event) + } + + function mockData(files: Array) { + return { + dataTransfer: { + files, + items: files.map(file => ({ + kind: 'file', + type: file.type, + getAsFile: () => file + })), + types: ['Files'] + } + } + } + + it('renders the root and input nodes with the necessary props', () => { + const {container} = render( + + {({getRootProps, getInputProps}) => ( +
+ +
+ )} +
+ ); + expect(container.innerHTML).toMatchSnapshot() + }); + test('runs onDragEnter when a file is dragged in component', async () => { + const file = new File([ + JSON.stringify({ping: true}) + ], 'ping.json', {type: 'application/json'}); + const data = mockData([file]); + const onDragEnter = jest.fn(); + + const ui = ( + + {({getRootProps, getInputProps}) => ( +
+ +
+ )} +
+ ); + const {container} = render(ui); + const dropzone = container.querySelector('div'); + + dispatchEvt(dropzone, 'dragenter', data); + await flushPromises(ui, container); + + expect(onDragEnter).toHaveBeenCalled() + }); + it('runs onChange when a change happens in the component', () => { + const inputProps = { + onChange: jest.fn() + }; + + const ui = ( + + {({ getRootProps, getInputProps }) => ( +
+ +
+ )} +
+ ); + + const { container } = render(ui); + const input = container.querySelector('input'); + + fireEvent.change(input); + expect(inputProps.onChange).toHaveBeenCalled() + }) + it('should have an input that accepts files', () => { + + const dropZone= wrapper.find('input').at(1); + expect(dropZone.props().type).toBe('file') + }) + it('should only accept json files', () => { + + const dropZone= wrapper.find('input').at(1); + expect(dropZone.props().accept).toBe('application/json') + }) + + +}); \ No newline at end of file diff --git a/ui/test/components/__snapshots__/AddGraph.test.tsx.snap b/ui/test/components/__snapshots__/AddGraph.test.tsx.snap new file mode 100644 index 0000000..3dfb8bd --- /dev/null +++ b/ui/test/components/__snapshots__/AddGraph.test.tsx.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Dropzone behaviour renders the root and input nodes with the necessary props 1`] = `"
"`; From 3b17a5f1ab1fde363a10fe5f6b760e83e451363f Mon Sep 17 00:00:00 2001 From: K-itKat20 Date: Wed, 2 Sep 2020 10:51:34 +0100 Subject: [PATCH 16/23] Minor change --- ui/test/components/AddGraph.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/test/components/AddGraph.test.tsx b/ui/test/components/AddGraph.test.tsx index a09ebf4..6c67db9 100644 --- a/ui/test/components/AddGraph.test.tsx +++ b/ui/test/components/AddGraph.test.tsx @@ -97,13 +97,13 @@ describe('Dropzone behaviour', () => { }; const ui = ( - + {({ getRootProps, getInputProps }) => (
)} -
+ ); const { container } = render(ui); From 6bac7bfb96c794b1f18928d2ecef58b15435b7b4 Mon Sep 17 00:00:00 2001 From: K-itKat20 Date: Thu, 3 Sep 2020 11:50:29 +0100 Subject: [PATCH 17/23] User Guide page Changed graphId to graphName. Created the User Guide page. --- ui/package-lock.json | 126 +++++++++++++++- ui/package.json | 1 + ui/server/middleware.js | 10 +- ui/src/components/AddGraph/AddGraph.tsx | 22 +-- ui/src/components/Navigation/Routes.tsx | 5 +- ui/src/components/Page1.tsx | 28 ---- ui/src/components/Page2.tsx | 29 ---- ui/src/components/UserGuide/UserGuide.tsx | 138 ++++++++++++++++++ ui/src/domain/graph.ts | 8 +- .../request-interfaces.ts | 2 +- .../response-interfaces.ts | 2 +- ui/src/rest/repositories/create-graph-repo.ts | 4 +- ui/src/rest/repositories/delete-graph-repo.ts | 4 +- .../rest/repositories/get-all-graphs-repo.ts | 2 +- ui/src/rest/repositories/get-graph-repo.ts | 6 +- ui/test/components/AddGraph.test.tsx | 2 +- ui/test/components/UserGuide.test.tsx | 10 ++ .../repositories/get-all-graphs-repo.test.ts | 6 +- .../rest/repositories/get-graph-repo.test.ts | 2 +- ui/test/rest/rest-client.test.ts | 8 +- 20 files changed, 316 insertions(+), 99 deletions(-) delete mode 100644 ui/src/components/Page1.tsx delete mode 100644 ui/src/components/Page2.tsx create mode 100644 ui/src/components/UserGuide/UserGuide.tsx create mode 100644 ui/test/components/UserGuide.test.tsx diff --git a/ui/package-lock.json b/ui/package-lock.json index 796b383..abaa125 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -3894,6 +3894,11 @@ } } }, + "base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" + }, "base64-arraybuffer": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz", @@ -6082,7 +6087,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, "requires": { "iconv-lite": "^0.6.2" }, @@ -6091,7 +6095,6 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "optional": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } @@ -7192,6 +7195,43 @@ "bser": "2.1.1" } }, + "fbemitter": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-2.1.1.tgz", + "integrity": "sha1-Uj4U/a9SSIBbsC9i78M75wP1GGU=", + "requires": { + "fbjs": "^0.8.4" + } + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + } + } + }, "fetch-mock": { "version": "9.10.7", "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.10.7.tgz", @@ -7406,6 +7446,15 @@ "readable-stream": "^2.3.6" } }, + "flux": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/flux/-/flux-3.1.3.tgz", + "integrity": "sha1-0jvtUVp5oi2TOrU6tK2hnQWy8Io=", + "requires": { + "fbemitter": "^2.0.0", + "fbjs": "^0.8.0" + } + }, "follow-redirects": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", @@ -8862,6 +8911,15 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -10169,6 +10227,11 @@ "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" }, + "lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" + }, "lodash.escape": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", @@ -10181,6 +10244,11 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, + "lodash.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" + }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -11047,6 +11115,15 @@ "tslib": "^1.10.0" } }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, "node-forge": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", @@ -13436,6 +13513,11 @@ "escape-goat": "^2.0.0" } }, + "pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -13592,6 +13674,17 @@ "whatwg-fetch": "^3.0.0" } }, + "react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw=", + "requires": { + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" + } + }, "react-beautiful-dnd": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz", @@ -13823,6 +13916,22 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-json-view": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.19.1.tgz", + "integrity": "sha512-u5e0XDLIs9Rj43vWkKvwL8G3JzvXSl6etuS5G42a8klMohZuYFQzSN6ri+/GiBptDqlrXPTdExJVU7x9rrlXhg==", + "requires": { + "flux": "^3.1.3", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^6.1.0" + } + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "react-redux": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.1.tgz", @@ -13973,6 +14082,14 @@ "resolved": "https://registry.npmjs.org/react-testing-library/-/react-testing-library-8.0.1.tgz", "integrity": "sha512-Gq4JC9r3prA4hYwo7afcbHHMFckO29+5Nrh2KblAEPuK/DWaU0bJE1vtpAgLhzhY9bBirmcgjjIHljHEwGAXKw==" }, + "react-textarea-autosize": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz", + "integrity": "sha512-F6bI1dgib6fSvG8so1HuArPUv+iVEfPliuLWusLF+gAKz0FbB4jLrWUrTAeq1afnPT2c9toEZYUdz/y1uKMy4A==", + "requires": { + "prop-types": "^15.6.0" + } + }, "react-transition-group": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", @@ -16383,6 +16500,11 @@ "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "dev": true }, + "ua-parser-js": { + "version": "0.7.21", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", + "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==" + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", diff --git a/ui/package.json b/ui/package.json index 47bb2ff..af97c2f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -30,6 +30,7 @@ "npm-check-updates": "^7.1.1", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-json-view": "^1.19.1", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", "react-scripts": "3.4.1", diff --git a/ui/server/middleware.js b/ui/server/middleware.js index 712e428..2a0955e 100644 --- a/ui/server/middleware.js +++ b/ui/server/middleware.js @@ -10,24 +10,24 @@ app.listen(port, () => console.log(`Listening on port ${port}`)); // Get all graphs app.get('/graphs', (req, res) => { res.send([ { - "graphId": "roadTraffic", + "graphName": "roadTraffic", "currentState": "DEPLOYED" }, { - "graphId": "basicGraph", + "graphName": "basicGraph", "currentState": "DEPLOYED" }]); }); // Get graph by ID -app.get('/graphs/:graphId', (req, res) => { +app.get('/graphs/:graphName', (req, res) => { res.send({ - "graphId": req.params.graphId, + "graphName": req.params.graphName, "currentState": "DEPLOYED" }); }); // Delete graph by ID -app.delete('/graphs/:graphId', (req, res) => { +app.delete('/graphs/:graphName', (req, res) => { res.status(202).end(); }); diff --git a/ui/src/components/AddGraph/AddGraph.tsx b/ui/src/components/AddGraph/AddGraph.tsx index 09812c9..189ab2b 100644 --- a/ui/src/components/AddGraph/AddGraph.tsx +++ b/ui/src/components/AddGraph/AddGraph.tsx @@ -26,7 +26,7 @@ interface IState { files: Array, schemaFieldDisable: boolean, newGraph: { - graphId: string, + graphName: string, administrators: Array, schemaJson: string, } @@ -48,7 +48,7 @@ export default class AddGraph extends React.Component<{}, IState> { schemaFieldDisable: false, files: [], newGraph: { - graphId: "", + graphName: "", administrators: [], schemaJson: "", }, @@ -89,12 +89,12 @@ export default class AddGraph extends React.Component<{}, IState> { })); private async submitNewGraph() { - const {graphId, administrators, schemaJson} = this.state.newGraph; + const {graphName, administrators, schemaJson} = this.state.newGraph; const schema = new Schema(schemaJson); const notifications: Notifications = schema.validation(); if (notifications.isEmpty()) { - await new CreateGraphRepo().create(graphId, administrators, schema); + await new CreateGraphRepo().create(graphName, administrators, schema); } else { this.setState({notifications: notifications}); @@ -143,18 +143,18 @@ export default class AddGraph extends React.Component<{}, IState> { { this.setState({ newGraph: { ...this.state.newGraph, - graphId: event.target.value + graphName: event.target.value } }); }} @@ -277,7 +277,7 @@ export default class AddGraph extends React.Component<{}, IState> { files: [], newGraph: { ...this.state.newGraph, - graphId: "", + graphName: "", schemaJson:"" diff --git a/ui/src/components/Navigation/Routes.tsx b/ui/src/components/Navigation/Routes.tsx index c9b4609..9e25fab 100644 --- a/ui/src/components/Navigation/Routes.tsx +++ b/ui/src/components/Navigation/Routes.tsx @@ -1,6 +1,7 @@ import ViewGraph from "../ViewGraph/ViewGraph"; import AddGraph from "../AddGraph/AddGraph"; +import UserGuide from "../UserGuide/UserGuide"; import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; import VisibilityIcon from '@material-ui/icons/Visibility'; @@ -16,8 +17,10 @@ const Routes = [ component: ViewGraph }, { - path: '#', + path: '/UserGuide', sidebarName: 'User Guide', + component: UserGuide + }, ]; diff --git a/ui/src/components/Page1.tsx b/ui/src/components/Page1.tsx deleted file mode 100644 index e722637..0000000 --- a/ui/src/components/Page1.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import {makeStyles, Grid, Paper} from "@material-ui/core"; -import ExampleTable from './Graphs/ExampleTable' - -const useStyles = makeStyles((theme) => ({ - root: { - marginTop: 10 - }, - container: { - width: '40%' - }, -})); - -export default function Page1() { - - const classes = useStyles(); - - return ( - - - - - - ); -} diff --git a/ui/src/components/Page2.tsx b/ui/src/components/Page2.tsx deleted file mode 100644 index b7e7fcb..0000000 --- a/ui/src/components/Page2.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { makeStyles, Typography, Grid } from "@material-ui/core"; - -const useStyles = makeStyles((theme) => ({ - root: { - width: '100%', - marginTop: 10 - }, - container: { - width: '40%' - }, - -})); - -export default function Page2() { - const classes = useStyles(); - - return ( - - - Page 2 - - - - ); -} diff --git a/ui/src/components/UserGuide/UserGuide.tsx b/ui/src/components/UserGuide/UserGuide.tsx new file mode 100644 index 0000000..8421716 --- /dev/null +++ b/ui/src/components/UserGuide/UserGuide.tsx @@ -0,0 +1,138 @@ +import React from 'react'; +import {Button, Card, CardActions, CardContent, Grid, makeStyles, Toolbar, Typography} from "@material-ui/core"; +import ReactJson from "react-json-view"; + + +interface IState { + schemaJson: {}, +} + +export default class UserGuide extends React.Component <{}, IState> { + constructor(props: object) { + super(props); + this.state = { + schemaJson: { + "elements": { + "edges": { + "BasicEdge": { + "source": "vertex", + "destination": "vertex", + "directed": "true", + "properties": { + "count": "count" + } + } + } + }, + "types": { + "types": { + "vertex": { + "class": "java.lang.String" + }, + "count": { + "class": "java.lang.Integer", + "aggregateFunction": { + "class": "uk.gov.gchq.koryphe.impl.binaryoperator.Sum" + } + }, + "true": { + "description": "A simple boolean that must always be true.", + "class": "java.lang.Boolean", + "validateFunctions": [ + { + "class": "uk.gov.gchq.koryphe.impl.predicate.IsTrue" + } + ] + } + } + }} + } + + + } + + private classes: any = makeStyles((theme) => ({ + root: { + width: '100%', + }, + heading: { + fontSize: theme.typography.pxToRem(15), + fontWeight: theme.typography.fontWeightRegular, + }, + card: { + maxWidth: 345, + }, + + })); + + + public render() { + + return ( +
+ + + + + + + Add Graphs + + + When creating a new graph you need a unique Graph Name and a Schema. + Type in a unique name in the Graph Name text field. + In the Schema textarea, type in a valid schema with elements and types. + + + + You can import a schema by clicking the document icon. + You can only import a single JSON file. + You can remove your uploaded schema by clicking on the clear icon next to the name of your file in the selected files section. + + + Once you exit the dialog box, your imported schema will appear in the schema textarea. + You can edit the imported schema by typing in the Schema textarea. + + + Click Add Graph to add your graph. If your schema is invalid, it will give you an error. + + + Note: Make sure your schema has elements and types and is surrounded by curly brackets({"{}"}). + + + View Graphs + + + View your graphs in the View Graphs section. + + + + Schema + + + A schema can be split into two parts: elements and types. + + + Example Schema: + + + + + + + + + + + + + +
+ ) + } +} \ No newline at end of file diff --git a/ui/src/domain/graph.ts b/ui/src/domain/graph.ts index 4224b58..d180d90 100644 --- a/ui/src/domain/graph.ts +++ b/ui/src/domain/graph.ts @@ -1,15 +1,15 @@ export class Graph { - private graphId: string; + private graphName: string; private status: string; - constructor(graphId: string, status: string) { - this.graphId = graphId; + constructor(graphName: string, status: string) { + this.graphName = graphName; this.status = status; } public getId(): string { - return this.graphId; + return this.graphName; } public getStatus(): string { diff --git a/ui/src/rest/http-message-interfaces/request-interfaces.ts b/ui/src/rest/http-message-interfaces/request-interfaces.ts index 4874658..1762505 100644 --- a/ui/src/rest/http-message-interfaces/request-interfaces.ts +++ b/ui/src/rest/http-message-interfaces/request-interfaces.ts @@ -1,5 +1,5 @@ export interface ICreateGraphRequestBody { - graphId: string; + graphName: string; administrators: Array; schema: { elements: object; diff --git a/ui/src/rest/http-message-interfaces/response-interfaces.ts b/ui/src/rest/http-message-interfaces/response-interfaces.ts index 67d77a7..40b5aa4 100644 --- a/ui/src/rest/http-message-interfaces/response-interfaces.ts +++ b/ui/src/rest/http-message-interfaces/response-interfaces.ts @@ -1,5 +1,5 @@ export interface IGraphByIdResponse { - graphId: string; + graphName: string; currentState: string; } diff --git a/ui/src/rest/repositories/create-graph-repo.ts b/ui/src/rest/repositories/create-graph-repo.ts index a6e2092..13b3f86 100644 --- a/ui/src/rest/repositories/create-graph-repo.ts +++ b/ui/src/rest/repositories/create-graph-repo.ts @@ -4,9 +4,9 @@ import { Schema } from '../../domain/schema'; export class CreateGraphRepo { - public async create(graphId: string, administrators: Array, schema: Schema): Promise { + public async create(graphName: string, administrators: Array, schema: Schema): Promise { const httpRequestBody: ICreateGraphRequestBody = { - graphId: graphId, + graphName: graphName, administrators: administrators, schema: schema.getSchema(), }; diff --git a/ui/src/rest/repositories/delete-graph-repo.ts b/ui/src/rest/repositories/delete-graph-repo.ts index 18e302b..de1c8b0 100644 --- a/ui/src/rest/repositories/delete-graph-repo.ts +++ b/ui/src/rest/repositories/delete-graph-repo.ts @@ -2,8 +2,8 @@ import { RestClient, IApiResponse } from '../rest-client'; export class DeleteGraphRepo { - public async delete(graphId: string): Promise { - const response: IApiResponse = await RestClient.delete(graphId); + public async delete(graphName: string): Promise { + const response: IApiResponse = await RestClient.delete(graphName); if (response.status !== 202) { throw new Error(`Expected status code 202 for Accepted Delete Graph Process but got (${response.status})`); diff --git a/ui/src/rest/repositories/get-all-graphs-repo.ts b/ui/src/rest/repositories/get-all-graphs-repo.ts index 2668f20..1329629 100644 --- a/ui/src/rest/repositories/get-all-graphs-repo.ts +++ b/ui/src/rest/repositories/get-all-graphs-repo.ts @@ -8,7 +8,7 @@ export class GetAllGraphsRepo { const response: IApiResponse = await RestClient.get(); return response.data.map((jsonObject: any) => { - return new Graph(jsonObject.graphId, jsonObject.currentState); + return new Graph(jsonObject.graphName, jsonObject.currentState); }); } } diff --git a/ui/src/rest/repositories/get-graph-repo.ts b/ui/src/rest/repositories/get-graph-repo.ts index 514de38..5fd77c0 100644 --- a/ui/src/rest/repositories/get-graph-repo.ts +++ b/ui/src/rest/repositories/get-graph-repo.ts @@ -3,9 +3,9 @@ import { Graph } from '../../domain/graph'; import { IGraphByIdResponse } from '../http-message-interfaces/response-interfaces'; export class GetGraphRepo { - public async get(graphId: string): Promise { - const response: IApiResponse = await RestClient.get(graphId); + public async get(graphName: string): Promise { + const response: IApiResponse = await RestClient.get(graphName); - return new Graph(response.data.graphId, response.data.currentState); + return new Graph(response.data.graphName, response.data.currentState); } } diff --git a/ui/test/components/AddGraph.test.tsx b/ui/test/components/AddGraph.test.tsx index 6c67db9..3fee425 100644 --- a/ui/test/components/AddGraph.test.tsx +++ b/ui/test/components/AddGraph.test.tsx @@ -8,7 +8,7 @@ describe('When AddGraph mounts', () => { it('should have a Graph Id text field', () => { const textfield = wrapper.find('input'); - expect(textfield.at(0).props().name).toBe("graphId"); + expect(textfield.at(0).props().name).toBe("graphName"); }); it('should have a Schema text area', () => { const textfield = wrapper.find('textarea'); diff --git a/ui/test/components/UserGuide.test.tsx b/ui/test/components/UserGuide.test.tsx new file mode 100644 index 0000000..83bb550 --- /dev/null +++ b/ui/test/components/UserGuide.test.tsx @@ -0,0 +1,10 @@ +import {mount} from 'enzyme'; +import React from 'react'; +import UserGuide from "../../src/components/UserGuide/UserGuide"; +const wrapper = mount(); +describe('When UserGuide mounts', () => { + + + + +}); \ No newline at end of file diff --git a/ui/test/rest/repositories/get-all-graphs-repo.test.ts b/ui/test/rest/repositories/get-all-graphs-repo.test.ts index b290e19..7ed4561 100644 --- a/ui/test/rest/repositories/get-all-graphs-repo.test.ts +++ b/ui/test/rest/repositories/get-all-graphs-repo.test.ts @@ -14,11 +14,11 @@ describe('Get All Graphs Repo', () => { it('should return many Graphs when api returns many', async () => { const apiResponse: IAllGraphsResponse = [ { - graphId: 'roadTraffic', + graphName: 'roadTraffic', currentState: 'DEPLOYED', }, { - graphId: 'basicGraph', + graphName: 'basicGraph', currentState: 'DELETION_QUEUED', }, ]; @@ -33,7 +33,7 @@ describe('Get All Graphs Repo', () => { it('should return one Graph when api returns one', async () => { const apiResponse: IAllGraphsResponse = [ { - graphId: 'streetTraffic', + graphName: 'streetTraffic', currentState: 'DELETION_QUEUED', }, ]; diff --git a/ui/test/rest/repositories/get-graph-repo.test.ts b/ui/test/rest/repositories/get-graph-repo.test.ts index 6ef46c2..3e605b0 100644 --- a/ui/test/rest/repositories/get-graph-repo.test.ts +++ b/ui/test/rest/repositories/get-graph-repo.test.ts @@ -9,7 +9,7 @@ const mock = new MockAdapter(axios); const repo = new GetGraphRepo(); beforeAll(() => { - const apiResponse: IGraphByIdResponse = { graphId: 'graph-1', currentState: 'DEPLOYED' }; + const apiResponse: IGraphByIdResponse = { graphName: 'graph-1', currentState: 'DEPLOYED' }; mock.onGet('/graphs/graph-1').reply(200, apiResponse); mock.onGet('/graphs/notfound-graph').reply(404); }); diff --git a/ui/test/rest/rest-client.test.ts b/ui/test/rest/rest-client.test.ts index fed9b54..a89a4ca 100644 --- a/ui/test/rest/rest-client.test.ts +++ b/ui/test/rest/rest-client.test.ts @@ -9,9 +9,9 @@ describe('RestClient 2** Responses', () => { beforeAll(() => mock .onGet('/graphs') - .reply(200, [{ graphId: 'any-graph', currentStatus: 'DEPLOYED' }]) + .reply(200, [{ graphName: 'any-graph', currentStatus: 'DEPLOYED' }]) .onGet('/graphs/graph-1') - .reply(200, { graphId: 'graph-1', currentStatus: 'DELETED' }) + .reply(200, { graphName: 'graph-1', currentStatus: 'DELETED' }) .onPost('/graphs', { post: 'this' }) .reply(201) .onDelete('/graphs/redundant-graph') @@ -26,7 +26,7 @@ describe('RestClient 2** Responses', () => { status: 200, data: [ { - graphId: 'any-graph', + graphName: 'any-graph', currentStatus: 'DEPLOYED', }, ], @@ -38,7 +38,7 @@ describe('RestClient 2** Responses', () => { expect(actual).toEqual({ status: 200, data: { - graphId: 'graph-1', + graphName: 'graph-1', currentStatus: 'DELETED', }, }); From 0175f1af77881244df5b3eb1207352df4d81daba Mon Sep 17 00:00:00 2001 From: K-itKat20 Date: Thu, 3 Sep 2020 13:39:23 +0100 Subject: [PATCH 18/23] User Guide unit tests --- ui/test/components/UserGuide.test.tsx | 56 + .../__snapshots__/UserGuide.test.tsx.snap | 9216 +++++++++++++++++ 2 files changed, 9272 insertions(+) create mode 100644 ui/test/components/__snapshots__/UserGuide.test.tsx.snap diff --git a/ui/test/components/UserGuide.test.tsx b/ui/test/components/UserGuide.test.tsx index 83bb550..61920dc 100644 --- a/ui/test/components/UserGuide.test.tsx +++ b/ui/test/components/UserGuide.test.tsx @@ -1,8 +1,64 @@ import {mount} from 'enzyme'; import React from 'react'; import UserGuide from "../../src/components/UserGuide/UserGuide"; +import ReactJson from "react-json-view"; const wrapper = mount(); +const exampleJSON= { + "elements": { + "edges": { + "BasicEdge": { + "source": "vertex", + "destination": "vertex", + "directed": "true", + "properties": { + "count": "count" + } + } + } + }, + "types": { + "types": { + "vertex": { + "class": "java.lang.String" + }, + "count": { + "class": "java.lang.Integer", + "aggregateFunction": { + "class": "uk.gov.gchq.koryphe.impl.binaryoperator.Sum" + } + }, + "true": { + "description": "A simple boolean that must always be true.", + "class": "java.lang.Boolean", + "validateFunctions": [ + { + "class": "uk.gov.gchq.koryphe.impl.predicate.IsTrue" + } + ] + } + } + }}; + describe('When UserGuide mounts', () => { + it('there should be a Gaffer Documentation button ', () => { + const button = wrapper.find('a').at(26); + expect(button.text()).toBe("Gaffer Documentation") + + }); + it('the Gaffer Documentation button should link to the correct gaffer doc link ', () => { + const button = wrapper.find('a').at(26); + expect(button.props().href).toBe("https://gchq.github.io/gaffer-doc/summaries/getting-started.html") + + }); + it('should display example schema correctly ', () => { + const exampleSchema = wrapper.find('div.react-json-view'); + + const display= mount() + expect(exampleSchema).toHaveLength(1); + expect(exampleSchema.html()).toBe(display.html()) + + }); + diff --git a/ui/test/components/__snapshots__/UserGuide.test.tsx.snap b/ui/test/components/__snapshots__/UserGuide.test.tsx.snap new file mode 100644 index 0000000..e6a4544 --- /dev/null +++ b/ui/test/components/__snapshots__/UserGuide.test.tsx.snap @@ -0,0 +1,9216 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`When UserGuide mounts should render correctly 1`] = ` + +
+ + +
+ + + + +
+ + + + +
+ + +
+ + +

+ Add Graphs +

+
+
+ + +

+ When creating a new graph you need a unique Graph Name and a Schema. Type in a unique name in the Graph Name text field. In the Schema textarea, type in a valid schema with elements and types. +

+
+
+ + +

+ You can import a schema by clicking the document icon. You can only import a single JSON file. You can remove your uploaded schema by clicking on the clear icon next to the name of your file in the selected files section. +

+
+
+ + +

+ Once you exit the dialog box, your imported schema will appear in the schema textarea. You can edit the imported schema by typing in the Schema textarea. +

+
+
+ + +

+ Click Add Graph to add your graph. If your schema is invalid, it will give you an error. +

+
+
+ + +

+ Note: Make sure your schema has elements and types and is surrounded by curly brackets( + {} + ). +

+
+
+ + +

+ View Graphs +

+
+
+ + +

+ View your graphs in the View Graphs section. +

+
+
+ + +

+ Schema +

+
+
+ + +

+ A schema can be split into two parts: elements and types. +

+
+
+ + +

+ Example Schema: +

+
+
+ +
+ + +
+
+ +
+ + + + + + + + " + + + schema + + + " + + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + + + + + " + + + elements + + + " + + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + + + + + " + + + edges + + + " + + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + + + + + " + + + BasicEdge + + + " + + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + " + + + source + + + " + + + + : + + +
+ +
+ + + " + vertex + " + +
+
+
+ +
+
+ +
+ + + + " + + + destination + + + " + + + + : + + +
+ +
+ + + " + vertex + " + +
+
+
+ +
+
+ +
+ + + + " + + + directed + + + " + + + + : + + +
+ +
+ + + " + true + " + +
+
+
+ +
+
+ +
+ + + + + + + + " + + + properties + + + " + + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + " + + + count + + + " + + + + : + + +
+ +
+ + + " + count + " + +
+
+
+ +
+
+
+
+ + + } + + +
+
+
+
+ + + } + + +
+
+
+
+ + + } + + +
+
+
+
+ + + } + + +
+
+ +
+ + + + + + + + " + + + types + + + " + + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + + + + + " + + + types + + + " + + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + + + + + " + + + vertex + + + " + + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + " + + + class + + + " + + + + : + + +
+ +
+ + + " + java.lang.String + " + +
+
+
+ +
+
+
+
+ + + } + + +
+
+ +
+ + + + + + + + " + + + count + + + " + + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + " + + + class + + + " + + + + : + + +
+ +
+ + + " + java.lang.Integer + " + +
+
+
+ +
+
+ +
+ + + + + + + + " + + + aggregateFunction + + + " + + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + " + + + class + + + " + + + + : + + +
+ +
+ + + " + uk.gov.gchq.koryphe.impl.binaryoperator.Sum + " + +
+
+
+ +
+
+
+
+ + + } + + +
+
+
+
+ + + } + + +
+
+ +
+ + + + + + + + " + + + true + + + " + + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + " + + + description + + + " + + + + : + + +
+ +
+ + + " + A simple boolean that must always be true. + " + +
+
+
+ +
+
+ +
+ + + + " + + + class + + + " + + + + : + + +
+ +
+ + + " + java.lang.Boolean + " + +
+
+
+ +
+
+ +
+ + + + + + + + " + + + validateFunctions + + + " + + + + : + + + + + [ + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + + + + 0 + + + : + + + + + { + + + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + + " + + + class + + + " + + + + : + + +
+ +
+ + + " + uk.gov.gchq.koryphe.impl.predicate.IsTrue + " + +
+
+
+ +
+
+
+
+ + + } + + +
+
+
+
+ + + ] + + +
+
+
+
+ + + } + + +
+
+
+
+ + + } + + +
+
+
+
+ + + } + + +
+
+
+
+ + + } + + +
+
+
+
+
+ +
+
+
+
+
+ + + + + +
+
+
+
+
+
+
+
+
+
+`; From 5fdfecbf93cabe2449761f77cd73838dd0b4db71 Mon Sep 17 00:00:00 2001 From: jpelbertrios Date: Thu, 3 Sep 2020 17:02:12 +0100 Subject: [PATCH 19/23] Unit tests for NavAppbar and ViewGraph --- .../Navigation/NavigationAppbar.tsx | 6 +- ui/src/components/ViewGraph/ViewGraph.tsx | 7 +-- .../Navigation/NavigationAppbar.test.tsx | 58 +++++++++++++++++++ .../components/ViewGraph/ViewGraph.test.tsx | 55 ++++++++++++++++++ 4 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 ui/test/components/Navigation/NavigationAppbar.test.tsx create mode 100644 ui/test/components/ViewGraph/ViewGraph.test.tsx diff --git a/ui/src/components/Navigation/NavigationAppbar.tsx b/ui/src/components/Navigation/NavigationAppbar.tsx index 38bf7e6..3c94009 100644 --- a/ui/src/components/Navigation/NavigationAppbar.tsx +++ b/ui/src/components/Navigation/NavigationAppbar.tsx @@ -68,6 +68,7 @@ const useStyles = makeStyles((theme: Theme) => ); const NavigationAppbar: React.FC = (props: any) => { + const classes = useStyles(); const [isOpen, setIsOpen] = useState(false); const toggleDrawer = (open: boolean) => ( @@ -87,7 +88,7 @@ const NavigationAppbar: React.FC = (props: any) => { const activeRoute = (routeName: any) => { return props.location.pathname === routeName ? true : false; } - const getAvataricon = (sidebarName: any) => { + const getSideNavIcon = (sidebarName: any) => { switch(sidebarName) { case 'Add Graph': return (); @@ -137,7 +138,7 @@ const NavigationAppbar: React.FC = (props: any) => { key={key}> - {getAvataricon(prop.sidebarName)} + {getSideNavIcon(prop.sidebarName)} @@ -154,4 +155,3 @@ const NavigationAppbar: React.FC = (props: any) => { }; export default withRouter(NavigationAppbar); - diff --git a/ui/src/components/ViewGraph/ViewGraph.tsx b/ui/src/components/ViewGraph/ViewGraph.tsx index ae588c5..b6dc4f7 100644 --- a/ui/src/components/ViewGraph/ViewGraph.tsx +++ b/ui/src/components/ViewGraph/ViewGraph.tsx @@ -53,9 +53,9 @@ export default class ViewGraph extends React.Component<{}, IState> { - Graph Name + Graph Name Current State - Update + Delete @@ -67,9 +67,6 @@ export default class ViewGraph extends React.Component<{}, IState> { {graph.getId()} {graph.getStatus()} - - - diff --git a/ui/test/components/Navigation/NavigationAppbar.test.tsx b/ui/test/components/Navigation/NavigationAppbar.test.tsx new file mode 100644 index 0000000..c05b3ff --- /dev/null +++ b/ui/test/components/Navigation/NavigationAppbar.test.tsx @@ -0,0 +1,58 @@ +import {mount, shallow, configure} from 'enzyme'; +import NavigationAppbar from '../../../src/components/Navigation/NavigationAppbar'; +import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; + + +describe('Navigation Appbar Component', () => { + + const wrapper = mount( + + + ); + + it('should display appbar ', () => { + const appbar = wrapper.find('h6') + expect(appbar).toHaveLength(1); + expect(appbar.text()).toEqual('Graph As Service'); + }); + + it('should show user id and email in Navbar', () => { + const NavUl = wrapper.find('ul').at(0); + const UserIcon = NavUl.find('svg'); + + expect(NavUl.find('span').text()).toEqual('User'); + expect(NavUl.find('p').text()).toEqual('someuser@mail.com'); + expect(UserIcon).toHaveLength(1); + }); + + it('should display menu in Navbar', () => { + const cols = [ + {name: 'Add Graph'}, + {name: 'View Graph'}, + {name: 'User Guide'} + ]; + const NavLi = wrapper.find('li').at(1); + NavLi.forEach((li, idx) => { + const NavIcon = li.find('svg'); + expect(li.text()).toEqual(cols[idx].name); + expect(NavIcon).toHaveLength(1); + + }); + }); + + it('should have navigation link in each list item', () => { + const Target = [ + {href: '/AddGraph'}, + {href: '/ViewGraph'}, + {href: '/UserGuide'} + ]; + const NavUl = wrapper.find('ul').at(1); + + NavUl.forEach((NavUl, idx) => { + const anchor = NavUl.find('a').at(idx); + const getAttribute = anchor.getDOMNode().getAttribute('href'); + expect(getAttribute).toBe(Target[idx].href) + }) + }); +}); diff --git a/ui/test/components/ViewGraph/ViewGraph.test.tsx b/ui/test/components/ViewGraph/ViewGraph.test.tsx new file mode 100644 index 0000000..c7a46f1 --- /dev/null +++ b/ui/test/components/ViewGraph/ViewGraph.test.tsx @@ -0,0 +1,55 @@ +import {mount} from 'enzyme'; +import ExampleTable from '../../../src/components/ViewGraph/ViewGraph'; +import React from 'react'; +import { render } from '@testing-library/react'; +import { createRenderer } from 'react-dom/test-utils'; + +describe('When ExampleTable mounts', () => { + const wrapper = mount(); + const rows = wrapper.find('tbody').find('tr'); + + it('should display only 1 table element', () => { + const table = wrapper.find('table'); + expect(table).toHaveLength(1); + }); + + it('should display only 3 columns in the table element', () => { + const tableHead = wrapper.find('th'); + expect(tableHead).toHaveLength(3); + }); + + it('should display Graph Id and Current State Columns', () => { + const cols = [ + {name: 'Graph Name'}, + {name: 'Current State'}, + {name: 'Delete'} + ]; + const tableHead = wrapper.find('th'); + tableHead.forEach((th, idx) => { + expect(th.text()).toEqual(cols[idx].name); + }); + }); + + it('should only have 1 table body', () => { + const tableBody = wrapper.find('tbody'); + expect(tableBody).toHaveLength(1); + }); + + it('should get all the graphs and display it in the table', () => { + rows.forEach(() => { + const cells = rows.find('td'); + expect(cells.at(0).text()).toEqual("testId1"); + expect(cells.at(1).text()).toEqual("deployed"); + expect(cells.at(2).text()).toEqual("Delete"); + }); + }); + + it('should have Delete button in each row', () => { + rows.forEach(() => { + const cells = rows.find('td'); + expect(cells.at(0).find('svg')).toHaveLength(1); + expect(cells.at(1).find('svg')).toHaveLength(1); + expect(cells.at(2).find('svg')).toHaveLength(1); + }); + }); +}); From 688ee3c5908425839af8528ad7970b2ab612b4f9 Mon Sep 17 00:00:00 2001 From: K-itKat20 Date: Fri, 4 Sep 2020 11:39:14 +0100 Subject: [PATCH 20/23] Deleting unused files --- ui/src/components/Graphs/CreateGraph.tsx | 226 ------------------ ui/src/components/Graphs/ExampleTable.tsx | 79 ------ .../components/Graphs/CreateGraph.test.tsx | 33 --- .../components/Graphs/ExampleTable.test.tsx | 39 --- 4 files changed, 377 deletions(-) delete mode 100644 ui/src/components/Graphs/CreateGraph.tsx delete mode 100644 ui/src/components/Graphs/ExampleTable.tsx delete mode 100644 ui/test/components/Graphs/CreateGraph.test.tsx delete mode 100644 ui/test/components/Graphs/ExampleTable.test.tsx diff --git a/ui/src/components/Graphs/CreateGraph.tsx b/ui/src/components/Graphs/CreateGraph.tsx deleted file mode 100644 index 7655f4d..0000000 --- a/ui/src/components/Graphs/CreateGraph.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import React from 'react'; -import { - Button, - Container, - CssBaseline, - Dialog, - DialogActions, - DialogContent, - Grid, - IconButton, - Slide, - TextField, - Typography -} from '@material-ui/core' -import { Alert } from '@material-ui/lab'; -import {TransitionProps} from '@material-ui/core/transitions'; -import ClearIcon from "@material-ui/icons/Clear"; -import {makeStyles} from "@material-ui/core/styles"; -import { Schema } from '../../domain/schema'; -import { Notifications } from '../../domain/notifications'; -import { CreateGraphRepo } from '../../rest/repositories/create-graph-repo'; - -const useStyles = makeStyles((theme) => ({ - paper: { - marginTop: theme.spacing(2), - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - }, - avatar: { - margin: theme.spacing(1), - backgroundColor: theme.palette.secondary.main, - }, - form: { - width: '100%', // Fix IE 11 issue. - marginTop: theme.spacing(3), - }, - submit: { - margin: theme.spacing(3, 0, 2), - }, - button: { - margin: "10px", - } -})); - -const Transition = React.forwardRef(function Transition( - props: TransitionProps & { children?: React.ReactElement }, - ref: React.Ref, -) { - return ; -}); - -interface IState { - dialogIsOpen: boolean, - newGraph: { - graphId: string, - administrators: Array, - schemaJson: string, - } - notifications: Notifications, -} - -export default class CreateGraph extends React.Component<{}, IState> { - constructor(props: Object) { - super(props); - this.state = { - dialogIsOpen: false, - newGraph: { - graphId: "", - administrators: [], - schemaJson: "", - }, - notifications: new Notifications(), - } - } - - private classes: any = makeStyles((theme) => ({ - paper: { - marginTop: theme.spacing(2), - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - }, - avatar: { - margin: theme.spacing(1), - backgroundColor: theme.palette.secondary.main, - }, - form: { - width: '100%', // Fix IE 11 issue. - marginTop: theme.spacing(3), - }, - submit: { - margin: theme.spacing(3, 0, 2), - }, - button: { - margin: "10px", - } - })); - - private async submitNewGraph() { - const { graphId, administrators, schemaJson } = this.state.newGraph; - const schema = new Schema(schemaJson); - const notifications: Notifications = schema.validation(); - - if (notifications.isEmpty()) { - await new CreateGraphRepo().create(graphId, administrators, schema); - this.setState({ dialogIsOpen: false }); - - } else { - this.setState({ notifications: notifications }); - } - } - - public render() { - const openDialogBox = () => { - this.setState({ dialogIsOpen: true }); - }; - const closeDialogBox = () => { - this.setState({ dialogIsOpen: false }); - }; - - return ( -
- - - - - - - - - - - - - {!this.state.notifications.isEmpty() && - Error(s): {this.state.notifications.errorMessage()}} -
- - Create Graph - -
- - - - - - { - this.setState( { - newGraph: { - ...this.state.newGraph, - graphId: event.target.value - } - }); - }} - /> - - - - { - this.setState({ - newGraph: { - ...this.state.newGraph, - schemaJson: event.target.value - } - }); - }} - /> - - -
-
-
-
- - - - - -
-
- ); - } -} diff --git a/ui/src/components/Graphs/ExampleTable.tsx b/ui/src/components/Graphs/ExampleTable.tsx deleted file mode 100644 index a20fd2f..0000000 --- a/ui/src/components/Graphs/ExampleTable.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react'; -import {makeStyles} from '@material-ui/core/styles'; -import { Box, Button, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@material-ui/core' -import { Graph } from '../../domain/graph'; -import CreateGraph from "./CreateGraph"; -import { DeleteGraphRepo } from '../../rest/repositories/delete-graph-repo'; -import { GetAllGraphsRepo } from '../../rest/repositories/get-all-graphs-repo'; - -interface IState { - graphs: Graph[], - selectedRow: any, -} - -export default class ExampleTable extends React.Component<{}, IState> { - constructor(props: Object) { - super(props); - this.state = { - graphs: [], - selectedRow: '', - } - } - - public async componentDidMount() { - try { - this.setState({ graphs: await new GetAllGraphsRepo().getAll() }); - } catch (e) { - console.log(e.message); - } - } - - private classes: any = makeStyles({ - table: { - minWidth: 650, - }, - }); - - private async deleteAndGetGraphs(): Promise { - await new DeleteGraphRepo().delete(this.state.selectedRow); - return await new GetAllGraphsRepo().getAll(); - } - - public render() { - return ( - - - - - - - Graph ID - Current State - - - - - {this.state.graphs.map((graph: Graph) => ( - this.setState({selectedRow: graph.getId()})}> - - {graph.getId()} - {graph.getStatus()} - - ))} - -
- - -
- - - - -
- - ); - } -} diff --git a/ui/test/components/Graphs/CreateGraph.test.tsx b/ui/test/components/Graphs/CreateGraph.test.tsx deleted file mode 100644 index 9d5212a..0000000 --- a/ui/test/components/Graphs/CreateGraph.test.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import {mount} from 'enzyme'; -import CreateGraph from "../../../src/components/Graphs/CreateGraph"; -import React from 'react'; - -describe('When CreateGraph dialog mounts', () => { - const wrapper = mount(); - it('should have a Create Graph title' , () => { - const title = wrapper.find('h2'); - expect(title).toHaveLength(1); - expect(title.text()).toBe('Create Graph') - - }); - it('should have a Graph Id text field', () => { - const textfield= wrapper.find('input'); - expect(textfield).toHaveLength(1); - }); - it('should have a Schema text area', () => { - const textfield= wrapper.find('textarea'); - expect(textfield).toHaveLength(1); - }); - it('should have a Submit button', () => { - const submitButton= wrapper.find('button').at(2).text(); - expect(submitButton).toBe("Submit"); - }); - it('should have a close icon button', () => { - const closeButton= wrapper.find('button').at(1).find('svg'); - expect(closeButton).toHaveLength(1); - // console.log(closeButton); - }) - -}); - - diff --git a/ui/test/components/Graphs/ExampleTable.test.tsx b/ui/test/components/Graphs/ExampleTable.test.tsx deleted file mode 100644 index dbde703..0000000 --- a/ui/test/components/Graphs/ExampleTable.test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import {mount} from 'enzyme'; -import ExampleTable from '../../../src/components/Graphs/ExampleTable'; -import React from 'react'; - -describe('When ExampleTable mounts', () => { - const wrapper = mount(); - - it('should display only 1 table element', () => { - const table = wrapper.find('table'); - expect(table).toHaveLength(1); - }); - it('should display only 2 columns in the table element', () => { - const tableHead = wrapper.find('th'); - expect(tableHead).toHaveLength(2); - }); - it('should display Graph Id and Current State Columns', () => { - const cols = [ - {name: 'Graph ID'}, - {name: 'Current State'}, - ]; - const tableHead = wrapper.find('th'); - tableHead.forEach((th, idx) => { - expect(th.text()).toEqual(cols[idx].name); - }); - }); - it('should only have 1 table body', () => { - const tableBody = wrapper.find('tbody'); - expect(tableBody).toHaveLength(1); - }); - it('should get all the graphs and display it in the table', () =>{ - const rows = wrapper.find('tbody').find('tr'); - // expect(rows).toHaveLength(1); - rows.forEach((tr, rowIndex) => { - const cells = rows.find('td'); - expect(cells.at(0).text()).toEqual("testId1"); - expect(cells.at(1).text()).toEqual("deployed"); - }) - }) -}); From 057cc8cab1038c235e6ede0a9691e86e122979b8 Mon Sep 17 00:00:00 2001 From: macenturalxl1 Date: Fri, 4 Sep 2020 15:15:35 +0100 Subject: [PATCH 21/23] Kai README back to root --- README.md | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..c9ec18c --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +Kai +================== +Kai is an experimental Graph as a Service application built on AWS. It uses the Amazon CDK. + +The `cdk.json` file tells the CDK Toolkit how to execute your app. + +NOTE: As Kai is currently early in development and likely subject to breaking changes, we do not advise this product be used in any production capacity. +If you have an interest in using Kai in production, please watch this repository to stay updated. + +## Useful commands + + * `npm run build` compile typescript to js + * `npm run watch` watch for changes and compile + * `npm run lint` run the eslint style checking + * `npm run test` perform the jest unit tests + * `cdk deploy` deploy this stack to your default AWS account/region + * `cdk diff` compare deployed stack with current state + * `cdk synth` emits the synthesized CloudFormation template + +## Configuration + +Kai has a number of different properties which can be altered using the `cdk.json` file or by passing +in context objects through the --context option + +Name | Type | Default value | Description +---------------------------|---------------|---------------|---------------- +vpcId | string | "DEFAULT" | The Vpc that the eks cluster will use. By default it uses the default VPC for the account you're deploying with. If this is removed, a VPC will be created. If a VPC id is specified it will use that VPC. +extraIngressSecurityGroups | string | "" | Additional vpcs that will be added to every application load balancer that comes with a gaffer deployment. To Add multiple ones, use a comma seperated list eg "sg-xxxxxxxxx, sg-yyyyyyyyyy". The security group of the EKS cluster is automatically added. +globalTags | object | {} | Tags that get added to every taggable resource. +clusterNodeGroup | object | null | Configuration for the eks cluster nodegroup. See below for details. +userPoolConfiguration | object | null | Cognito UserPool configuration. See below for details. +graphDatabaseProps | object | see cdk.json | Configuration for the Dynamodb graph database's autoscaling. + +## Changing the nodegroup properties + +By default, Kai ships with a nodegroup with the following parameters: +```json +{ + "instanceType": "m3.medium", + "minSize": 1, + "maxSize": 10, + "preferredSize": 2 +} +``` + +These properties are changeable through the context variable: "clusterNodeGroup". + +## Graph Database Autoscaling +Depending on your needs, you may want to change the autoscaling properties of the Graph Database. The default properties in the cdk.json file are as follows: +```json +{ + "graphDatabaseProps": { + "minCapacity": 1, + "maxCapacity": 25, + "targetUtilizationPercent": 80 + } +} +``` +The min and max capacity relate to amazon's [read and write capacity units](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ProvisionedThroughput.html#ProvisionedThroughput.CapacityUnits.Read) + +These settings haven't yet been tested at production scale so may change when we do. + +## Cognito UserPool configuration + +By default Kai uses a vanilla AWS Cognito UserPool to manage authentication with the application. +The default UserPool and UserPoolClient settings can be overridden by supplying a `userPoolConfiguration` context option populated as shown here: +```json +{ + "defaultPoolConfig": { + "userPoolProps": { + "selfSignUpEnabled": false // See below for full options + }, + "userPoolClientOptions": { + "disableOAuth": true // See below for full options + } + } +} +``` +The full list of [userPoolProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-cognito.UserPoolProps.html) and [userPoolClientOptions](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-cognito.UserPoolClientOptions.html) can be found on Amazon's docs + +Alternatively a pre-configured external pool can be referenced using the following example: +```json +{ + "externalPool": { + "userPoolId": "myRegion_userPoolId", + "userPoolClientId": "randomString" + } +} +``` From 1b5e0c035dcb32b6b6c251c751bb5880bd19af04 Mon Sep 17 00:00:00 2001 From: macenturalxl1 Date: Fri, 4 Sep 2020 15:16:53 +0100 Subject: [PATCH 22/23] Removed README from infra to root --- infrastructure/README.md | 89 ---------------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 infrastructure/README.md diff --git a/infrastructure/README.md b/infrastructure/README.md deleted file mode 100644 index c9ec18c..0000000 --- a/infrastructure/README.md +++ /dev/null @@ -1,89 +0,0 @@ -Kai -================== -Kai is an experimental Graph as a Service application built on AWS. It uses the Amazon CDK. - -The `cdk.json` file tells the CDK Toolkit how to execute your app. - -NOTE: As Kai is currently early in development and likely subject to breaking changes, we do not advise this product be used in any production capacity. -If you have an interest in using Kai in production, please watch this repository to stay updated. - -## Useful commands - - * `npm run build` compile typescript to js - * `npm run watch` watch for changes and compile - * `npm run lint` run the eslint style checking - * `npm run test` perform the jest unit tests - * `cdk deploy` deploy this stack to your default AWS account/region - * `cdk diff` compare deployed stack with current state - * `cdk synth` emits the synthesized CloudFormation template - -## Configuration - -Kai has a number of different properties which can be altered using the `cdk.json` file or by passing -in context objects through the --context option - -Name | Type | Default value | Description ----------------------------|---------------|---------------|---------------- -vpcId | string | "DEFAULT" | The Vpc that the eks cluster will use. By default it uses the default VPC for the account you're deploying with. If this is removed, a VPC will be created. If a VPC id is specified it will use that VPC. -extraIngressSecurityGroups | string | "" | Additional vpcs that will be added to every application load balancer that comes with a gaffer deployment. To Add multiple ones, use a comma seperated list eg "sg-xxxxxxxxx, sg-yyyyyyyyyy". The security group of the EKS cluster is automatically added. -globalTags | object | {} | Tags that get added to every taggable resource. -clusterNodeGroup | object | null | Configuration for the eks cluster nodegroup. See below for details. -userPoolConfiguration | object | null | Cognito UserPool configuration. See below for details. -graphDatabaseProps | object | see cdk.json | Configuration for the Dynamodb graph database's autoscaling. - -## Changing the nodegroup properties - -By default, Kai ships with a nodegroup with the following parameters: -```json -{ - "instanceType": "m3.medium", - "minSize": 1, - "maxSize": 10, - "preferredSize": 2 -} -``` - -These properties are changeable through the context variable: "clusterNodeGroup". - -## Graph Database Autoscaling -Depending on your needs, you may want to change the autoscaling properties of the Graph Database. The default properties in the cdk.json file are as follows: -```json -{ - "graphDatabaseProps": { - "minCapacity": 1, - "maxCapacity": 25, - "targetUtilizationPercent": 80 - } -} -``` -The min and max capacity relate to amazon's [read and write capacity units](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ProvisionedThroughput.html#ProvisionedThroughput.CapacityUnits.Read) - -These settings haven't yet been tested at production scale so may change when we do. - -## Cognito UserPool configuration - -By default Kai uses a vanilla AWS Cognito UserPool to manage authentication with the application. -The default UserPool and UserPoolClient settings can be overridden by supplying a `userPoolConfiguration` context option populated as shown here: -```json -{ - "defaultPoolConfig": { - "userPoolProps": { - "selfSignUpEnabled": false // See below for full options - }, - "userPoolClientOptions": { - "disableOAuth": true // See below for full options - } - } -} -``` -The full list of [userPoolProps](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-cognito.UserPoolProps.html) and [userPoolClientOptions](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-cognito.UserPoolClientOptions.html) can be found on Amazon's docs - -Alternatively a pre-configured external pool can be referenced using the following example: -```json -{ - "externalPool": { - "userPoolId": "myRegion_userPoolId", - "userPoolClientId": "randomString" - } -} -``` From bb7989614722a64cf64813711646c3ae0d5ce425 Mon Sep 17 00:00:00 2001 From: macenturalxl1 Date: Fri, 4 Sep 2020 15:26:22 +0100 Subject: [PATCH 23/23] Added UI job to travis yml --- .travis.yml | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index b4f02fa..c2bd16d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,12 +3,26 @@ dist: bionic node_js: - node -before_install: - - cd infrastructure/ +jobs: + fast_finish: true + include: + - name: Infrastructure + before_install: + - cd infrastructure/ -install: - - npm install + install: + - npm install -script: - - npm run lint - - npm run test \ No newline at end of file + script: + - npm run lint + - npm run test + + - name: UI + before_install: + - cd ui/ + + install: + - npm install + + script: + - npm run test \ No newline at end of file