diff --git a/src/_mocks/template.yml b/src/_mocks/template.yml new file mode 100644 index 0000000..b5c7c36 --- /dev/null +++ b/src/_mocks/template.yml @@ -0,0 +1,266 @@ +AWSTemplateFormatVersion: 2010-09-09 +Description: The AWS CloudFormation template for this Serverless application +Resources: + ServerlessDeploymentBucket: + Type: AWS::S3::Bucket + Properties: + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: AES256 + ServerlessDeploymentBucketPolicy: + Type: AWS::S3::BucketPolicy + Properties: + Bucket: !Ref ServerlessDeploymentBucket + PolicyDocument: + Statement: + - Action: s3:* + Effect: Deny + Principal: '*' + Resource: + - !Join + - '' + - - 'arn:' + - !Ref AWS::Partition + - ':s3:::' + - !Ref ServerlessDeploymentBucket + - /* + - !Join + - '' + - - 'arn:' + - !Ref AWS::Partition + - ':s3:::' + - !Ref ServerlessDeploymentBucket + Condition: + Bool: + aws:SecureTransport: false + ProducerLogGroup: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: /aws/lambda/aws-node-sqs-worker-project-dev-producer + JobsWorkerLogGroup: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: /aws/lambda/aws-node-sqs-worker-project-dev-jobsWorker + IamRoleLambdaExecution: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - sts:AssumeRole + Policies: + - PolicyName: !Join + - '-' + - - aws-node-sqs-worker-project + - dev + - lambda + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - logs:CreateLogStream + - logs:CreateLogGroup + - logs:TagResource + Resource: + - !Sub >- + arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-node-sqs-worker-project-dev*:* + - Effect: Allow + Action: + - logs:PutLogEvents + Resource: + - !Sub >- + arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-node-sqs-worker-project-dev*:*:* + - Effect: Allow + Action: + - sqs:SendMessage + - sqs:ChangeMessageVisibility + Resource: + - !GetAtt jobsQueueCEDBAE3E.Arn + - Effect: Allow + Action: + - sqs:ReceiveMessage + - sqs:DeleteMessage + - sqs:GetQueueAttributes + Resource: + - !GetAtt jobsQueueCEDBAE3E.Arn + Path: / + RoleName: !Join + - '-' + - - aws-node-sqs-worker-project + - dev + - !Ref AWS::Region + - lambdaRole + ProducerLambdaFunction: + Type: AWS::Lambda::Function + Properties: + Code: + S3Bucket: !Ref ServerlessDeploymentBucket + S3Key: >- + serverless/aws-node-sqs-worker-project/dev/1705833879496-2024-01-21T10:44:39.496Z/aws-node-sqs-worker-project.zip + Handler: index.producer + Runtime: nodejs18.x + FunctionName: aws-node-sqs-worker-project-dev-producer + MemorySize: 1024 + Timeout: 6 + Environment: + Variables: + QUEUE_URL: !Ref jobsQueueCEDBAE3E + Role: !GetAtt IamRoleLambdaExecution.Arn + DependsOn: + - ProducerLogGroup + JobsWorkerLambdaFunction: + Type: AWS::Lambda::Function + Properties: + Code: + S3Bucket: !Ref ServerlessDeploymentBucket + S3Key: >- + serverless/aws-node-sqs-worker-project/dev/1705833879496-2024-01-21T10:44:39.496Z/aws-node-sqs-worker-project.zip + Handler: index.consumer + Runtime: nodejs18.x + FunctionName: aws-node-sqs-worker-project-dev-jobsWorker + MemorySize: 1024 + Timeout: 6 + Role: !GetAtt IamRoleLambdaExecution.Arn + ReservedConcurrentExecutions: 200 + DependsOn: + - JobsWorkerLogGroup + ProducerLambdaVersionuTQ7t4amwQGQCm6r1nMXU48wnVVaGIpkfkz7ZdThFp0: + Type: AWS::Lambda::Version + DeletionPolicy: Retain + Properties: + FunctionName: !Ref ProducerLambdaFunction + CodeSha256: AnrSvCAIoAKh7dIyDh8TyJ5Jbrqhyd8KoY1zzxb7XCM= + JobsWorkerLambdaVersionx1AYxWc2gFMOTQcCDa9Z0cA1wcVonrxuYZMubQpE: + Type: AWS::Lambda::Version + DeletionPolicy: Retain + Properties: + FunctionName: !Ref JobsWorkerLambdaFunction + CodeSha256: AnrSvCAIoAKh7dIyDh8TyJ5Jbrqhyd8KoY1zzxb7XCM= + JobsWorkerEventSourceMappingSQSJobsQueueCEDBAE3E: + Type: AWS::Lambda::EventSourceMapping + DependsOn: + - IamRoleLambdaExecution + Properties: + BatchSize: 5 + MaximumBatchingWindowInSeconds: 33 + EventSourceArn: !GetAtt jobsQueueCEDBAE3E.Arn + FunctionName: !GetAtt JobsWorkerLambdaFunction.Arn + Enabled: false + FunctionResponseTypes: + - ReportBatchItemFailures + ScalingConfig: + MaximumConcurrency: 1000 + HttpApi: + Type: AWS::ApiGatewayV2::Api + Properties: + Name: dev-aws-node-sqs-worker-project + ProtocolType: HTTP + HttpApiStage: + Type: AWS::ApiGatewayV2::Stage + Properties: + ApiId: !Ref HttpApi + StageName: $default + AutoDeploy: true + DefaultRouteSettings: + DetailedMetricsEnabled: false + ProducerLambdaPermissionHttpApi: + Type: AWS::Lambda::Permission + Properties: + FunctionName: !GetAtt ProducerLambdaFunction.Arn + Action: lambda:InvokeFunction + Principal: apigateway.amazonaws.com + SourceArn: !Join + - '' + - - 'arn:' + - !Ref AWS::Partition + - ':execute-api:' + - !Ref AWS::Region + - ':' + - !Ref AWS::AccountId + - ':' + - !Ref HttpApi + - /* + HttpApiIntegrationProducer: + Type: AWS::ApiGatewayV2::Integration + Properties: + ApiId: !Ref HttpApi + IntegrationType: AWS_PROXY + IntegrationUri: !GetAtt ProducerLambdaFunction.Arn + PayloadFormatVersion: '2.0' + TimeoutInMillis: 30000 + HttpApiRoutePostProduce: + Type: AWS::ApiGatewayV2::Route + Properties: + ApiId: !Ref HttpApi + RouteKey: POST /produce + Target: !Join + - / + - - integrations + - !Ref HttpApiIntegrationProducer + DependsOn: HttpApiIntegrationProducer + jobsDlqD18CF374: + Type: AWS::SQS::Queue + Properties: + MessageRetentionPeriod: 1209600 + QueueName: aws-node-sqs-worker-project-dev-jobs-dlq + UpdateReplacePolicy: Delete + DeletionPolicy: Delete + jobsQueueCEDBAE3E: + Type: AWS::SQS::Queue + Properties: + DelaySeconds: 60 + QueueName: aws-node-sqs-worker-project-dev-jobs + RedrivePolicy: + deadLetterTargetArn: !GetAtt jobsDlqD18CF374.Arn + maxReceiveCount: 1 + VisibilityTimeout: 1 + UpdateReplacePolicy: Delete + DeletionPolicy: Delete +Outputs: + ServerlessDeploymentBucketName: + Value: !Ref ServerlessDeploymentBucket + Export: + Name: sls-aws-node-sqs-worker-project-dev-ServerlessDeploymentBucketName + ProducerLambdaFunctionQualifiedArn: + Description: Current Lambda function version + Value: !Ref ProducerLambdaVersionuTQ7t4amwQGQCm6r1nMXU48wnVVaGIpkfkz7ZdThFp0 + Export: + Name: sls-aws-node-sqs-worker-project-dev-ProducerLambdaFunctionQualifiedArn + JobsWorkerLambdaFunctionQualifiedArn: + Description: Current Lambda function version + Value: !Ref JobsWorkerLambdaVersionx1AYxWc2gFMOTQcCDa9Z0cA1wcVonrxuYZMubQpE + Export: + Name: sls-aws-node-sqs-worker-project-dev-JobsWorkerLambdaFunctionQualifiedArn + HttpApiId: + Description: Id of the HTTP API + Value: !Ref HttpApi + Export: + Name: sls-aws-node-sqs-worker-project-dev-HttpApiId + HttpApiUrl: + Description: URL of the HTTP API + Value: !Join + - '' + - - https:// + - !Ref HttpApi + - .execute-api. + - !Ref AWS::Region + - . + - !Ref AWS::URLSuffix + Export: + Name: sls-aws-node-sqs-worker-project-dev-HttpApiUrl + jobsQueueArnA5A2FF7E: + Description: ARN of the "jobs" SQS queue. + Value: !GetAtt jobsQueueCEDBAE3E.Arn + jobsQueueUrl573F5B7A: + Description: URL of the "jobs" SQS queue. + Value: !Ref jobsQueueCEDBAE3E + jobsDlqUrl2C7FA9D4: + Description: URL of the "jobs" SQS dead letter queue. + Value: !Ref jobsDlqD18CF374 \ No newline at end of file diff --git a/src/_mocks/template_json.ts b/src/_mocks/template_json.ts new file mode 100644 index 0000000..57a9895 --- /dev/null +++ b/src/_mocks/template_json.ts @@ -0,0 +1,462 @@ +export const TEMPLATE_JSON = { + AWSTemplateFormatVersion: "2010-09-09", + Description: "An example AWS CloudFormation template", + Resources: { + ServerlessDeploymentBucket: { + Type: "AWS::S3::Bucket", + Properties: { + BucketEncryption: { + ServerSideEncryptionConfiguration: [ + { + ServerSideEncryptionByDefault: { + SSEAlgorithm: "AES256", + }, + }, + ], + }, + }, + }, + ServerlessDeploymentBucketPolicy: { + Type: "AWS::S3::BucketPolicy", + Properties: { + Bucket: { + Ref: "ServerlessDeploymentBucket", + }, + PolicyDocument: { + Statement: [ + { + Action: "s3:*", + Effect: "Deny", + Principal: "*", + Resource: [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + Ref: "AWS::Partition", + }, + ":s3:::", + { + Ref: "ServerlessDeploymentBucket", + }, + "/*", + ], + ], + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + Ref: "AWS::Partition", + }, + ":s3:::", + { + Ref: "ServerlessDeploymentBucket", + }, + ], + ], + }, + ], + Condition: { + Bool: { + "aws:SecureTransport": false, + }, + }, + }, + ], + }, + }, + }, + ProducerLogGroup: { + Type: "AWS::Logs::LogGroup", + Properties: { + LogGroupName: "/aws/lambda/aws-node-sqs-worker-project-dev-producer", + }, + }, + JobsWorkerLogGroup: { + Type: "AWS::Logs::LogGroup", + Properties: { + LogGroupName: "/aws/lambda/my_worker_function", + }, + }, + IamRoleLambdaExecution: { + Type: "AWS::IAM::Role", + Properties: { + AssumeRolePolicyDocument: { + Version: "2012-10-17", + Statement: [ + { + Effect: "Allow", + Principal: { + Service: ["lambda.amazonaws.com"], + }, + Action: ["sts:AssumeRole"], + }, + ], + }, + Policies: [ + { + PolicyName: { + "Fn::Join": [ + "-", + ["aws-node-sqs-worker-project", "dev", "lambda"], + ], + }, + PolicyDocument: { + Version: "2012-10-17", + Statement: [ + { + Effect: "Allow", + Action: [ + "logs:CreateLogStream", + "logs:CreateLogGroup", + "logs:TagResource", + ], + Resource: [ + { + "Fn::Sub": + "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-node-sqs-worker-project-dev*:*", + }, + ], + }, + { + Effect: "Allow", + Action: ["logs:PutLogEvents"], + Resource: [ + { + "Fn::Sub": + "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-node-sqs-worker-project-dev*:*:*", + }, + ], + }, + { + Effect: "Allow", + Action: ["sqs:SendMessage", "sqs:ChangeMessageVisibility"], + Resource: [ + { + "Fn::GetAtt": ["my_jobs_queue", "Arn"], + }, + ], + }, + { + Effect: "Allow", + Action: [ + "sqs:ReceiveMessage", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + ], + Resource: [ + { + "Fn::GetAtt": ["my_jobs_queue", "Arn"], + }, + ], + }, + ], + }, + }, + ], + Path: "/", + RoleName: { + "Fn::Join": [ + "-", + [ + "aws-node-sqs-worker-project", + "dev", + { + Ref: "AWS::Region", + }, + "lambdaRole", + ], + ], + }, + }, + }, + ProducerLambdaFunction: { + Type: "AWS::Lambda::Function", + Properties: { + Code: { + S3Bucket: { + Ref: "ServerlessDeploymentBucket", + }, + S3Key: + "serverless/aws-node-sqs-worker-project/dev/1705833879496-2024-01-21T10:44:39.496Z/aws-node-sqs-worker-project.zip", + }, + Handler: "index.producer", + Runtime: "nodejs18.x", + FunctionName: "aws-node-sqs-worker-project-dev-producer", + MemorySize: 1024, + Timeout: 6, + Environment: { + Variables: { + QUEUE_URL: { + Ref: "my_jobs_queue", + }, + }, + }, + Role: { + "Fn::GetAtt": ["IamRoleLambdaExecution", "Arn"], + }, + }, + DependsOn: ["ProducerLogGroup"], + }, + JobsWorkerLambdaFunction: { + Type: "AWS::Lambda::Function", + Properties: { + Code: { + S3Bucket: { + Ref: "ServerlessDeploymentBucket", + }, + S3Key: + "serverless/aws-node-sqs-worker-project/dev/1705833879496-2024-01-21T10:44:39.496Z/aws-node-sqs-worker-project.zip", + }, + Handler: "index.consumer", + Runtime: "nodejs18.x", + FunctionName: "my_worker_function", + MemorySize: 1024, + Timeout: 6, + Role: { + "Fn::GetAtt": ["IamRoleLambdaExecution", "Arn"], + }, + ReservedConcurrentExecutions: 200, + }, + DependsOn: ["JobsWorkerLogGroup"], + }, + ProducerLambdaVersionuTQ7t4amwQGQCm6r1nMXU48wnVVaGIpkfkz7ZdThFp0: { + Type: "AWS::Lambda::Version", + DeletionPolicy: "Retain", + Properties: { + FunctionName: { + Ref: "ProducerLambdaFunction", + }, + CodeSha256: "AnrSvCAIoAKh7dIyDh8TyJ5Jbrqhyd8KoY1zzxb7XCM=", + }, + }, + JobsWorkerLambdaVersionx1AYxWc2gFMOTQcCDa9Z0cA1wcVonrxuYZMubQpE: { + Type: "AWS::Lambda::Version", + DeletionPolicy: "Retain", + Properties: { + FunctionName: { + Ref: "JobsWorkerLambdaFunction", + }, + CodeSha256: "AnrSvCAIoAKh7dIyDh8TyJ5Jbrqhyd8KoY1zzxb7XCM=", + }, + }, + JobsWorkerEventSourceMappingSQSmy_jobs_queue: { + Type: "AWS::Lambda::EventSourceMapping", + DependsOn: ["IamRoleLambdaExecution"], + Properties: { + BatchSize: 5, + MaximumBatchingWindowInSeconds: 33, + EventSourceArn: { + "Fn::GetAtt": ["my_jobs_queue", "Arn"], + }, + FunctionName: { + "Fn::GetAtt": ["JobsWorkerLambdaFunction", "Arn"], + }, + Enabled: false, + FunctionResponseTypes: ["ReportBatchItemFailures"], + ScalingConfig: { + MaximumConcurrency: 1000, + }, + }, + }, + HttpApi: { + Type: "AWS::ApiGatewayV2::Api", + Properties: { + Name: "dev-aws-node-sqs-worker-project", + ProtocolType: "HTTP", + }, + }, + HttpApiStage: { + Type: "AWS::ApiGatewayV2::Stage", + Properties: { + ApiId: { + Ref: "HttpApi", + }, + StageName: "$default", + AutoDeploy: true, + DefaultRouteSettings: { + DetailedMetricsEnabled: false, + }, + }, + }, + ProducerLambdaPermissionHttpApi: { + Type: "AWS::Lambda::Permission", + Properties: { + FunctionName: { + "Fn::GetAtt": ["ProducerLambdaFunction", "Arn"], + }, + Action: "lambda:InvokeFunction", + Principal: "apigateway.amazonaws.com", + SourceArn: { + "Fn::Join": [ + "", + [ + "arn:", + { + Ref: "AWS::Partition", + }, + ":execute-api:", + { + Ref: "AWS::Region", + }, + ":", + { + Ref: "AWS::AccountId", + }, + ":", + { + Ref: "HttpApi", + }, + "/*", + ], + ], + }, + }, + }, + HttpApiIntegrationProducer: { + Type: "AWS::ApiGatewayV2::Integration", + Properties: { + ApiId: { + Ref: "HttpApi", + }, + IntegrationType: "AWS_PROXY", + IntegrationUri: { + "Fn::GetAtt": ["ProducerLambdaFunction", "Arn"], + }, + PayloadFormatVersion: "2.0", + TimeoutInMillis: 30000, + }, + }, + HttpApiRoutePostProduce: { + Type: "AWS::ApiGatewayV2::Route", + Properties: { + ApiId: { + Ref: "HttpApi", + }, + RouteKey: "POST /produce", + Target: { + "Fn::Join": [ + "/", + [ + "integrations", + { + Ref: "HttpApiIntegrationProducer", + }, + ], + ], + }, + }, + DependsOn: "HttpApiIntegrationProducer", + }, + my_jobs_dlq: { + Type: "AWS::SQS::Queue", + Properties: { + MessageRetentionPeriod: 1209600, + QueueName: "my_worker_queue-dlq", + }, + UpdateReplacePolicy: "Delete", + DeletionPolicy: "Delete", + }, + my_jobs_queue: { + Type: "AWS::SQS::Queue", + Properties: { + DelaySeconds: 60, + QueueName: "my_worker_queue", + RedrivePolicy: { + deadLetterTargetArn: { + "Fn::GetAtt": ["my_jobs_dlq", "Arn"], + }, + maxReceiveCount: 1, + }, + VisibilityTimeout: 1, + }, + UpdateReplacePolicy: "Delete", + DeletionPolicy: "Delete", + }, + }, + Outputs: { + ServerlessDeploymentBucketName: { + Value: { + Ref: "ServerlessDeploymentBucket", + }, + Export: { + Name: "sls-aws-node-sqs-worker-project-dev-ServerlessDeploymentBucketName", + }, + }, + ProducerLambdaFunctionQualifiedArn: { + Description: "Current Lambda function version", + Value: { + Ref: "ProducerLambdaVersionuTQ7t4amwQGQCm6r1nMXU48wnVVaGIpkfkz7ZdThFp0", + }, + Export: { + Name: "sls-aws-node-sqs-worker-project-dev-ProducerLambdaFunctionQualifiedArn", + }, + }, + JobsWorkerLambdaFunctionQualifiedArn: { + Description: "Current Lambda function version", + Value: { + Ref: "JobsWorkerLambdaVersionx1AYxWc2gFMOTQcCDa9Z0cA1wcVonrxuYZMubQpE", + }, + Export: { + Name: "sls-my_worker_functionLambdaFunctionQualifiedArn", + }, + }, + HttpApiId: { + Description: "Id of the HTTP API", + Value: { + Ref: "HttpApi", + }, + Export: { + Name: "sls-aws-node-sqs-worker-project-dev-HttpApiId", + }, + }, + HttpApiUrl: { + Description: "URL of the HTTP API", + Value: { + "Fn::Join": [ + "", + [ + "https://", + { + Ref: "HttpApi", + }, + ".execute-api.", + { + Ref: "AWS::Region", + }, + ".", + { + Ref: "AWS::URLSuffix", + }, + ], + ], + }, + Export: { + Name: "sls-aws-node-sqs-worker-project-dev-HttpApiUrl", + }, + }, + jobsQueueArnA5A2FF7E: { + Description: 'ARN of the "jobs" SQS queue.', + Value: { + "Fn::GetAtt": ["my_jobs_queue", "Arn"], + }, + }, + jobsQueueUrl573F5B7A: { + Description: 'URL of the "jobs" SQS queue.', + Value: { + Ref: "my_jobs_queue", + }, + }, + jobsDlqUrl2C7FA9D4: { + Description: 'URL of the "jobs" SQS dead letter queue.', + Value: { + Ref: "my_jobs_dlq", + }, + }, + }, +}; diff --git a/src/components/app-footer/index.tsx b/src/components/app-footer/index.tsx index 45ae064..94ac476 100644 --- a/src/components/app-footer/index.tsx +++ b/src/components/app-footer/index.tsx @@ -1,14 +1,7 @@ const AppFooter = () => ( ); diff --git a/src/components/app-navbar/index.tsx b/src/components/app-navbar/index.tsx index ee1fb1e..3610b4b 100644 --- a/src/components/app-navbar/index.tsx +++ b/src/components/app-navbar/index.tsx @@ -1,82 +1,13 @@ const AppNavbar = () => ( -
- - -
+
+
+
+ + + SQS Lambda Assistant + +
+
+
); export default AppNavbar; diff --git a/src/components/hero/index.tsx b/src/components/hero/index.tsx index 1f4fb53..8fcd34a 100644 --- a/src/components/hero/index.tsx +++ b/src/components/hero/index.tsx @@ -4,82 +4,23 @@ interface Props { } const Hero = ({ onOpenModal }: Props) => ( - <> -
-
- -
-
-
-
-

SQS Lambda Assistant

-

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eiusmod tempor incididunt ut labore et dolore magna aliqua. -

-

- -

-
-
-
-
- + + ); export default Hero; diff --git a/src/components/modals/template/index.tsx b/src/components/modals/template/index.tsx index 51593b5..63ddeba 100644 --- a/src/components/modals/template/index.tsx +++ b/src/components/modals/template/index.tsx @@ -7,6 +7,7 @@ export type EditorLanguage = "yaml" | "json"; interface Props { content?: string; language?: EditorLanguage; + error?: string | null; isOpen: boolean; onClose: () => void; onChangeLanguage: (language: EditorLanguage) => void; @@ -16,6 +17,7 @@ interface Props { const TemplateModal = ({ content = "", language = "json", + error = null, isOpen, onClose, onChangeContent, @@ -36,6 +38,7 @@ const TemplateModal = ({ +
{error}
} isOpen={isOpen} diff --git a/src/views/home/index.tsx b/src/views/home/index.tsx index ec7940b..d5cf5e3 100644 --- a/src/views/home/index.tsx +++ b/src/views/home/index.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { yamlParse } from "yaml-cfn"; import Hero from "../../components/hero"; import TemplateModal, { EditorLanguage, @@ -6,507 +7,61 @@ import TemplateModal, { import WorkersCollection from "../../components/workers/collection"; import { advisor, Worker } from "../../core"; import { filter } from "../../core/helpers/filter"; - -const cfTemplate = { - AWSTemplateFormatVersion: "2010-09-09", - Description: - "The AWS CloudFormation template for this Serverless application", - Resources: { - ServerlessDeploymentBucket: { - Type: "AWS::S3::Bucket", - Properties: { - BucketEncryption: { - ServerSideEncryptionConfiguration: [ - { - ServerSideEncryptionByDefault: { - SSEAlgorithm: "AES256", - }, - }, - ], - }, - }, - }, - ServerlessDeploymentBucketPolicy: { - Type: "AWS::S3::BucketPolicy", - Properties: { - Bucket: { - Ref: "ServerlessDeploymentBucket", - }, - PolicyDocument: { - Statement: [ - { - Action: "s3:*", - Effect: "Deny", - Principal: "*", - Resource: [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - Ref: "AWS::Partition", - }, - ":s3:::", - { - Ref: "ServerlessDeploymentBucket", - }, - "/*", - ], - ], - }, - { - "Fn::Join": [ - "", - [ - "arn:", - { - Ref: "AWS::Partition", - }, - ":s3:::", - { - Ref: "ServerlessDeploymentBucket", - }, - ], - ], - }, - ], - Condition: { - Bool: { - "aws:SecureTransport": false, - }, - }, - }, - ], - }, - }, - }, - ProducerLogGroup: { - Type: "AWS::Logs::LogGroup", - Properties: { - LogGroupName: "/aws/lambda/aws-node-sqs-worker-project-dev-producer", - }, - }, - JobsWorkerLogGroup: { - Type: "AWS::Logs::LogGroup", - Properties: { - LogGroupName: "/aws/lambda/aws-node-sqs-worker-project-dev-jobsWorker", - }, - }, - IamRoleLambdaExecution: { - Type: "AWS::IAM::Role", - Properties: { - AssumeRolePolicyDocument: { - Version: "2012-10-17", - Statement: [ - { - Effect: "Allow", - Principal: { - Service: ["lambda.amazonaws.com"], - }, - Action: ["sts:AssumeRole"], - }, - ], - }, - Policies: [ - { - PolicyName: { - "Fn::Join": [ - "-", - ["aws-node-sqs-worker-project", "dev", "lambda"], - ], - }, - PolicyDocument: { - Version: "2012-10-17", - Statement: [ - { - Effect: "Allow", - Action: [ - "logs:CreateLogStream", - "logs:CreateLogGroup", - "logs:TagResource", - ], - Resource: [ - { - "Fn::Sub": - "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-node-sqs-worker-project-dev*:*", - }, - ], - }, - { - Effect: "Allow", - Action: ["logs:PutLogEvents"], - Resource: [ - { - "Fn::Sub": - "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/aws-node-sqs-worker-project-dev*:*:*", - }, - ], - }, - { - Effect: "Allow", - Action: ["sqs:SendMessage", "sqs:ChangeMessageVisibility"], - Resource: [ - { - "Fn::GetAtt": ["jobsQueueCEDBAE3E", "Arn"], - }, - ], - }, - { - Effect: "Allow", - Action: [ - "sqs:ReceiveMessage", - "sqs:DeleteMessage", - "sqs:GetQueueAttributes", - ], - Resource: [ - { - "Fn::GetAtt": ["jobsQueueCEDBAE3E", "Arn"], - }, - ], - }, - ], - }, - }, - ], - Path: "/", - RoleName: { - "Fn::Join": [ - "-", - [ - "aws-node-sqs-worker-project", - "dev", - { - Ref: "AWS::Region", - }, - "lambdaRole", - ], - ], - }, - }, - }, - ProducerLambdaFunction: { - Type: "AWS::Lambda::Function", - Properties: { - Code: { - S3Bucket: { - Ref: "ServerlessDeploymentBucket", - }, - S3Key: - "serverless/aws-node-sqs-worker-project/dev/1705833879496-2024-01-21T10:44:39.496Z/aws-node-sqs-worker-project.zip", - }, - Handler: "index.producer", - Runtime: "nodejs18.x", - FunctionName: "aws-node-sqs-worker-project-dev-producer", - MemorySize: 1024, - Timeout: 6, - Environment: { - Variables: { - QUEUE_URL: { - Ref: "jobsQueueCEDBAE3E", - }, - }, - }, - Role: { - "Fn::GetAtt": ["IamRoleLambdaExecution", "Arn"], - }, - }, - DependsOn: ["ProducerLogGroup"], - }, - JobsWorkerLambdaFunction: { - Type: "AWS::Lambda::Function", - Properties: { - Code: { - S3Bucket: { - Ref: "ServerlessDeploymentBucket", - }, - S3Key: - "serverless/aws-node-sqs-worker-project/dev/1705833879496-2024-01-21T10:44:39.496Z/aws-node-sqs-worker-project.zip", - }, - Handler: "index.consumer", - Runtime: "nodejs18.x", - FunctionName: "aws-node-sqs-worker-project-dev-jobsWorker", - MemorySize: 1024, - Timeout: 6, - Role: { - "Fn::GetAtt": ["IamRoleLambdaExecution", "Arn"], - }, - ReservedConcurrentExecutions: 200, - }, - DependsOn: ["JobsWorkerLogGroup"], - }, - ProducerLambdaVersionuTQ7t4amwQGQCm6r1nMXU48wnVVaGIpkfkz7ZdThFp0: { - Type: "AWS::Lambda::Version", - DeletionPolicy: "Retain", - Properties: { - FunctionName: { - Ref: "ProducerLambdaFunction", - }, - CodeSha256: "AnrSvCAIoAKh7dIyDh8TyJ5Jbrqhyd8KoY1zzxb7XCM=", - }, - }, - JobsWorkerLambdaVersionx1AYxWc2gFMOTQcCDa9Z0cA1wcVonrxuYZMubQpE: { - Type: "AWS::Lambda::Version", - DeletionPolicy: "Retain", - Properties: { - FunctionName: { - Ref: "JobsWorkerLambdaFunction", - }, - CodeSha256: "AnrSvCAIoAKh7dIyDh8TyJ5Jbrqhyd8KoY1zzxb7XCM=", - }, - }, - JobsWorkerEventSourceMappingSQSJobsQueueCEDBAE3E: { - Type: "AWS::Lambda::EventSourceMapping", - DependsOn: ["IamRoleLambdaExecution"], - Properties: { - BatchSize: 5, - MaximumBatchingWindowInSeconds: 33, - EventSourceArn: { - "Fn::GetAtt": ["jobsQueueCEDBAE3E", "Arn"], - }, - FunctionName: { - "Fn::GetAtt": ["JobsWorkerLambdaFunction", "Arn"], - }, - Enabled: false, - FunctionResponseTypes: ["ReportBatchItemFailures"], - ScalingConfig: { - MaximumConcurrency: 1000, - }, - }, - }, - HttpApi: { - Type: "AWS::ApiGatewayV2::Api", - Properties: { - Name: "dev-aws-node-sqs-worker-project", - ProtocolType: "HTTP", - }, - }, - HttpApiStage: { - Type: "AWS::ApiGatewayV2::Stage", - Properties: { - ApiId: { - Ref: "HttpApi", - }, - StageName: "$default", - AutoDeploy: true, - DefaultRouteSettings: { - DetailedMetricsEnabled: false, - }, - }, - }, - ProducerLambdaPermissionHttpApi: { - Type: "AWS::Lambda::Permission", - Properties: { - FunctionName: { - "Fn::GetAtt": ["ProducerLambdaFunction", "Arn"], - }, - Action: "lambda:InvokeFunction", - Principal: "apigateway.amazonaws.com", - SourceArn: { - "Fn::Join": [ - "", - [ - "arn:", - { - Ref: "AWS::Partition", - }, - ":execute-api:", - { - Ref: "AWS::Region", - }, - ":", - { - Ref: "AWS::AccountId", - }, - ":", - { - Ref: "HttpApi", - }, - "/*", - ], - ], - }, - }, - }, - HttpApiIntegrationProducer: { - Type: "AWS::ApiGatewayV2::Integration", - Properties: { - ApiId: { - Ref: "HttpApi", - }, - IntegrationType: "AWS_PROXY", - IntegrationUri: { - "Fn::GetAtt": ["ProducerLambdaFunction", "Arn"], - }, - PayloadFormatVersion: "2.0", - TimeoutInMillis: 30000, - }, - }, - HttpApiRoutePostProduce: { - Type: "AWS::ApiGatewayV2::Route", - Properties: { - ApiId: { - Ref: "HttpApi", - }, - RouteKey: "POST /produce", - Target: { - "Fn::Join": [ - "/", - [ - "integrations", - { - Ref: "HttpApiIntegrationProducer", - }, - ], - ], - }, - }, - DependsOn: "HttpApiIntegrationProducer", - }, - jobsDlqD18CF374: { - Type: "AWS::SQS::Queue", - Properties: { - MessageRetentionPeriod: 1209600, - QueueName: "aws-node-sqs-worker-project-dev-jobs-dlq", - }, - UpdateReplacePolicy: "Delete", - DeletionPolicy: "Delete", - }, - jobsQueueCEDBAE3E: { - Type: "AWS::SQS::Queue", - Properties: { - DelaySeconds: 60, - QueueName: "aws-node-sqs-worker-project-dev-jobs", - RedrivePolicy: { - deadLetterTargetArn: { - "Fn::GetAtt": ["jobsDlqD18CF374", "Arn"], - }, - maxReceiveCount: 1, - }, - VisibilityTimeout: 1, - }, - UpdateReplacePolicy: "Delete", - DeletionPolicy: "Delete", - }, - }, - Outputs: { - ServerlessDeploymentBucketName: { - Value: { - Ref: "ServerlessDeploymentBucket", - }, - Export: { - Name: "sls-aws-node-sqs-worker-project-dev-ServerlessDeploymentBucketName", - }, - }, - ProducerLambdaFunctionQualifiedArn: { - Description: "Current Lambda function version", - Value: { - Ref: "ProducerLambdaVersionuTQ7t4amwQGQCm6r1nMXU48wnVVaGIpkfkz7ZdThFp0", - }, - Export: { - Name: "sls-aws-node-sqs-worker-project-dev-ProducerLambdaFunctionQualifiedArn", - }, - }, - JobsWorkerLambdaFunctionQualifiedArn: { - Description: "Current Lambda function version", - Value: { - Ref: "JobsWorkerLambdaVersionx1AYxWc2gFMOTQcCDa9Z0cA1wcVonrxuYZMubQpE", - }, - Export: { - Name: "sls-aws-node-sqs-worker-project-dev-JobsWorkerLambdaFunctionQualifiedArn", - }, - }, - HttpApiId: { - Description: "Id of the HTTP API", - Value: { - Ref: "HttpApi", - }, - Export: { - Name: "sls-aws-node-sqs-worker-project-dev-HttpApiId", - }, - }, - HttpApiUrl: { - Description: "URL of the HTTP API", - Value: { - "Fn::Join": [ - "", - [ - "https://", - { - Ref: "HttpApi", - }, - ".execute-api.", - { - Ref: "AWS::Region", - }, - ".", - { - Ref: "AWS::URLSuffix", - }, - ], - ], - }, - Export: { - Name: "sls-aws-node-sqs-worker-project-dev-HttpApiUrl", - }, - }, - jobsQueueArnA5A2FF7E: { - Description: 'ARN of the "jobs" SQS queue.', - Value: { - "Fn::GetAtt": ["jobsQueueCEDBAE3E", "Arn"], - }, - }, - jobsQueueUrl573F5B7A: { - Description: 'URL of the "jobs" SQS queue.', - Value: { - Ref: "jobsQueueCEDBAE3E", - }, - }, - jobsDlqUrl2C7FA9D4: { - Description: 'URL of the "jobs" SQS dead letter queue.', - Value: { - Ref: "jobsDlqD18CF374", - }, - }, - }, -}; +import { TEMPLATE_JSON } from "../../_mocks/template_json"; const Home = () => { const [items, setItems] = useState< { worker: Worker; suggestions: string[] }[] >([]); const analyze = (template: string) => { - //@ts-ignore - const result = filter(JSON.parse(template)); - setItems( - result.map((item) => { - const worker = new Worker({ - id: JSON.stringify(item.integration), - //@ts-ignore - integration: item.integration, - //@ts-ignore - lambda: item.lambda, - //@ts-ignore - sqs: item.sqs, - }); - const suggestions = advisor.analyze(worker); - return { - suggestions, - worker, - }; - }), - ); + try { + //@ts-ignore + const result = filter(template); + setItems( + result.map((item) => { + const worker = new Worker({ + id: JSON.stringify(item.integration), + //@ts-ignore + integration: item.integration, + //@ts-ignore + lambda: item.lambda, + //@ts-ignore + sqs: item.sqs, + }); + const suggestions = advisor.analyze(worker); + return { + suggestions, + worker, + }; + }), + ); + setError(null); + setIsOpenModal(false); + } catch (error) { + console.info(error); + setError( + "Could not perform analysis. Please check your template and try again.", + ); + } }; const handleOnImportTemplate = () => { - analyze(content); - setIsOpenModal(false); + try { + if (editorLanguage === "yaml") { + analyze(yamlParse(content)); + } else { + analyze(JSON.parse(content)); + } + } catch (error) { + console.info(error); + setError("Invalid template"); + } }; const [editorLanguage, setEditorLanguage] = useState("json"); const handleOnChangeLanguage = (language: EditorLanguage) => { setEditorLanguage(language); }; const [content, setContent] = useState( - JSON.stringify(cfTemplate, null, 2).toString(), + JSON.stringify(TEMPLATE_JSON, null, 2).toString(), ); const handleOnChangeContent = (c: string) => { setContent(c); @@ -518,7 +73,7 @@ const Home = () => { const handleOnCloseModal = () => { setIsOpenModal(false); }; - // analyze(JSON.stringify(cfTemplate)); + const [error, setError] = useState(null); return ( <>
@@ -537,6 +92,7 @@ const Home = () => {