diff --git a/assets/example.png b/assets/example.png index 82fc0bb..0b28ad7 100644 Binary files a/assets/example.png and b/assets/example.png differ diff --git a/package-lock.json b/package-lock.json index 8e60f49..f37238e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@semantic-release/release-notes-generator": "^12.1.0", "commander": "^11.1.0", "js-yaml": "^4.1.0", + "js-yaml-cloudformation-schema": "^1.0.0", "playwright": "^1.40.1" }, "bin": { @@ -7466,6 +7467,34 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/js-yaml-cloudformation-schema": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/js-yaml-cloudformation-schema/-/js-yaml-cloudformation-schema-1.0.0.tgz", + "integrity": "sha512-eokVVPLsjLFuuCRQWIIaE5fX7qPUNRAAmJFXSvtzUnJcdNS0ZtAPdaFcwCrTHM+owGcBR82rlpd0b6bu8pFwQA==", + "dependencies": { + "js-yaml": "^3.7.0" + } + }, + "node_modules/js-yaml-cloudformation-schema/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/js-yaml-cloudformation-schema/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -13839,8 +13868,7 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/ssim.js": { "version": "3.5.0", diff --git a/package.json b/package.json index 9a0facf..653e531 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@semantic-release/release-notes-generator": "^12.1.0", "commander": "^11.1.0", "js-yaml": "^4.1.0", + "js-yaml-cloudformation-schema": "^1.0.0", "playwright": "^1.40.1" }, "devDependencies": { diff --git a/src/reader.ts b/src/reader.ts index c0a9b76..71a3758 100644 --- a/src/reader.ts +++ b/src/reader.ts @@ -1,5 +1,6 @@ import * as fs from 'node:fs'; -import yaml from 'js-yaml'; +import * as yaml from 'js-yaml'; +import {CLOUDFORMATION_SCHEMA} from 'js-yaml-cloudformation-schema'; const acceptedExtensions = ['.json', '.yaml', '.yml']; @@ -42,7 +43,7 @@ export const readTemplate = async (path: string) => { case '.yaml': case '.yml': try { - yaml.load(fileContent); + yaml.load(fileContent, {schema: CLOUDFORMATION_SCHEMA}); } catch (e) { throw new Error(`Invalid YAML file: ${e}`); } diff --git a/test/__image_snapshots__/renderer-test-ts-renderer-should-produce-the-same-output-image-1-snap.png b/test/__image_snapshots__/renderer-test-ts-renderer-should-produce-the-same-output-image-for-a-given-json-template-1-snap.png similarity index 100% rename from test/__image_snapshots__/renderer-test-ts-renderer-should-produce-the-same-output-image-1-snap.png rename to test/__image_snapshots__/renderer-test-ts-renderer-should-produce-the-same-output-image-for-a-given-json-template-1-snap.png diff --git a/test/__image_snapshots__/renderer-test-ts-renderer-should-produce-the-same-output-image-for-a-given-yaml-template-1-snap.png b/test/__image_snapshots__/renderer-test-ts-renderer-should-produce-the-same-output-image-for-a-given-yaml-template-1-snap.png new file mode 100644 index 0000000..abf7105 Binary files /dev/null and b/test/__image_snapshots__/renderer-test-ts-renderer-should-produce-the-same-output-image-for-a-given-yaml-template-1-snap.png differ diff --git a/test/fixtures/template.yaml b/test/fixtures/template.yaml new file mode 100644 index 0000000..37d674d --- /dev/null +++ b/test/fixtures/template.yaml @@ -0,0 +1,197 @@ +Transform: AWS::Serverless-2016-10-31 +Resources: + Api: + Type: AWS::Serverless::Api + Properties: + Name: !Sub + - ${ResourceName} From Stack ${AWS::StackName} + - ResourceName: Api + StageName: Prod + DefinitionBody: + openapi: '3.0' + info: {} + paths: + /items: + get: + x-amazon-apigateway-integration: + httpMethod: POST + type: aws_proxy + uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ListItems.Arn}/invocations + responses: {} + post: + x-amazon-apigateway-integration: + httpMethod: POST + type: aws_proxy + uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${CreateItem.Arn}/invocations + responses: {} + /items/{id}: + get: + x-amazon-apigateway-integration: + httpMethod: POST + type: aws_proxy + uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetItem.Arn}/invocations + responses: {} + put: + x-amazon-apigateway-integration: + httpMethod: POST + type: aws_proxy + uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${UpdateItem.Arn}/invocations + responses: {} + delete: + x-amazon-apigateway-integration: + httpMethod: POST + type: aws_proxy + uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DeleteItem.Arn}/invocations + responses: {} + EndpointConfiguration: REGIONAL + TracingEnabled: true + Items: + Type: AWS::DynamoDB::Table + Properties: + AttributeDefinitions: + - AttributeName: id + AttributeType: S + BillingMode: PAY_PER_REQUEST + KeySchema: + - AttributeName: id + KeyType: HASH + ListItems: + Type: AWS::Serverless::Function + Properties: + FunctionName: !Sub ${AWS::StackName}-ListItems + Description: !Sub + - Stack ${AWS::StackName} Function ${ResourceName} + - ResourceName: ListItems + CodeUri: src/ListItems + Handler: index.handler + Runtime: nodejs18.x + MemorySize: 1024 + Timeout: 30 + Tracing: Active + Events: + ApiGETitems: + Type: Api + Properties: + Path: /items + Method: GET + RestApiId: !Ref Api + Environment: + Variables: + ITEMS_TABLE_NAME: !Ref Items + Policies: + - DynamoDBReadPolicy: + TableName: !Ref Items + CreateItem: + Type: AWS::Serverless::Function + Properties: + FunctionName: !Sub ${AWS::StackName}-CreateItem + Description: !Sub + - Stack ${AWS::StackName} Function ${ResourceName} + - ResourceName: CreateItem + CodeUri: src/CreateItem + Handler: index.handler + Runtime: nodejs18.x + MemorySize: 1024 + Timeout: 30 + Tracing: Active + Events: + ApiPOSTitems: + Type: Api + Properties: + Path: /items + Method: POST + RestApiId: !Ref Api + Environment: + Variables: + ITEMS_TABLE_NAME: !Ref Items + Policies: + - DynamoDBWritePolicy: + TableName: !Ref Items + GetItem: + Type: AWS::Serverless::Function + Properties: + FunctionName: !Sub ${AWS::StackName}-GetItem + Description: !Sub + - Stack ${AWS::StackName} Function ${ResourceName} + - ResourceName: GetItem + CodeUri: src/GetItem + Handler: index.handler + Runtime: nodejs18.x + MemorySize: 1024 + Timeout: 30 + Tracing: Active + Events: + ApiGETitemsid: + Type: Api + Properties: + Path: /items/{id} + Method: GET + RestApiId: !Ref Api + Environment: + Variables: + ITEMS_TABLE_NAME: !Ref Items + Policies: + - DynamoDBReadPolicy: + TableName: !Ref Items + UpdateItem: + Type: AWS::Serverless::Function + Properties: + FunctionName: !Sub ${AWS::StackName}-UpdateItem + Description: !Sub + - Stack ${AWS::StackName} Function ${ResourceName} + - ResourceName: UpdateItem + CodeUri: src/UpdateItem + Handler: index.handler + Runtime: nodejs18.x + MemorySize: 1024 + Timeout: 30 + Tracing: Active + Events: + ApiPUTitemsid: + Type: Api + Properties: + Path: /items/{id} + Method: PUT + RestApiId: !Ref Api + Environment: + Variables: + ITEMS_TABLE_NAME: !Ref Items + Policies: + - DynamoDBWritePolicy: + TableName: !Ref Items + DeleteItem: + Type: AWS::Serverless::Function + Properties: + FunctionName: !Sub ${AWS::StackName}-DeleteItem + Description: !Sub + - Stack ${AWS::StackName} Function ${ResourceName} + - ResourceName: DeleteItem + CodeUri: src/DeleteItem + Handler: index.handler + Runtime: nodejs18.x + MemorySize: 1024 + Timeout: 30 + Tracing: Active + Events: + ApiDELETEitemsid: + Type: Api + Properties: + Path: /items/{id} + Method: DELETE + RestApiId: !Ref Api + Environment: + Variables: + ITEMS_TABLE_NAME: !Ref Items + Policies: + - DynamoDBCrudPolicy: + TableName: !Ref Items +Metadata: + AWS::Composer::Groups: + Group: + Label: API Compute + Members: + - ListItems + - CreateItem + - GetItem + - UpdateItem + - DeleteItem \ No newline at end of file diff --git a/test/renderer.test.ts b/test/renderer.test.ts index 114ba1c..a2d854d 100644 --- a/test/renderer.test.ts +++ b/test/renderer.test.ts @@ -6,13 +6,23 @@ import {toMatchImageSnapshot} from 'jest-image-snapshot'; expect.extend({toMatchImageSnapshot}); describe('Renderer', () => { - const inputPath = path.resolve(__dirname, './fixtures', 'template.json'); - const imagePath = path.resolve(__dirname, './.out', 'template.png'); + const inputPathJson = path.resolve(__dirname, './fixtures', 'template.json'); + const inputPathYaml = path.resolve(__dirname, './fixtures', 'template.yaml'); + const imagePath = path.resolve(__dirname, './../.out', 'template.png'); // Regression - it('should produce the same output image', async () => { + it('should produce the same output image for a given json template', async () => { await renderTemplate({ - inputPath: inputPath, + inputPath: inputPathJson, + outputPath: imagePath, + }); + + const image = fs.readFileSync(imagePath); + expect(image).toMatchImageSnapshot(); + }); + it('should produce the same output image for a given yaml template', async () => { + await renderTemplate({ + inputPath: inputPathYaml, outputPath: imagePath, });