From 28299ff21df96ce0af70b6f1a207c55fe524b039 Mon Sep 17 00:00:00 2001 From: Jarrad Whitaker Date: Tue, 14 Nov 2017 09:07:29 +1100 Subject: [PATCH] Refactor JavaScript to TypeScript (#72) Addresses issue #70 * typescript infrastructure * es6 -> ts * Convert to Typescript * Move tests into src --- .babelrc | 3 - gulpfile.js | 24 --- package.json | 24 +-- src/awsData.ts | 123 ++++++++++++++ src/{docs.es6 => docs.ts} | 15 +- src/fixes.d.ts | 7 + src/{index.es6 => index.ts} | 19 ++- src/{logger.es6 => logger.ts} | 4 +- src/{parser.es6 => parser.ts} | 15 +- src/{resourcesSpec.es6 => resourcesSpec.ts} | 91 +++++----- test/docsTest.js => src/test/docsTest.ts | 6 +- test/indexTest.js => src/test/indexTest.ts | 22 +-- test/parserTest.js => src/test/parserTest.ts | 12 +- .../test/resourcesTest.ts | 4 +- .../test/validatorTest.ts | 116 ++++++------- src/{validator.es6 => validator.ts} | 160 +++++++++--------- .../1_intrinsic_function_in_parameters.json | 0 .../json/1_invalid_get_azs_parameter.json | 0 .../json/1_invalid_intrinsic_get_att.json | 0 .../json/1_invalid_intrinsic_mappings.json | 0 .../json/1_invalid_parameter_reference.json | 0 .../json/1_invalid_parameter_type.json | 0 .../json/1_invalid_resource_reference.json | 0 .../invalid/json/1_invalid_resource_type.json | 0 .../json/1_missing_parameter_type.json | 0 .../invalid/json/1_ref_not_string_param.json | 0 .../json/1_warning_ref_get_azs_parameter.json | 0 .../json/2_invalid_intrinsic_join.json | 0 .../invalid/json/empty_resources.json | 0 .../invalid/json/invalid_json.json | 0 .../invalid/json/no_resources.json | 0 .../invalid/yaml/invalid_arn.yaml | 0 .../invalid/yaml/invalid_boolean_type.yaml | 0 .../invalid/yaml/invalid_condition_value.yaml | 0 .../yaml/invalid_condition_value_type.yaml | 0 .../yaml/invalid_ec2_tags_property_name.yaml | 0 .../yaml/invalid_if_statement_arguments.yaml | 0 .../yaml/invalid_if_statement_condition.yaml | 0 ...valid_import_value_intrinsic_function.yaml | 0 .../invalid/yaml/invalid_maps.yaml | 0 .../yaml/invalid_missing_nested_property.yaml | 0 .../yaml/invalid_property_type_string.yaml | 0 .../invalid_propertytype_property_name.yaml | 0 ...id_required_propertytype_prop_missing.yaml | 0 ...id_required_resourcetype_prop_missing.yaml | 0 .../invalid_resourcetype_property_name.yaml | 0 .../invalid/yaml/invalid_sub_getatt.yaml | 0 .../invalid/yaml/invalid_sub_reference.yaml | 0 .../yaml/invalid_template_format_version.yaml | 0 .../invalid/yaml/invalid_yaml.yaml | 0 .../issue-39-output-reference-invalid.yaml | 0 {test/data => testData}/valid/json/1.json | 0 {test/data => testData}/valid/json/2.json | 0 {test/data => testData}/valid/json/3.json | 0 {test/data => testData}/valid/json/4.json | 0 {test/data => testData}/valid/yaml/1.yaml | 0 {test/data => testData}/valid/yaml/2.yaml | 0 .../valid/yaml/issue-24.yaml | 0 .../yaml/issue-27-numeric-properties.yaml | 0 .../valid/yaml/issue-28-custom-resource.yaml | 0 .../yaml/issue-39-output-reference-check.yaml | 0 .../yaml/issue-42-managed-policy-name.yaml | 0 .../yaml/issue-44-database-endpoint.yaml | 0 .../valid/yaml/issue-61.yaml | 0 {test/data => testData}/valid/yaml/maps.yaml | 0 .../valid/yaml/parameters-allowed-values.yaml | 0 .../valid/yaml/pseudo-parameters.yaml | 0 .../valid/yaml/pseudo-w-parameter.yaml | 0 .../valid/yaml/valid_import_value.yaml | 0 .../valid/yaml/valid_minus_one_as_string.yaml | 0 ...valid_missing_template_format_version.yaml | 0 .../valid/yaml/valid_shorthand_sub.yaml | 0 .../valid/yaml/valid_sub_operations.yaml | 0 .../yaml/valid_unquoted_template_version.yaml | 0 tsconfig.json | 53 ++++++ 75 files changed, 428 insertions(+), 270 deletions(-) delete mode 100644 .babelrc delete mode 100644 gulpfile.js create mode 100644 src/awsData.ts rename src/{docs.es6 => docs.ts} (86%) create mode 100644 src/fixes.d.ts rename src/{index.es6 => index.ts} (85%) rename src/{logger.es6 => logger.ts} (83%) rename src/{parser.es6 => parser.ts} (90%) rename src/{resourcesSpec.es6 => resourcesSpec.ts} (64%) rename test/docsTest.js => src/test/docsTest.ts (93%) rename test/indexTest.js => src/test/indexTest.ts (64%) rename test/parserTest.js => src/test/parserTest.ts (76%) rename test/resourcesTest.js => src/test/resourcesTest.ts (98%) rename test/validatorTest.js => src/test/validatorTest.ts (84%) rename src/{validator.es6 => validator.ts} (90%) rename {test/data => testData}/invalid/json/1_intrinsic_function_in_parameters.json (100%) rename {test/data => testData}/invalid/json/1_invalid_get_azs_parameter.json (100%) rename {test/data => testData}/invalid/json/1_invalid_intrinsic_get_att.json (100%) rename {test/data => testData}/invalid/json/1_invalid_intrinsic_mappings.json (100%) rename {test/data => testData}/invalid/json/1_invalid_parameter_reference.json (100%) rename {test/data => testData}/invalid/json/1_invalid_parameter_type.json (100%) rename {test/data => testData}/invalid/json/1_invalid_resource_reference.json (100%) rename {test/data => testData}/invalid/json/1_invalid_resource_type.json (100%) rename {test/data => testData}/invalid/json/1_missing_parameter_type.json (100%) rename {test/data => testData}/invalid/json/1_ref_not_string_param.json (100%) rename {test/data => testData}/invalid/json/1_warning_ref_get_azs_parameter.json (100%) rename {test/data => testData}/invalid/json/2_invalid_intrinsic_join.json (100%) rename {test/data => testData}/invalid/json/empty_resources.json (100%) rename {test/data => testData}/invalid/json/invalid_json.json (100%) rename {test/data => testData}/invalid/json/no_resources.json (100%) rename {test/data => testData}/invalid/yaml/invalid_arn.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_boolean_type.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_condition_value.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_condition_value_type.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_ec2_tags_property_name.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_if_statement_arguments.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_if_statement_condition.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_import_value_intrinsic_function.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_maps.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_missing_nested_property.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_property_type_string.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_propertytype_property_name.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_required_propertytype_prop_missing.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_required_resourcetype_prop_missing.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_resourcetype_property_name.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_sub_getatt.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_sub_reference.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_template_format_version.yaml (100%) rename {test/data => testData}/invalid/yaml/invalid_yaml.yaml (100%) rename {test/data => testData}/invalid/yaml/issue-39-output-reference-invalid.yaml (100%) rename {test/data => testData}/valid/json/1.json (100%) rename {test/data => testData}/valid/json/2.json (100%) rename {test/data => testData}/valid/json/3.json (100%) rename {test/data => testData}/valid/json/4.json (100%) rename {test/data => testData}/valid/yaml/1.yaml (100%) rename {test/data => testData}/valid/yaml/2.yaml (100%) rename {test/data => testData}/valid/yaml/issue-24.yaml (100%) rename {test/data => testData}/valid/yaml/issue-27-numeric-properties.yaml (100%) rename {test/data => testData}/valid/yaml/issue-28-custom-resource.yaml (100%) rename {test/data => testData}/valid/yaml/issue-39-output-reference-check.yaml (100%) rename {test/data => testData}/valid/yaml/issue-42-managed-policy-name.yaml (100%) rename {test/data => testData}/valid/yaml/issue-44-database-endpoint.yaml (100%) rename {test/data => testData}/valid/yaml/issue-61.yaml (100%) rename {test/data => testData}/valid/yaml/maps.yaml (100%) rename {test/data => testData}/valid/yaml/parameters-allowed-values.yaml (100%) rename {test/data => testData}/valid/yaml/pseudo-parameters.yaml (100%) rename {test/data => testData}/valid/yaml/pseudo-w-parameter.yaml (100%) rename {test/data => testData}/valid/yaml/valid_import_value.yaml (100%) rename {test/data => testData}/valid/yaml/valid_minus_one_as_string.yaml (100%) rename {test/data => testData}/valid/yaml/valid_missing_template_format_version.yaml (100%) rename {test/data => testData}/valid/yaml/valid_shorthand_sub.yaml (100%) rename {test/data => testData}/valid/yaml/valid_sub_operations.yaml (100%) rename {test/data => testData}/valid/yaml/valid_unquoted_template_version.yaml (100%) create mode 100644 tsconfig.json diff --git a/.babelrc b/.babelrc deleted file mode 100644 index af0f0c3..0000000 --- a/.babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "presets": ["es2015"] -} \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 946f273..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,24 +0,0 @@ -var gulp = require('gulp'); -var sourcemaps = require('gulp-sourcemaps'); -var babel = require('gulp-babel'); - -var path = require('path'); - -var paths = { - es6: ['src/**/*.es6'], - es5: 'lib', - // Must be absolute or relative to source map - sourceRoot: path.join(__dirname, 'src'), -}; -gulp.task('babel', function () { // (A) - return gulp.src(paths.es6) - .pipe(sourcemaps.init()) // (B) - .pipe(babel()) - .pipe(sourcemaps.write('.', // (C) - { sourceRoot: paths.sourceRoot })) - .pipe(gulp.dest(paths.es5)); -}); -gulp.task('watch', function() { // (D) - gulp.watch(paths.es6, ['babel']); -}); -gulp.task('default', ['watch']); // (E) \ No newline at end of file diff --git a/package.json b/package.json index 9ef1dbb..431bfe4 100644 --- a/package.json +++ b/package.json @@ -17,25 +17,27 @@ "cloudformation-js-yaml-schema": "0.3.0", "colors": "latest", "commander": "latest", - "fs": "0.0.2", "js-yaml": "^3.7.0", "opn": "latest", "winston": "latest" }, "devDependencies": { - "gulp": "latest", - "babel-polyfill": "latest", - "gulp-babel": "latest", - "gulp-sourcemaps": "latest", - "babel-preset-es2015": "latest", - "babel-register": "latest", - "babel-cli": "latest", + "@types/chai": "^4.0.4", + "@types/colors": "^1.1.3", + "@types/commander": "^2.11.0", + "@types/js-yaml": "^3.9.1", + "@types/mocha": "^2.2.44", + "@types/node": "^8.0.50", + "@types/opn": "^3.0.28", + "@types/winston": "^2.3.7", + "chai": "latest", "mocha": "latest", - "chai": "latest" + "typescript": "^2.6.1" }, "scripts": { - "build": "babel src --presets babel-preset-es2015 --out-dir lib", - "test": "mocha --compilers js:babel-register --require babel-polyfill", + "build": "tsc", + "test": "mocha lib/test", + "watch": "tsc -w", "prepublish": "npm run build", "deploy": "npm publish" }, diff --git a/src/awsData.ts b/src/awsData.ts new file mode 100644 index 0000000..706c931 --- /dev/null +++ b/src/awsData.ts @@ -0,0 +1,123 @@ +type AWSExtraDocs = {[k: string]: string} + +export const awsExtraDocs = require('../data/aws_extra_docs.json') as AWSExtraDocs; + +export type AWSPrimitiveType = 'String' | 'Integer' | 'Boolean' | 'Json'; + +interface PropertyBase { + Required: boolean, + Documentation: string, + UpdateType: 'Mutable' | 'Immutable' | 'Conditional', +} + +interface PrimitiveProperty extends PropertyBase { + PrimitiveType: AWSPrimitiveType, + + Type: undefined + ItemType: undefined + PrimitiveItemType: undefined +} + +interface ComplexProperty extends PropertyBase { + Type: string, + + PrimitiveType: undefined + ItemType: undefined + PrimitiveItemType: undefined +} + +interface ListPropertyBase extends PropertyBase { + Type: 'List', + DuplicatesAllowed: boolean, + + PrimitiveType: undefined +} + +interface PrimitiveItemType { + PrimitiveItemType: AWSPrimitiveType, + + ItemType: undefined +} + +interface ItemType { + ItemType: string, + + PrimitiveItemType: undefined +} + +type ListProperty = ListPropertyBase & (PrimitiveItemType | ItemType) + +interface MapPropertyBase extends PropertyBase { + Type: 'Map', + DuplicatesAllowed: boolean, + + PrimitiveType: undefined + ItemType: undefined +} + +type MapProperty = MapPropertyBase & (PrimitiveItemType | ItemType) + +export type Property = PrimitiveProperty | ComplexProperty | ListProperty | MapProperty + +export interface ResourcePropertyType { + Documentation: string, + Properties: {[propertyName: string]: Property} +} + +export interface ResourceType { + Documentation: string, + Properties: {[propertyName: string]: Property}, + Attributes?: {[attributeName: string]: Attribute}, + AdditionalProperties?: boolean +} + +interface PrimitiveAttribute { + ItemType: AWSPrimitiveType +} + +interface ListAttribute { + Type: 'List', + PrimitiveItemType: AWSPrimitiveType +} + +type Attribute = PrimitiveAttribute | ListAttribute; + +interface AWSResourcesSpecification { + PropertyTypes: {[propertyName: string]: ResourcePropertyType} + ResourceTypes: {[resourceName: string]: ResourceType} +} + +export const awsResources = require('../data/aws_resources_specification.json') as AWSResourcesSpecification; + +type ResourceRefTypes = {[resourceName: string]: string}; + +export const awsResourceRefTypes = require('../data/aws_resource_ref_types.json') as ResourceRefTypes; + +type ParameterTypes = {[parameterName: string]: 'string' | 'array'}; + +export const awsParameterTypes = require('../data/aws_parameter_types.json') as ParameterTypes; + +type IntrinsicFunctions = { + [functionName: string]: { + supportedFunctions: string[] + } +} + +export const awsIntrinsicFunctions = require('../data/aws_intrinsic_functions.json') as IntrinsicFunctions; + +// avoid "does this exist" checks everywhere +for (let functionName in awsIntrinsicFunctions) { + awsIntrinsicFunctions[functionName].supportedFunctions = awsIntrinsicFunctions[functionName].supportedFunctions || []; +} + +type RefOverrides = { + "AWS::AccountId": string, + "AWS::NotificationARNs": string[], + "AWS::NoValue": "", + "AWS::Region": string, + "AWS::StackId": string, + "AWS::StackName": string, + [refOverride: string]: any +}; + +export const awsRefOverrides = require('../data/aws_ref_override.json') as RefOverrides; \ No newline at end of file diff --git a/src/docs.es6 b/src/docs.ts similarity index 86% rename from src/docs.es6 rename to src/docs.ts index fd1955f..798c18e 100644 --- a/src/docs.es6 +++ b/src/docs.ts @@ -1,9 +1,8 @@ -let awsResources = require('../data/aws_resources_specification.json'); -let awsExtraDocs = require('../data/aws_extra_docs.json'); -let opn = require('opn'); +import {awsResources, awsExtraDocs} from './awsData'; +import opn = require('opn'); -exports.getDoc = function getDoc(search, browse = true){ +export function getDoc(search: string | null, browse: boolean = true){ let formattedSearch = search; @@ -22,7 +21,7 @@ exports.getDoc = function getDoc(search, browse = true){ }; -exports.getUrls = function getUrls(search = ''){ +export function getUrls(search: string | null = ''){ if(search == null) search = ''; @@ -41,7 +40,7 @@ exports.getUrls = function getUrls(search = ''){ return docs; }; -function searchInResources(search){ +function searchInResources(search: string): string[] { let dotCount = (search.match(/\./g) || []).length; @@ -54,7 +53,7 @@ function searchInResources(search){ }else if(dotCount == 1){ - let urls = new Array(); + let urls: string[] = new Array(); // Check PropertyTypes if(awsResources['PropertyTypes'].hasOwnProperty(search)){ @@ -90,7 +89,7 @@ function searchInResources(search){ return []; } -function searchExtraDocs(search){ +function searchExtraDocs(search: string) { if(awsExtraDocs.hasOwnProperty(search)){ return [ awsExtraDocs[search] ]; }else{ diff --git a/src/fixes.d.ts b/src/fixes.d.ts new file mode 100644 index 0000000..acaea2a --- /dev/null +++ b/src/fixes.d.ts @@ -0,0 +1,7 @@ +import yaml = require('js-yaml') + +declare module 'js-yaml' { + export interface LoadOptions { + onWarning?: (warning: string) => void; + } +} \ No newline at end of file diff --git a/src/index.es6 b/src/index.ts similarity index 85% rename from src/index.es6 rename to src/index.ts index a01b610..068f343 100644 --- a/src/index.es6 +++ b/src/index.ts @@ -4,12 +4,13 @@ * Module dependencies. */ -let program = require('commander'); -let colors = require('colors'); +import program = require('commander'); +require('colors'); let version = require('../package').version; -let firstArg, secondArg, params = null; +let firstArg: string | undefined = undefined +let secondArg: string = undefined!; -function list(val) { +function list(val: string) { return val.split(','); } @@ -31,8 +32,12 @@ if (typeof firstArg === 'undefined') { process.exit(1); } +import validatorBaseImport = require('./validator'); +import docsBaseImport = require('./docs'); + if(firstArg == "validate"){ - const validator = require('./validator'); + + const validator = require('./validator') as typeof validatorBaseImport; if(program.parameters){ for(let param of program.parameters){ @@ -51,7 +56,6 @@ if(firstArg == "validate"){ } let result = validator.validateFile(secondArg); - // Show the errors console.log((result['errors']['info'].length + " infos").grey); for(let info of result['errors']['info']){ @@ -82,8 +86,7 @@ if(firstArg == "validate"){ process.exit(0) } - }else if(firstArg == "docs"){ - const docs = require('./docs'); + const docs = require('./docs') as typeof docsBaseImport; console.log(docs.getDoc(secondArg)) } diff --git a/src/logger.es6 b/src/logger.ts similarity index 83% rename from src/logger.es6 rename to src/logger.ts index ca7ff96..c4d78bf 100644 --- a/src/logger.es6 +++ b/src/logger.ts @@ -1,4 +1,4 @@ -let winston = require('winston'); +import winston = require('winston'); let logger = new (winston.Logger)({ transports: [ @@ -10,4 +10,4 @@ let logger = new (winston.Logger)({ exitOnError: false }); -module.exports = logger; \ No newline at end of file +export = logger; \ No newline at end of file diff --git a/src/parser.es6 b/src/parser.ts similarity index 90% rename from src/parser.es6 rename to src/parser.ts index ecd281d..5601288 100644 --- a/src/parser.es6 +++ b/src/parser.ts @@ -1,9 +1,8 @@ -import yaml from 'js-yaml'; -import { CLOUDFORMATION_SCHEMA} from 'cloudformation-js-yaml-schema'; -import fs from 'fs'; +import yaml = require('js-yaml'); +const { CLOUDFORMATION_SCHEMA } = require('cloudformation-js-yaml-schema'); +import fs = require('fs'); - -exports.openFile = function openFile(path){ +export function openFile(path: string){ // Check the file path is valid if (!fs.existsSync(path)) { @@ -26,7 +25,7 @@ exports.openFile = function openFile(path){ }; -function openYaml(path){ +function openYaml(path: string){ // Try and load the Yaml let yamlParse = yaml.safeLoad(fs.readFileSync(path, 'utf8'), { @@ -49,7 +48,7 @@ function openYaml(path){ } -function openJson(path){ +function openJson(path: string){ return JSON.parse(fs.readFileSync(path, 'utf8')); @@ -57,7 +56,7 @@ function openJson(path){ let lastPlaceInTemplate = null; let lastKeyInTemplate = null; -function cleanupYaml(ref){ +function cleanupYaml(ref: any){ // Step into next attribute for(let i=0; i < Object.keys(ref).length; i++){ diff --git a/src/resourcesSpec.es6 b/src/resourcesSpec.ts similarity index 64% rename from src/resourcesSpec.es6 rename to src/resourcesSpec.ts index f217976..9aac037 100644 --- a/src/resourcesSpec.es6 +++ b/src/resourcesSpec.ts @@ -1,7 +1,12 @@ -let specification = require('../data/aws_resources_specification.json'); -let resourceRefOverride = require('../data/aws_resource_ref_types.json'); - -function getResourceType(type){ +import { + awsResources as specification, + awsResourceRefTypes as resourceRefOverride, + ResourceType, + ResourcePropertyType, + AWSPrimitiveType +} from './awsData'; + +function getResourceType(type: string){ // If the type starts with Custom::, it's a custom resource. if(type.indexOf('Custom::') === 0){ return specification.ResourceTypes['AWS::CloudFormation::CustomResource']; @@ -14,7 +19,7 @@ function getResourceType(type){ return null; } -function getPropertyType(type){ +function getPropertyType(type: string){ if(specification.PropertyTypes.hasOwnProperty(type)){ return specification.PropertyTypes[type] }else{ @@ -24,9 +29,8 @@ function getPropertyType(type){ /** * Get a Resource or Property type from the specification. - * @param type object or null */ -function getType(type){ +function getType(type: string){ if(isPropertyTypeFormat(type)){ return getPropertyType(type); }else{ @@ -34,7 +38,7 @@ function getType(type){ } } -function isPropertyTypeFormat(type){ +function isPropertyTypeFormat(type: string){ if(typeof type == 'string') { return (type.indexOf('.') != -1) || type == 'Tag'; }else{ @@ -42,7 +46,7 @@ function isPropertyTypeFormat(type){ } } -function getRefOverride(resourceType){ +function getRefOverride(resourceType: string){ if(resourceRefOverride.hasOwnProperty(resourceType)){ return resourceRefOverride[resourceType]; }else{ @@ -52,11 +56,10 @@ function getRefOverride(resourceType){ /** * Checks a ResourceType or PropertyType for the presence of a propertyName - * @param parentPropertyType string of a ResourceType or PropertyType + * @param parentPropertyType a ResourceType or PropertyType * @param propertyName name of the property to check against the specification - * @return {boolean} True if the property exists for the parentPropertyType */ -function isValidProperty(parentPropertyType, propertyName){ +function isValidProperty(parentPropertyType: string, propertyName: string){ // Check if the parentPropertyType exists let spec = getType(parentPropertyType); @@ -71,11 +74,8 @@ function isValidProperty(parentPropertyType, propertyName){ /** * Checks the resource type and returns true if the propertyName is required. - * @param parentPropertyType - * @param propertyName - * @return {boolean} */ -function isRequiredProperty(parentPropertyType, propertyName){ +function isRequiredProperty(parentPropertyType: string, propertyName: string){ // Check if the parentPropertyType exists let spec = getType(parentPropertyType); if(spec === null){ @@ -92,12 +92,12 @@ function isRequiredProperty(parentPropertyType, propertyName){ } } -function isArnProperty(propertyName){ +function isArnProperty(propertyName: string){ // Check if the parentPropertyType exists return (propertyName.indexOf('Arn') != -1); } -function isSinglePrimitivePropertyType(parentPropertyType, propertyName){ +function isSinglePrimitivePropertyType(parentPropertyType: string, propertyName: string){ // Check if the parentPropertyType exists let spec = getType(parentPropertyType); if(spec === null){ @@ -114,19 +114,19 @@ function isSinglePrimitivePropertyType(parentPropertyType, propertyName){ } } -function isAdditionalPropertiesEnabled(resourceType){ - let spec = getType(resourceType); - return (spec.hasOwnProperty('AdditionalProperties') && spec['AdditionalProperties'] === true) +function isAdditionalPropertiesEnabled(resourceType: string){ + let spec = getResourceType(resourceType); + return (spec !== null && spec['AdditionalProperties'] === true) } -function isPropertyTypeList(parentPropertyType, key){ +function isPropertyTypeList(parentPropertyType: string, key: string){ // Get the type let spec = getType(parentPropertyType); // Check if Type == List - return (spec !== null && spec['Properties'][key].hasOwnProperty('Type') && spec['Properties'][key]['Type'] == "List"); + return (spec !== null && spec['Properties'][key]['Type'] === "List"); } -function isPropertyTypeMap(parentPropertyType, key){ +function isPropertyTypeMap(parentPropertyType: string, key: string){ // Get the type let spec = getType(parentPropertyType); // Check if Type == Map @@ -134,10 +134,10 @@ function isPropertyTypeMap(parentPropertyType, key){ } -function getPropertyTypeApi(baseType, propType, key){ +function getPropertyTypeApi(baseType: string, propType: string, key: string){ let spec = getType(propType); - if(spec['Properties'].hasOwnProperty(key)){ + if(spec !== null && spec['Properties'].hasOwnProperty(key)){ if(spec['Properties'][key].hasOwnProperty('PrimitiveType')){ return spec['Properties'][key]['PrimitiveType']; } @@ -156,22 +156,22 @@ function getPropertyTypeApi(baseType, propType, key){ return 'Unknown'; } -function hasPrimitiveItemType(type, key) { +function hasPrimitiveItemType(type: string, key: string) { let spec = getType(type); - return spec['Properties'].hasOwnProperty(key) && spec['Properties'][key].hasOwnProperty('PrimitiveItemType'); + return (spec !== null) && spec['Properties'].hasOwnProperty(key) && spec['Properties'][key].hasOwnProperty('PrimitiveItemType'); } -function getPrimitiveItemType(type, key){ +function getPrimitiveItemType(type: string, key: string): AWSPrimitiveType | undefined { let spec = getType(type); - if(hasPrimitiveItemType(type, key)){ + if(spec !== null && hasPrimitiveItemType(type, key)){ return spec['Properties'][key]['PrimitiveItemType']; } } -function getRequiredProperties(type){ +function getRequiredProperties(type: string){ let spec = getType(type); let requiredProperties = []; @@ -188,16 +188,19 @@ function getRequiredProperties(type){ return requiredProperties; } -exports.getType = getType; -exports.isValidProperty = isValidProperty; -exports.isRequiredProperty = isRequiredProperty; -exports.isPrimitiveProperty = isSinglePrimitivePropertyType; -exports.isArnProperty = isArnProperty; -exports.getRefOverride = getRefOverride; -exports.isPropertyTypeList = isPropertyTypeList; -exports.isPropertyTypeMap = isPropertyTypeMap; -exports.getPropertyType = getPropertyTypeApi; -exports.getPrimitiveItemType = getPrimitiveItemType; -exports.hasPrimitiveItemType = hasPrimitiveItemType; -exports.getRequiredProperties = getRequiredProperties; -exports.isAdditionalPropertiesEnabled = isAdditionalPropertiesEnabled; \ No newline at end of file +export = { + getType, + getResourceType, + isValidProperty, + isRequiredProperty, + isPrimitiveProperty: isSinglePrimitivePropertyType, + isArnProperty, + getRefOverride, + isPropertyTypeList, + isPropertyTypeMap, + getPropertyType: getPropertyTypeApi, + getPrimitiveItemType, + hasPrimitiveItemType, + getRequiredProperties, + isAdditionalPropertiesEnabled +}; \ No newline at end of file diff --git a/test/docsTest.js b/src/test/docsTest.ts similarity index 93% rename from test/docsTest.js rename to src/test/docsTest.ts index eae9dcb..4ae9eb9 100644 --- a/test/docsTest.js +++ b/src/test/docsTest.ts @@ -1,12 +1,10 @@ -const chai = require('chai'); +import chai = require('chai'); const expect = chai.expect; const assert = chai.assert; -const docs = require('../src/docs'); +import docs = require('../docs'); describe('docs', () =>{ - - it('resource type', () => { let result = docs.getDoc("AWS::Lambda::Function", false); expect(result).to.contain("aws-resource-lambda-function.html"); diff --git a/test/indexTest.js b/src/test/indexTest.ts similarity index 64% rename from test/indexTest.js rename to src/test/indexTest.ts index 7bf1630..dd6f926 100644 --- a/test/indexTest.js +++ b/src/test/indexTest.ts @@ -1,7 +1,10 @@ -const chai = require('chai'); +import chai = require('chai'); const expect = chai.expect; const assert = chai.assert; +import childProcess = require('child_process'); +const exec = childProcess.exec; + describe('index', () => { @@ -9,7 +12,6 @@ describe('index', () => { it('no parameters', (done) => { - let exec = require('child_process').exec; exec('node lib/index.js', function(error, stdout, stderr) { expect(stderr).to.contain('no command given!'); done(); @@ -17,7 +19,6 @@ describe('index', () => { }).timeout(5000);; it('missing file parameter', (done) => { - let exec = require('child_process').exec; exec('node lib/index.js validate', function(error, stdout, stderr) { expect(stderr).to.contain('missing required argument'); done(); @@ -27,8 +28,7 @@ describe('index', () => { it('validate simple yaml', (done) => { - let exec = require('child_process').exec; - exec('node lib/index.js validate test/data/valid/yaml/issue-28-custom-resource.yaml', function(error, stdout, stderr) { + exec('node lib/index.js validate testData/valid/yaml/issue-28-custom-resource.yaml', function(error, stdout, stderr) { console.log(stderr); console.log(stdout); expect(stdout).to.contain('0 crit'); @@ -38,8 +38,7 @@ describe('index', () => { it('validate parameter flag', (done) => { - let exec = require('child_process').exec; - exec('node lib/index.js validate test/data/valid/json/2.json --parameters InstanceType="t1.micro"', function(error, stdout, stderr) { + exec('node lib/index.js validate testData/valid/json/2.json --parameters InstanceType="t1.micro"', function(error, stdout, stderr) { expect(stdout).to.contain('0 crit'); done(); }); @@ -47,8 +46,7 @@ describe('index', () => { it('validate pseudo flag', (done) => { - let exec = require('child_process').exec; - exec('node lib/index.js validate test/data/valid/yaml/pseudo-parameters.yaml ' + + exec('node lib/index.js validate testData/valid/yaml/pseudo-parameters.yaml ' + '--pseudo AWS::Region=us-east-1,AWS::AccountId=000000000000', function(error, stdout, stderr) { expect(stdout).to.contain('0 crit'); done(); @@ -58,8 +56,7 @@ describe('index', () => { it('validate pseudo + parameter flag', (done) => { - let exec = require('child_process').exec; - exec('node lib/index.js validate test/data/valid/yaml/pseudo-w-parameter.yaml ' + + exec('node lib/index.js validate testData/valid/yaml/pseudo-w-parameter.yaml ' + '--parameters MyInput=abcd --pseudo AWS::Region=us-east-1', function(error, stdout, stderr) { expect(stdout).to.contain('0 crit'); done(); @@ -68,8 +65,7 @@ describe('index', () => { it('invalid pseudo flag throws 2 critical error', (done) => { - let exec = require('child_process').exec; - exec('node lib/index.js validate test/data/valid/yaml/pseudo-parameters.yaml ' + + exec('node lib/index.js validate testData/valid/yaml/pseudo-parameters.yaml ' + '--pseudo AWS::Region=us-east-1,Something=000000000000', function(error, stdout, stderr) { expect(stdout).to.contain('2 crit'); done(); diff --git a/test/parserTest.js b/src/test/parserTest.ts similarity index 76% rename from test/parserTest.js rename to src/test/parserTest.ts index b0a039e..e73785e 100644 --- a/test/parserTest.js +++ b/src/test/parserTest.ts @@ -1,14 +1,14 @@ -const chai = require('chai'); +import chai = require('chai'); const expect = chai.expect; const assert = chai.assert; -const parser = require('../src/parser'); +import parser = require('../parser'); describe('parser', () =>{ describe('openFile', () => { it('valid yaml should return valid javascript object', () => { - let result = parser.openFile("./test/data/valid/yaml/1.yaml"); + let result = parser.openFile("./testData/valid/yaml/1.yaml"); expect(result).to.not.equal(undefined); expect(result).to.not.equal(null); expect(result['Outputs']['PublicIP']['Value']).to.have.any.keys('Fn::GetAtt'); @@ -17,18 +17,18 @@ describe('parser', () =>{ }); it('valid yaml with shorthand should return valid javascript object', () => { - let result = parser.openFile("./test/data/valid/yaml/valid_shorthand_sub.yaml"); + let result = parser.openFile("./testData/valid/yaml/valid_shorthand_sub.yaml"); expect(result).to.not.equal(undefined); expect(result).to.not.equal(null); }); it('invalid yaml should throw an Error', () => { - let fn = function(){ parser.openFile("./test/data/invalid/yaml/invalid_yaml.yaml"); }; + let fn = function(){ parser.openFile("./testData/invalid/yaml/invalid_yaml.yaml"); }; expect(fn).to.throw(/Could not determine file type. Check your template is not malformed./); }); it('invalid json should throw an Error', () => { - let fn = function(){ parser.openFile("./test/data/invalid/json/invalid_json.json"); }; + let fn = function(){ parser.openFile("./testData/invalid/json/invalid_json.json"); }; expect(fn).to.throw(/Could not determine file type. Check your template is not malformed./); }); diff --git a/test/resourcesTest.js b/src/test/resourcesTest.ts similarity index 98% rename from test/resourcesTest.js rename to src/test/resourcesTest.ts index 38f871f..22b91eb 100644 --- a/test/resourcesTest.js +++ b/src/test/resourcesTest.ts @@ -1,7 +1,7 @@ -const chai = require('chai'); +import chai = require('chai'); const expect = chai.expect; const assert = chai.assert; -const resourceSpec = require('../src/resourcesSpec'); +import resourceSpec = require('../resourcesSpec'); describe('resourceSpec', () =>{ diff --git a/test/validatorTest.js b/src/test/validatorTest.ts similarity index 84% rename from test/validatorTest.js rename to src/test/validatorTest.ts index d35d283..499cd9e 100644 --- a/test/validatorTest.js +++ b/src/test/validatorTest.ts @@ -1,7 +1,7 @@ -const chai = require('chai'); +import chai = require('chai'); const expect = chai.expect; const assert = chai.assert; -const validator = require('../src/validator'); +import validator = require('../validator'); describe('validator', () => { @@ -14,7 +14,7 @@ describe('validator', () => { it('a valid (1.json) template should return an object with validTemplate = true, no crit errors', () => { - const input = require('./data/valid/json/1.json'); + const input = require('../../testData/valid/json/1.json'); let result = validator.validateJsonObject(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', true); @@ -22,7 +22,7 @@ describe('validator', () => { }); it('a valid (2.json) template should return an object with validTemplate = true, no crit errors', () => { - const input = require('./data/valid/json/2.json'); + const input = require('../../testData/valid/json/2.json'); validator.addParameterValue('InstanceType', 't1.micro'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', true); @@ -30,7 +30,7 @@ describe('validator', () => { }); it('a valid (3.json) template should return an object with validTemplate = true, no crit errors', () => { - const input = require('./data/valid/json/3.json'); + const input = require('../../testData/valid/json/3.json'); let result = validator.validateJsonObject(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', true); @@ -38,7 +38,7 @@ describe('validator', () => { }); it('a valid (4.json) template should return an object with validTemplate = true, no crit errors', () => { - const input = require('./data/valid/json/4.json'); + const input = require('../../testData/valid/json/4.json'); validator.addParameterValue('InstanceType', 't1.micro'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', true); @@ -46,7 +46,7 @@ describe('validator', () => { }); it('2 invalid resource types should return an object with validTemplate = false, 2 crit errors', () => { - const input = require('./data/invalid/json/1_invalid_resource_type.json'); + const input = require('../../testData/invalid/json/1_invalid_resource_type.json'); let result = validator.validateJsonObject(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', false); @@ -54,56 +54,56 @@ describe('validator', () => { }); it('1 missing parameter type should return an object with validTemplate = false, 1 crit errors', () => { - const input = require('./data/invalid/json/1_missing_parameter_type.json'); + const input = require('../../testData/invalid/json/1_missing_parameter_type.json'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); }); it('1 invalid parameter type should return an object with validTemplate = false, 1 crit errors', () => { - const input = require('./data/invalid/json/1_invalid_parameter_type.json'); + const input = require('../../testData/invalid/json/1_invalid_parameter_type.json'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); }); it('missing section resources should return an object with validTemplate = false, 1 crit errors', () => { - const input = require('./data/invalid/json/no_resources.json'); + const input = require('../../testData/invalid/json/no_resources.json'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); }); it('empty section resources should return an object with validTemplate = false, 1 crit errors', () => { - const input = require('./data/invalid/json/empty_resources.json'); + const input = require('../../testData/invalid/json/empty_resources.json'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); }); it('1 invalid resource reference should return an object with validTemplate = false, 1 crit errors', () => { - const input = require('./data/invalid/json/1_invalid_resource_reference.json'); + const input = require('../../testData/invalid/json/1_invalid_resource_reference.json'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); }); it('1 invalid parameter reference should return an object with validTemplate = false, 1 crit errors', () => { - const input = require('./data/invalid/json/1_invalid_parameter_reference.json'); + const input = require('../../testData/invalid/json/1_invalid_parameter_reference.json'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); }); it('2 invalid Fn::Join\'s should return an object with validTemplate = false, 2 crit errors', () => { - const input = require('./data/invalid/json/2_invalid_intrinsic_join.json'); + const input = require('../../testData/invalid/json/2_invalid_intrinsic_join.json'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(2); }); it('1 invalid Fn::GetAtt should return an object with validTemplate = false, 1 crit errors', () => { - const input = require('./data/invalid/json/1_invalid_intrinsic_get_att.json'); + const input = require('../../testData/invalid/json/1_invalid_intrinsic_get_att.json'); validator.addParameterValue('InstanceType', 't1.micro'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', false); @@ -111,14 +111,14 @@ describe('validator', () => { }); it('1 invalid Fn::FindInMap should return an object with validTemplate = false, 1 crit errors', () => { - const input = require('./data/invalid/json/1_invalid_intrinsic_mappings.json'); + const input = require('../../testData/invalid/json/1_invalid_intrinsic_mappings.json'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); }); it('1 invalid Ref within Parameters should return an object with validTemplate = false, 1 crit errors', () => { - const input = require('./data/invalid/json/1_intrinsic_function_in_parameters.json'); + const input = require('../../testData/invalid/json/1_intrinsic_function_in_parameters.json'); let result = validator.validateJsonObject(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', false); @@ -126,7 +126,7 @@ describe('validator', () => { }); it('1 invalid GetAZs parameter should return an object with validTemplate = false, 1 crit errors', () => { - const input = require('./data/invalid/json/1_invalid_get_azs_parameter.json'); + const input = require('../../testData/invalid/json/1_invalid_get_azs_parameter.json'); validator.addParameterValue('InstanceType', 't1.micro'); let result = validator.validateJsonObject(input); expect(result).to.have.deep.property('templateValid', false); @@ -134,7 +134,7 @@ describe('validator', () => { }); it('1 reference non AWS::Region GetAZs parameter should return an object with validTemplate = false, 1 warn errors', () => { - const input = require('./data/invalid/json/1_warning_ref_get_azs_parameter.json'); + const input = require('../../testData/invalid/json/1_warning_ref_get_azs_parameter.json'); validator.addParameterValue('InstanceType', 't1.micro'); let result = validator.validateJsonObject(input); console.log(result['errors']['crit']); @@ -147,14 +147,14 @@ describe('validator', () => { describe('Fn::Sub', () => { it('3 valid Fn::Sub should return an object with validTemplate = true, 0 crit errors', () => { - const input = './test/data/valid/yaml/valid_sub_operations.yaml'; + const input = './testData/valid/yaml/valid_sub_operations.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', true); expect(result['errors']['crit']).to.have.lengthOf(0); }); it('a sub referencing an invalid resource should result in validTemplate = false, 1 crit errors, no warnings', () => { - const input = 'test/data/invalid/yaml/invalid_sub_reference.yaml'; + const input = 'testData/invalid/yaml/invalid_sub_reference.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); @@ -162,7 +162,7 @@ describe('validator', () => { }); it('a sub getatt an invalid resource should result in validTemplate = false, 1 crit errors, no warnings', () => { - const input = 'test/data/invalid/yaml/invalid_sub_getatt.yaml'; + const input = 'testData/invalid/yaml/invalid_sub_getatt.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); @@ -174,7 +174,7 @@ describe('validator', () => { describe('Fn::ImportValue', () => { it('1 invalid Fn::ImportValue should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_import_value_intrinsic_function.yaml'; + const input = './testData/invalid/yaml/invalid_import_value_intrinsic_function.yaml'; let result = validator.validateFile(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', false); @@ -183,7 +183,7 @@ describe('validator', () => { }); it('1 invalid Fn::ImportValue should return an object with validTemplate = true, 0 crit errors', () => { - const input = './test/data/valid/yaml/valid_import_value.yaml'; + const input = './testData/valid/yaml/valid_import_value.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', true); expect(result['errors']['crit']).to.have.lengthOf(0); @@ -194,7 +194,7 @@ describe('validator', () => { describe('conditions', () => { it('1 invalid if condition arguments should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_if_statement_arguments.yaml'; + const input = './testData/invalid/yaml/invalid_if_statement_arguments.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); @@ -202,7 +202,7 @@ describe('validator', () => { }); it('1 invalid if condition should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_if_statement_condition.yaml'; + const input = './testData/invalid/yaml/invalid_if_statement_condition.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); @@ -210,7 +210,7 @@ describe('validator', () => { }); it('1 invalid condition value should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_condition_value.yaml'; + const input = './testData/invalid/yaml/invalid_condition_value.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); @@ -218,7 +218,7 @@ describe('validator', () => { }); it('1 invalid condition value type should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_condition_value_type.yaml'; + const input = './testData/invalid/yaml/invalid_condition_value_type.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); @@ -230,7 +230,7 @@ describe('validator', () => { describe('templateVersion', () => { it('1 invalid template version should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_template_format_version.yaml'; + const input = './testData/invalid/yaml/invalid_template_format_version.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); @@ -239,14 +239,14 @@ describe('validator', () => { // Check for optional template version it('1 missing template version should return an object with validTemplate = true, 0 crit errors', () => { - const input = './test/data/valid/yaml/valid_missing_template_format_version.yaml'; + const input = './testData/valid/yaml/valid_missing_template_format_version.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', true); expect(result['errors']['crit']).to.have.lengthOf(0); }); it('1 unquouted template format version should return an object with validTemplate = true, no crit errors, 1 warn error', () => { - const input = 'test/data/valid/yaml/valid_unquoted_template_version.yaml'; + const input = 'testData/valid/yaml/valid_unquoted_template_version.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', true); expect(result['errors']['crit']).to.have.lengthOf(0); @@ -258,7 +258,7 @@ describe('validator', () => { describe('propertyValidation', () => { it('1 invalid arn property should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_arn.yaml'; + const input = './testData/invalid/yaml/invalid_arn.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); @@ -266,7 +266,7 @@ describe('validator', () => { }); it('1 invalid property name of ResourceType should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_resourcetype_property_name.yaml'; + const input = './testData/invalid/yaml/invalid_resourcetype_property_name.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); @@ -275,7 +275,7 @@ describe('validator', () => { }); it('1 invalid property name of PropertyType should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_propertytype_property_name.yaml'; + const input = './testData/invalid/yaml/invalid_propertytype_property_name.yaml'; let result = validator.validateFile(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', false); @@ -284,7 +284,7 @@ describe('validator', () => { }); it('1 invalid property name of Tag list should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_ec2_tags_property_name.yaml'; + const input = './testData/invalid/yaml/invalid_ec2_tags_property_name.yaml'; let result = validator.validateFile(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', false); @@ -293,7 +293,7 @@ describe('validator', () => { }); it('1 string property should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_property_type_string.yaml'; + const input = './testData/invalid/yaml/invalid_property_type_string.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); console.log(result['errors']['crit']); @@ -303,7 +303,7 @@ describe('validator', () => { }); it('1 missing propertyType property should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_required_propertytype_prop_missing.yaml'; + const input = './testData/invalid/yaml/invalid_required_propertytype_prop_missing.yaml'; let result = validator.validateFile(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', false); @@ -312,7 +312,7 @@ describe('validator', () => { }); it('1 missing resourceType property should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_required_resourcetype_prop_missing.yaml'; + const input = './testData/invalid/yaml/invalid_required_resourcetype_prop_missing.yaml'; let result = validator.validateFile(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', false); @@ -321,7 +321,7 @@ describe('validator', () => { }); it('1 invalid boolean property should return an object with validTemplate = false, 1 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_boolean_type.yaml'; + const input = './testData/invalid/yaml/invalid_boolean_type.yaml'; validator.addParameterValue('CertificateArn', 'arn:aws:region:something'); let result = validator.validateFile(input); console.log(result['errors']['crit']); @@ -331,7 +331,7 @@ describe('validator', () => { }); it('4 invalid nested properties should return an object with validTemplate = false, 4 crit errors', () => { - const input = './test/data/invalid/yaml/invalid_missing_nested_property.yaml'; + const input = './testData/invalid/yaml/invalid_missing_nested_property.yaml'; let result = validator.validateFile(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', false); @@ -347,14 +347,14 @@ describe('validator', () => { }); it('a valid template with a Map property should return an object with validTemplate = true, no crit errors', () => { - const input = 'test/data/valid/yaml/maps.yaml'; + const input = 'testData/valid/yaml/maps.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', true); expect(result['errors']['crit']).to.have.lengthOf(0); }); it('an invalid template with a Map property should return an object with validTemplate = false, 1 crit error', () => { - const input = 'test/data/invalid/yaml/invalid_maps.yaml'; + const input = 'testData/invalid/yaml/invalid_maps.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(1); @@ -367,7 +367,7 @@ describe('validator', () => { describe('validateYamlFile', ()=> { it('a valid (1.yaml) template should return an object with validTemplate = true, no crit errors', () => { - const input = 'test/data/valid/yaml/1.yaml'; + const input = 'testData/valid/yaml/1.yaml'; validator.addParameterValue('InstanceType', 't1.micro'); let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', true); @@ -375,7 +375,7 @@ describe('validator', () => { }); it('a valid (2.yaml) template should return an object with validTemplate = true, no crit errors', () => { - const input = 'test/data/valid/yaml/2.yaml'; + const input = 'testData/valid/yaml/2.yaml'; validator.addParameterValue('CertificateArn', 'arn:aws:region:something'); let result = validator.validateFile(input); console.log(result['errors']['crit']); @@ -386,7 +386,7 @@ describe('validator', () => { }); it('a valid (valid_minus_one_as_string.yaml) template should return an object with validTemplate = true, no crit errors', () => { - const input = 'test/data/valid/yaml/valid_minus_one_as_string.yaml'; + const input = 'testData/valid/yaml/valid_minus_one_as_string.yaml'; let result = validator.validateFile(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', true); @@ -396,14 +396,14 @@ describe('validator', () => { describe('issues', () => { it('a valid ASG template with Tags property should return an object with validTemplate = true, no crit errors', () => { - const input = 'test/data/valid/yaml/issue-24.yaml'; + const input = 'testData/valid/yaml/issue-24.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', true); expect(result['errors']['crit']).to.have.lengthOf(0); }); it('both methods of defining a custom resource should result in validTemplate = true, no crit errors', () => { - const input = 'test/data/valid/yaml/issue-28-custom-resource.yaml'; + const input = 'testData/valid/yaml/issue-28-custom-resource.yaml'; let result = validator.validateFile(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', true); @@ -411,7 +411,7 @@ describe('validator', () => { }); it('numeric properties should result in validTemplate = true, no crit errors, no warn errors', () => { - const input = 'test/data/valid/yaml/issue-27-numeric-properties.yaml'; + const input = 'testData/valid/yaml/issue-27-numeric-properties.yaml'; let result = validator.validateFile(input); console.log(result['errors']['warn']); expect(result).to.have.deep.property('templateValid', true); @@ -420,14 +420,14 @@ describe('validator', () => { }); it('IAM with ManagedPolicyName should result in validTemplate=true, no crit errors, no warn errors', () => { - const input = 'test/data/valid/yaml/issue-42-managed-policy-name.yaml'; + const input = 'testData/valid/yaml/issue-42-managed-policy-name.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', true); expect(result['errors']['crit']).to.have.lengthOf(0); }); it('Reference to RDS attribute validTemplate=true, no crit errors, no warn errors', () => { - const input = 'test/data/valid/yaml/issue-44-database-endpoint.yaml'; + const input = 'testData/valid/yaml/issue-44-database-endpoint.yaml'; let result = validator.validateFile(input); console.log(result['errors']['crit']); console.log(result['errors']['warn']); @@ -436,7 +436,7 @@ describe('validator', () => { }); it('if condition not working as expected, no crit errors, no warn errors', () => { - const input = 'test/data/valid/yaml/issue-61.yaml'; + const input = 'testData/valid/yaml/issue-61.yaml'; validator.addParameterValue('Env', 'Production'); let result = validator.validateFile(input); console.log(result['errors']['crit']); @@ -446,7 +446,7 @@ describe('validator', () => { }); it('if condition not working as expected, 2 crit errors, no warn errors', () => { - const input = 'test/data/valid/yaml/issue-61.yaml'; + const input = 'testData/valid/yaml/issue-61.yaml'; validator.addParameterValue('Env', 'Dev'); let result = validator.validateFile(input); console.log(result['errors']['crit']); @@ -458,7 +458,7 @@ describe('validator', () => { describe('parameters-validation', () => { it('an unallowed parameter value provided at runtime gets rejected', () => { - const input = 'test/data/valid/yaml/parameters-allowed-values.yaml'; + const input = 'testData/valid/yaml/parameters-allowed-values.yaml'; validator.addParameterValue('Env', 'InvalidValue'); let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); @@ -466,7 +466,7 @@ describe('validator', () => { }); it('an allowed parameter value provided at runtime gets allowed', () => { - const input = 'test/data/valid/yaml/parameters-allowed-values.yaml'; + const input = 'testData/valid/yaml/parameters-allowed-values.yaml'; validator.addParameterValue('Env', 'Dev'); let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', true); @@ -474,7 +474,7 @@ describe('validator', () => { }); it('an empty parameter value provided at runtime picks up the empty default (which will throw an error)', () => { - const input = 'test/data/valid/yaml/parameters-allowed-values.yaml'; + const input = 'testData/valid/yaml/parameters-allowed-values.yaml'; let result = validator.validateFile(input); console.log(result['errors']['crit']); expect(result).to.have.deep.property('templateValid', false); @@ -484,7 +484,7 @@ describe('validator', () => { describe('pseudo-parmeters', () => { it('defining an override for accountId should result in validTemplate = true, no crit errors, no warnings', () => { - const input = 'test/data/valid/yaml/pseudo-parameters.yaml'; + const input = 'testData/valid/yaml/pseudo-parameters.yaml'; validator.addPseudoValue("AWS::AccountId", "000000000000"); validator.addPseudoValue("AWS::Region", "us-east-1"); let result = validator.validateFile(input); @@ -497,7 +497,7 @@ describe('validator', () => { describe('output-references', () => { it('an output referencing an invalid resource should result in validTemplate = false, 1 crit errors, no warnings', () => { - const input = 'test/data/invalid/yaml/issue-39-output-reference-invalid.yaml'; + const input = 'testData/invalid/yaml/issue-39-output-reference-invalid.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', false); expect(result['errors']['crit']).to.have.lengthOf(3); @@ -505,7 +505,7 @@ describe('validator', () => { }); it('an output referencing an valid resource should result in validTemplate = true, 0 crit errors, no warnings', () => { - const input = 'test/data/valid/yaml/issue-39-output-reference-check.yaml'; + const input = 'testData/valid/yaml/issue-39-output-reference-check.yaml'; let result = validator.validateFile(input); expect(result).to.have.deep.property('templateValid', true); expect(result['errors']['crit']).to.have.lengthOf(0); diff --git a/src/validator.es6 b/src/validator.ts similarity index 90% rename from src/validator.es6 rename to src/validator.ts index 2cecd74..854edb6 100644 --- a/src/validator.es6 +++ b/src/validator.ts @@ -1,40 +1,56 @@ -let workingInput = null; +let workingInput: any = null; let stopValidation = false; -let errorObject = {"templateValid": true, "errors": {"info": [], "warn": [], "crit": []}}; -const resourcesSpec = require('./resourcesSpec'); -const logger = require('./logger'); -const parser = require('./parser'); +import resourcesSpec = require('./resourcesSpec'); +import logger = require('./logger'); +import parser = require('./parser'); const mockArnPrefix = "arn:aws:mock:region:123456789012:"; -const parameterTypesSpec = require('../data/aws_parameter_types.json'); -const awsRefOverrides = require('../data/aws_ref_override.json'); -const awsIntrinsicFunctions = require('../data/aws_intrinsic_functions.json'); -const docs = require('./docs'); -let parameterRuntimeOverride= {}; +import { + awsParameterTypes as parameterTypesSpec, + awsRefOverrides, + awsIntrinsicFunctions +} from './awsData'; +import docs = require('./docs'); +let parameterRuntimeOverride: {[parameter: string]: string | string[]} = {}; // Todo: Allow override for RefOverrides ex. Regions -exports.resetValidator = function resetValidator(){ +interface ErrorRecord { + message: string, + resource: string, + documentation: string +} + +let errorObject = { + "templateValid": true, + "errors": { + "info": [] as ErrorRecord[], + "warn": [] as ErrorRecord[], + "crit": [] as ErrorRecord[] + } +}; + +export function resetValidator(){ errorObject = {"templateValid": true, "errors": {"info": [], "warn": [], "crit": []}}; stopValidation = false; parameterRuntimeOverride = {}; }; -exports.validateFile = function validateFile(path){ +export function validateFile(path: string){ // Convert to object, this will throw an exception on an error workingInput = parser.openFile(path); // Let's go! return validateWorkingInput(); }; -exports.validateJsonObject = function validateJsonObject(obj){ +export function validateJsonObject(obj: any){ workingInput = obj; return validateWorkingInput(); }; -exports.addParameterValue = function addParameterValue(parameter, value){ +export function addParameterValue(parameter: string, value: string){ addParameterOverride(parameter, value); }; -exports.addPseudoValue = function addPseudoValue(parameter, value){ +export function addPseudoValue(parameter: string, value: string){ // Silently drop requests to change AWS::NoValue if(parameter == 'AWS::NoValue') { return; @@ -57,7 +73,7 @@ exports.addPseudoValue = function addPseudoValue(parameter, value){ } }; -function addParameterOverride(parameter, value){ +function addParameterOverride(parameter: string, value: string){ parameterRuntimeOverride[parameter] = value; } @@ -122,7 +138,7 @@ function assignParametersOutput(){ if (workingInput['Parameters'].hasOwnProperty(param)) { // Check if Type is defined - let parameterRefAttribute = `string_input_${param}`; + let parameterRefAttribute: string | string[] = `string_input_${param}`; // Check if the Ref for the parameter has been defined at runtime if(parameterRuntimeOverride.hasOwnProperty(param)){ @@ -167,7 +183,9 @@ function assignParametersOutput(){ } } -function addError(severity, message, resourceStack, help){ +type Severity = keyof typeof errorObject.errors; + +function addError(severity: Severity, message : string, resourceStack: string[] = [], help?: string){ let obj = { 'message': message, 'resource': resourceStack.join(' > '), @@ -273,7 +291,7 @@ function assignResourcesOutputs(){ }else{ // Check if Type is valid resourceType = workingInput['Resources'][res]['Type']; - spec = resourcesSpec.getType(workingInput['Resources'][res]['Type']); + spec = resourcesSpec.getResourceType(workingInput['Resources'][res]['Type']); if(spec === null){ addError('crit', `Resource ${res} has an invalid Type of ${resourceType}.`, @@ -300,14 +318,12 @@ function assignResourcesOutputs(){ workingInput['Resources'][res]['Attributes']['Ref'] = refValue; // Go through the attributes of the specification, and assign them - if(spec != null && spec.hasOwnProperty('Attributes')){ - for(let attr in spec['Attributes']){ - if(spec['Attributes'].hasOwnProperty(attr)) { - if (attr.indexOf('Arn') != -1) { - workingInput['Resources'][res]['Attributes'][attr] = mockArnPrefix + res; - }else { - workingInput['Resources'][res]['Attributes'][attr] = "mockAttr_" + res; - } + if(spec != null && spec.Attributes){ + for(let attr in spec.Attributes){ + if (attr.indexOf('Arn') != -1) { + workingInput['Resources'][res]['Attributes'][attr] = mockArnPrefix + res; + }else { + workingInput['Resources'][res]['Attributes'][attr] = "mockAttr_" + res; } } } @@ -331,11 +347,11 @@ function resolveReferences(){ } -let placeInTemplate = []; -let lastPositionInTemplate = null; -let lastPositionInTemplateKey = null; +let placeInTemplate: string[] = []; +let lastPositionInTemplate: any = null; +let lastPositionInTemplateKey: string | null = null; -function recursiveDecent(ref){ +function recursiveDecent(ref: any){ // Step into next attribute for(let i=0; i < Object.keys(ref).length; i++){ let key = Object.keys(ref)[i]; @@ -355,7 +371,7 @@ function recursiveDecent(ref){ }else { // Resolve the function let functionOutput = resolveIntrinsicFunction(ref, key); - if (functionOutput !== null) { + if (functionOutput !== null && lastPositionInTemplateKey !== null) { // Overwrite the position with the resolved value lastPositionInTemplate[lastPositionInTemplateKey] = functionOutput; } @@ -372,7 +388,7 @@ function recursiveDecent(ref){ placeInTemplate.pop(); } -function resolveCondition(ref, key){ +function resolveCondition(ref: any, key: string){ let toGet = ref[key]; let condition = false; @@ -395,55 +411,41 @@ function resolveCondition(ref, key){ return condition; } -function resolveIntrinsicFunction(ref, key){ +function resolveIntrinsicFunction(ref: any, key: string) : string | boolean | string[] | undefined | null{ switch(key){ case 'Ref': return doIntrinsicRef(ref, key); - break; case 'Condition': return resolveCondition(ref, key); - break; case 'Fn::Join': return doIntrinsicJoin(ref, key); - break; case 'Fn::Base64': return doIntrinsicBase64(ref, key); - break; case 'Fn::GetAtt': return doIntrinsicGetAtt(ref, key); - break; case 'Fn::FindInMap': return doIntrinsicFindInMap(ref, key); - break; case 'Fn::GetAZs': return doIntrinsicGetAZs(ref, key); - break; case 'Fn::Sub': return doIntrinsicSub(ref, key); - break; case 'Fn::If': return doIntrinsicIf(ref, key); - break; case 'Fn::Equals': return doIntrinsicEquals(ref, key); - break; case 'Fn::Or': return doIntrinsicOr(ref, key); - break; case 'Fn::Not': return doIntrinsicNot(ref, key); - break; case 'Fn::ImportValue': return doIntrinsicImportValue(ref, key); - break; default: addError("warn", `Unhandled Intrinsic Function ${key}, this needs implementing. Some errors might be missed.`, placeInTemplate, "Functions"); return null; - break; } } -function doIntrinsicRef(ref, key){ +function doIntrinsicRef(ref: any, key: string){ let refValue = ref[key]; let resolvedVal = "INVALID_REF"; @@ -465,7 +467,7 @@ function doIntrinsicRef(ref, key){ } -function doIntrinsicBase64(ref, key){ +function doIntrinsicBase64(ref: any, key: string){ // Only base64 encode strings let toEncode = ref[key]; if(typeof toEncode != "string"){ @@ -479,7 +481,7 @@ function doIntrinsicBase64(ref, key){ return Buffer.from(toEncode).toString('base64'); } -function doIntrinsicJoin(ref, key){ +function doIntrinsicJoin(ref: any, key: string){ // Ensure that all objects in the join array have been resolved to string, otherwise // we need to resolve them. // Expect 2 parameters @@ -495,7 +497,7 @@ function doIntrinsicJoin(ref, key){ } } -function doIntrinsicGetAtt(ref, key){ +function doIntrinsicGetAtt(ref: any, key: string){ let toGet = ref[key]; if(toGet.length < 2){ addError("crit", "Invalid parameters for Fn::GetAtt", placeInTemplate, "Fn::GetAtt"); @@ -530,7 +532,7 @@ function doIntrinsicGetAtt(ref, key){ } } -function doIntrinsicFindInMap(ref, key){ +function doIntrinsicFindInMap(ref: any, key: string){ let toGet = ref[key]; if(toGet.length != 3){ addError("crit", "Invalid parameters for Fn::FindInMap", placeInTemplate, "Fn::FindInMap"); @@ -560,7 +562,7 @@ function doIntrinsicFindInMap(ref, key){ } } -function doIntrinsicGetAZs(ref, key){ +function doIntrinsicGetAZs(ref: any, key: string){ let toGet = ref[key]; let region = awsRefOverrides['AWS::Region']; // If the argument is not a string, check it's Ref and resolve @@ -570,7 +572,7 @@ function doIntrinsicGetAZs(ref, key){ if(toGet[key] != 'AWS::Region'){ addError("warn", "Fn::GetAZs expects a region, ensure this reference returns a region", placeInTemplate, "Fn::GetAZs"); } - region = resolveIntrinsicFunction(toGet, "Ref"); + region = resolveIntrinsicFunction(toGet, "Ref") as string; }else{ // TODO Implement unit test for this addError("crit", "Fn::GetAZs only supports Ref or string as a parameter", placeInTemplate, "Fn::GetAZs"); } @@ -590,7 +592,7 @@ function doIntrinsicGetAZs(ref, key){ } -function doIntrinsicSub(ref, key){ +function doIntrinsicSub(ref: any, key: string){ let toGet = ref[key]; let replacementStr = null; let definedParams = null; @@ -651,7 +653,7 @@ function doIntrinsicSub(ref, key){ }else{ if(definedParams !== null && definedParams.hasOwnProperty(m)){ if(typeof definedParams[m] !== 'string') { - replacementVal = resolveIntrinsicFunction(definedParams[m], Object.keys(m)[0]); + replacementVal = resolveIntrinsicFunction(definedParams[m], Object.keys(m)[0]) as string; }else{ replacementVal = definedParams[m]; } @@ -673,7 +675,7 @@ function doIntrinsicSub(ref, key){ return replacementStr; } -function doIntrinsicIf(ref, key){ +function doIntrinsicIf(ref: any, key: string){ let toGet = ref[key]; // Check the value of the condition @@ -723,7 +725,7 @@ function doIntrinsicIf(ref, key){ return "INVALID_IF_STATEMENT"; } -function doIntrinsicEquals(ref, key) { +function doIntrinsicEquals(ref: any, key: string) { let toGet = ref[key]; // Check the value of the condition @@ -759,7 +761,7 @@ function doIntrinsicEquals(ref, key) { return false; } -function doIntrinsicOr(ref, key) { +function doIntrinsicOr(ref: any, key: string) { let toGet = ref[key]; // Check the value of the condition @@ -791,7 +793,7 @@ function doIntrinsicOr(ref, key) { } } -function doIntrinsicNot(ref, key){ +function doIntrinsicNot(ref: any, key: string){ let toGet = ref[key]; @@ -823,7 +825,7 @@ function doIntrinsicNot(ref, key){ return false; } -function doIntrinsicImportValue(ref, key){ +function doIntrinsicImportValue(ref: any, key: string){ let toGet = ref[key]; // If not string, resolve using the supported functions @@ -841,14 +843,14 @@ function doIntrinsicImportValue(ref, key){ if(typeof toGet == 'string'){ return "IMPORTEDVALUE" + toGet; // TODO: Consider making this commandline defined }else{ - addError(`warn`, `Something went wrong when resolving references for a Fn::ImportValue`, placeInTemplate, 'Fn::ImportValue'); + addError('warn', `Something went wrong when resolving references for a Fn::ImportValue`, placeInTemplate, 'Fn::ImportValue'); return 'INVALID_FN_IMPORTVALUE'; } } -function fnJoin(join, parts){ +function fnJoin(join: any, parts: any){ // Go through each parts and ensure they are resolved for(let p in parts){ if(parts.hasOwnProperty(p)) { @@ -863,7 +865,7 @@ function fnJoin(join, parts){ return parts.join(join); } -function fnGetAtt(reference, attribute){ +function fnGetAtt(reference: string, attribute: string){ if(workingInput['Resources'].hasOwnProperty(reference)){ if(workingInput['Resources'][reference]['Attributes'].hasOwnProperty(attribute)){ return workingInput['Resources'][reference]['Attributes'][attribute]; @@ -873,7 +875,7 @@ function fnGetAtt(reference, attribute){ return null; } -function fnFindInMap(map, first, second){ +function fnFindInMap(map: any, first: string, second: string){ if(workingInput.hasOwnProperty('Mappings')){ if(workingInput['Mappings'].hasOwnProperty(map)){ if(workingInput['Mappings'][map].hasOwnProperty(first)){ @@ -886,7 +888,7 @@ function fnFindInMap(map, first, second){ return null; } -function getRef(reference){ +function getRef(reference: string){ // Check in Resources if(workingInput['Resources'].hasOwnProperty(reference)){ return workingInput['Resources'][reference]['Attributes']['Ref']; @@ -906,7 +908,7 @@ function getRef(reference){ return null; } -let baseResourceType = null; +let baseResourceType: string = null!; function checkResourceProperties() { @@ -954,13 +956,13 @@ function checkResourceProperties() { placeInTemplate.pop(); } -function checkEachProperty(resourceType, ref, key){ +function checkEachProperty(resourceType: string, ref: any, key: string){ Object.keys(ref[key]).forEach((prop) => { checkResourceProperty(resourceType, ref[key], prop); }); } -function checkResourceProperty(resourcePropType, ref, key){ +function checkResourceProperty(resourcePropType: string, ref: any, key: string){ // Using the Key, the the Resource Type, get the expected Property type // resourceSpec get type of property using resourceType and property name @@ -980,7 +982,7 @@ function checkResourceProperty(resourcePropType, ref, key){ if(ref[key].hasOwnProperty(item)) { if (resourcesSpec.hasPrimitiveItemType(resourcePropType, key)) { // Get the Primitive List Type - let primitiveItemType = resourcesSpec.getPrimitiveItemType(resourcePropType, key); + let primitiveItemType = resourcesSpec.getPrimitiveItemType(resourcePropType, key)!; // Go through each item in list for(let li in ref[key]){ @@ -993,7 +995,7 @@ function checkResourceProperty(resourcePropType, ref, key){ } }else{ let propertyType = resourcesSpec.getPropertyType(baseResourceType, resourcePropType, key); - checkProperty(resourcePropType, ref[key], item, isPrimitiveProperty, propertyType); + checkProperty(resourcePropType, ref[key], item, isPrimitiveProperty, propertyType!); } } } @@ -1007,8 +1009,8 @@ function checkResourceProperty(resourcePropType, ref, key){ if (typeof ref[key] == 'object' && ref[key].constructor === Object) { const isPrimitiveProperty = resourcesSpec.hasPrimitiveItemType(resourcePropType, key); const propertyType = (isPrimitiveProperty) - ? resourcesSpec.getPrimitiveItemType(resourcePropType, key) - : resourcesSpec.getPropertyType(baseResourceType, resourcePropType, key); + ? resourcesSpec.getPrimitiveItemType(resourcePropType, key)! + : resourcesSpec.getPropertyType(baseResourceType, resourcePropType, key)!; for (const itemKey of Object.getOwnPropertyNames(ref[key])) { placeInTemplate.push(itemKey); @@ -1026,7 +1028,7 @@ function checkResourceProperty(resourcePropType, ref, key){ let isPrimTypeOf = (primTypeOf == 'string' || primTypeOf == 'number' || primTypeOf == 'boolean'); if((typeof ref[key] == 'object' && !isPrimitiveProperty) || (isPrimTypeOf && isPrimitiveProperty)) { placeInTemplate.push(key); - let propertyType = resourcesSpec.getPropertyType(baseResourceType, resourcePropType, key); + let propertyType = resourcesSpec.getPropertyType(baseResourceType, resourcePropType, key)!; checkProperty(resourcePropType, ref, key, isPrimitiveProperty, propertyType); placeInTemplate.pop(); }else{ @@ -1041,7 +1043,7 @@ function checkResourceProperty(resourcePropType, ref, key){ } -function checkForMissingProperties(properties, resourceType){ +function checkForMissingProperties(properties: {[k: string]: any}, resourceType: string){ let requiredProperties = resourcesSpec.getRequiredProperties(resourceType); // Remove the properties we have from the required property list @@ -1057,13 +1059,13 @@ function checkForMissingProperties(properties, resourceType){ // If we have any items left over, they have not been defined if(requiredProperties.length > 0){ for(let prop of requiredProperties){ - addError(`crit`, `Required property ${prop} missing for type ${resourceType}`, placeInTemplate, resourceType); + addError('crit', `Required property ${prop} missing for type ${resourceType}`, placeInTemplate, resourceType); } } } // Checks a single element of a property -function checkProperty(resourcePropType, ref, key, isPrimitiveType, propertyType){ +function checkProperty(resourcePropType: string, ref: any, key: string, isPrimitiveType: boolean, propertyType: string){ if(!isPrimitiveType){ // Recursive solve this property @@ -1102,7 +1104,7 @@ function checkProperty(resourcePropType, ref, key, isPrimitiveType, propertyType } } -function checkPropertyType(ref, key, propertyType, resourcePropType){ +function checkPropertyType(ref: any, key: string, propertyType: string, resourcePropType: string){ let val = ref[key]; switch(propertyType){ case 'String': // A 'String' in CF can be an int or something starting with a number, it's a loose check diff --git a/test/data/invalid/json/1_intrinsic_function_in_parameters.json b/testData/invalid/json/1_intrinsic_function_in_parameters.json similarity index 100% rename from test/data/invalid/json/1_intrinsic_function_in_parameters.json rename to testData/invalid/json/1_intrinsic_function_in_parameters.json diff --git a/test/data/invalid/json/1_invalid_get_azs_parameter.json b/testData/invalid/json/1_invalid_get_azs_parameter.json similarity index 100% rename from test/data/invalid/json/1_invalid_get_azs_parameter.json rename to testData/invalid/json/1_invalid_get_azs_parameter.json diff --git a/test/data/invalid/json/1_invalid_intrinsic_get_att.json b/testData/invalid/json/1_invalid_intrinsic_get_att.json similarity index 100% rename from test/data/invalid/json/1_invalid_intrinsic_get_att.json rename to testData/invalid/json/1_invalid_intrinsic_get_att.json diff --git a/test/data/invalid/json/1_invalid_intrinsic_mappings.json b/testData/invalid/json/1_invalid_intrinsic_mappings.json similarity index 100% rename from test/data/invalid/json/1_invalid_intrinsic_mappings.json rename to testData/invalid/json/1_invalid_intrinsic_mappings.json diff --git a/test/data/invalid/json/1_invalid_parameter_reference.json b/testData/invalid/json/1_invalid_parameter_reference.json similarity index 100% rename from test/data/invalid/json/1_invalid_parameter_reference.json rename to testData/invalid/json/1_invalid_parameter_reference.json diff --git a/test/data/invalid/json/1_invalid_parameter_type.json b/testData/invalid/json/1_invalid_parameter_type.json similarity index 100% rename from test/data/invalid/json/1_invalid_parameter_type.json rename to testData/invalid/json/1_invalid_parameter_type.json diff --git a/test/data/invalid/json/1_invalid_resource_reference.json b/testData/invalid/json/1_invalid_resource_reference.json similarity index 100% rename from test/data/invalid/json/1_invalid_resource_reference.json rename to testData/invalid/json/1_invalid_resource_reference.json diff --git a/test/data/invalid/json/1_invalid_resource_type.json b/testData/invalid/json/1_invalid_resource_type.json similarity index 100% rename from test/data/invalid/json/1_invalid_resource_type.json rename to testData/invalid/json/1_invalid_resource_type.json diff --git a/test/data/invalid/json/1_missing_parameter_type.json b/testData/invalid/json/1_missing_parameter_type.json similarity index 100% rename from test/data/invalid/json/1_missing_parameter_type.json rename to testData/invalid/json/1_missing_parameter_type.json diff --git a/test/data/invalid/json/1_ref_not_string_param.json b/testData/invalid/json/1_ref_not_string_param.json similarity index 100% rename from test/data/invalid/json/1_ref_not_string_param.json rename to testData/invalid/json/1_ref_not_string_param.json diff --git a/test/data/invalid/json/1_warning_ref_get_azs_parameter.json b/testData/invalid/json/1_warning_ref_get_azs_parameter.json similarity index 100% rename from test/data/invalid/json/1_warning_ref_get_azs_parameter.json rename to testData/invalid/json/1_warning_ref_get_azs_parameter.json diff --git a/test/data/invalid/json/2_invalid_intrinsic_join.json b/testData/invalid/json/2_invalid_intrinsic_join.json similarity index 100% rename from test/data/invalid/json/2_invalid_intrinsic_join.json rename to testData/invalid/json/2_invalid_intrinsic_join.json diff --git a/test/data/invalid/json/empty_resources.json b/testData/invalid/json/empty_resources.json similarity index 100% rename from test/data/invalid/json/empty_resources.json rename to testData/invalid/json/empty_resources.json diff --git a/test/data/invalid/json/invalid_json.json b/testData/invalid/json/invalid_json.json similarity index 100% rename from test/data/invalid/json/invalid_json.json rename to testData/invalid/json/invalid_json.json diff --git a/test/data/invalid/json/no_resources.json b/testData/invalid/json/no_resources.json similarity index 100% rename from test/data/invalid/json/no_resources.json rename to testData/invalid/json/no_resources.json diff --git a/test/data/invalid/yaml/invalid_arn.yaml b/testData/invalid/yaml/invalid_arn.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_arn.yaml rename to testData/invalid/yaml/invalid_arn.yaml diff --git a/test/data/invalid/yaml/invalid_boolean_type.yaml b/testData/invalid/yaml/invalid_boolean_type.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_boolean_type.yaml rename to testData/invalid/yaml/invalid_boolean_type.yaml diff --git a/test/data/invalid/yaml/invalid_condition_value.yaml b/testData/invalid/yaml/invalid_condition_value.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_condition_value.yaml rename to testData/invalid/yaml/invalid_condition_value.yaml diff --git a/test/data/invalid/yaml/invalid_condition_value_type.yaml b/testData/invalid/yaml/invalid_condition_value_type.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_condition_value_type.yaml rename to testData/invalid/yaml/invalid_condition_value_type.yaml diff --git a/test/data/invalid/yaml/invalid_ec2_tags_property_name.yaml b/testData/invalid/yaml/invalid_ec2_tags_property_name.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_ec2_tags_property_name.yaml rename to testData/invalid/yaml/invalid_ec2_tags_property_name.yaml diff --git a/test/data/invalid/yaml/invalid_if_statement_arguments.yaml b/testData/invalid/yaml/invalid_if_statement_arguments.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_if_statement_arguments.yaml rename to testData/invalid/yaml/invalid_if_statement_arguments.yaml diff --git a/test/data/invalid/yaml/invalid_if_statement_condition.yaml b/testData/invalid/yaml/invalid_if_statement_condition.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_if_statement_condition.yaml rename to testData/invalid/yaml/invalid_if_statement_condition.yaml diff --git a/test/data/invalid/yaml/invalid_import_value_intrinsic_function.yaml b/testData/invalid/yaml/invalid_import_value_intrinsic_function.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_import_value_intrinsic_function.yaml rename to testData/invalid/yaml/invalid_import_value_intrinsic_function.yaml diff --git a/test/data/invalid/yaml/invalid_maps.yaml b/testData/invalid/yaml/invalid_maps.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_maps.yaml rename to testData/invalid/yaml/invalid_maps.yaml diff --git a/test/data/invalid/yaml/invalid_missing_nested_property.yaml b/testData/invalid/yaml/invalid_missing_nested_property.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_missing_nested_property.yaml rename to testData/invalid/yaml/invalid_missing_nested_property.yaml diff --git a/test/data/invalid/yaml/invalid_property_type_string.yaml b/testData/invalid/yaml/invalid_property_type_string.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_property_type_string.yaml rename to testData/invalid/yaml/invalid_property_type_string.yaml diff --git a/test/data/invalid/yaml/invalid_propertytype_property_name.yaml b/testData/invalid/yaml/invalid_propertytype_property_name.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_propertytype_property_name.yaml rename to testData/invalid/yaml/invalid_propertytype_property_name.yaml diff --git a/test/data/invalid/yaml/invalid_required_propertytype_prop_missing.yaml b/testData/invalid/yaml/invalid_required_propertytype_prop_missing.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_required_propertytype_prop_missing.yaml rename to testData/invalid/yaml/invalid_required_propertytype_prop_missing.yaml diff --git a/test/data/invalid/yaml/invalid_required_resourcetype_prop_missing.yaml b/testData/invalid/yaml/invalid_required_resourcetype_prop_missing.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_required_resourcetype_prop_missing.yaml rename to testData/invalid/yaml/invalid_required_resourcetype_prop_missing.yaml diff --git a/test/data/invalid/yaml/invalid_resourcetype_property_name.yaml b/testData/invalid/yaml/invalid_resourcetype_property_name.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_resourcetype_property_name.yaml rename to testData/invalid/yaml/invalid_resourcetype_property_name.yaml diff --git a/test/data/invalid/yaml/invalid_sub_getatt.yaml b/testData/invalid/yaml/invalid_sub_getatt.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_sub_getatt.yaml rename to testData/invalid/yaml/invalid_sub_getatt.yaml diff --git a/test/data/invalid/yaml/invalid_sub_reference.yaml b/testData/invalid/yaml/invalid_sub_reference.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_sub_reference.yaml rename to testData/invalid/yaml/invalid_sub_reference.yaml diff --git a/test/data/invalid/yaml/invalid_template_format_version.yaml b/testData/invalid/yaml/invalid_template_format_version.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_template_format_version.yaml rename to testData/invalid/yaml/invalid_template_format_version.yaml diff --git a/test/data/invalid/yaml/invalid_yaml.yaml b/testData/invalid/yaml/invalid_yaml.yaml similarity index 100% rename from test/data/invalid/yaml/invalid_yaml.yaml rename to testData/invalid/yaml/invalid_yaml.yaml diff --git a/test/data/invalid/yaml/issue-39-output-reference-invalid.yaml b/testData/invalid/yaml/issue-39-output-reference-invalid.yaml similarity index 100% rename from test/data/invalid/yaml/issue-39-output-reference-invalid.yaml rename to testData/invalid/yaml/issue-39-output-reference-invalid.yaml diff --git a/test/data/valid/json/1.json b/testData/valid/json/1.json similarity index 100% rename from test/data/valid/json/1.json rename to testData/valid/json/1.json diff --git a/test/data/valid/json/2.json b/testData/valid/json/2.json similarity index 100% rename from test/data/valid/json/2.json rename to testData/valid/json/2.json diff --git a/test/data/valid/json/3.json b/testData/valid/json/3.json similarity index 100% rename from test/data/valid/json/3.json rename to testData/valid/json/3.json diff --git a/test/data/valid/json/4.json b/testData/valid/json/4.json similarity index 100% rename from test/data/valid/json/4.json rename to testData/valid/json/4.json diff --git a/test/data/valid/yaml/1.yaml b/testData/valid/yaml/1.yaml similarity index 100% rename from test/data/valid/yaml/1.yaml rename to testData/valid/yaml/1.yaml diff --git a/test/data/valid/yaml/2.yaml b/testData/valid/yaml/2.yaml similarity index 100% rename from test/data/valid/yaml/2.yaml rename to testData/valid/yaml/2.yaml diff --git a/test/data/valid/yaml/issue-24.yaml b/testData/valid/yaml/issue-24.yaml similarity index 100% rename from test/data/valid/yaml/issue-24.yaml rename to testData/valid/yaml/issue-24.yaml diff --git a/test/data/valid/yaml/issue-27-numeric-properties.yaml b/testData/valid/yaml/issue-27-numeric-properties.yaml similarity index 100% rename from test/data/valid/yaml/issue-27-numeric-properties.yaml rename to testData/valid/yaml/issue-27-numeric-properties.yaml diff --git a/test/data/valid/yaml/issue-28-custom-resource.yaml b/testData/valid/yaml/issue-28-custom-resource.yaml similarity index 100% rename from test/data/valid/yaml/issue-28-custom-resource.yaml rename to testData/valid/yaml/issue-28-custom-resource.yaml diff --git a/test/data/valid/yaml/issue-39-output-reference-check.yaml b/testData/valid/yaml/issue-39-output-reference-check.yaml similarity index 100% rename from test/data/valid/yaml/issue-39-output-reference-check.yaml rename to testData/valid/yaml/issue-39-output-reference-check.yaml diff --git a/test/data/valid/yaml/issue-42-managed-policy-name.yaml b/testData/valid/yaml/issue-42-managed-policy-name.yaml similarity index 100% rename from test/data/valid/yaml/issue-42-managed-policy-name.yaml rename to testData/valid/yaml/issue-42-managed-policy-name.yaml diff --git a/test/data/valid/yaml/issue-44-database-endpoint.yaml b/testData/valid/yaml/issue-44-database-endpoint.yaml similarity index 100% rename from test/data/valid/yaml/issue-44-database-endpoint.yaml rename to testData/valid/yaml/issue-44-database-endpoint.yaml diff --git a/test/data/valid/yaml/issue-61.yaml b/testData/valid/yaml/issue-61.yaml similarity index 100% rename from test/data/valid/yaml/issue-61.yaml rename to testData/valid/yaml/issue-61.yaml diff --git a/test/data/valid/yaml/maps.yaml b/testData/valid/yaml/maps.yaml similarity index 100% rename from test/data/valid/yaml/maps.yaml rename to testData/valid/yaml/maps.yaml diff --git a/test/data/valid/yaml/parameters-allowed-values.yaml b/testData/valid/yaml/parameters-allowed-values.yaml similarity index 100% rename from test/data/valid/yaml/parameters-allowed-values.yaml rename to testData/valid/yaml/parameters-allowed-values.yaml diff --git a/test/data/valid/yaml/pseudo-parameters.yaml b/testData/valid/yaml/pseudo-parameters.yaml similarity index 100% rename from test/data/valid/yaml/pseudo-parameters.yaml rename to testData/valid/yaml/pseudo-parameters.yaml diff --git a/test/data/valid/yaml/pseudo-w-parameter.yaml b/testData/valid/yaml/pseudo-w-parameter.yaml similarity index 100% rename from test/data/valid/yaml/pseudo-w-parameter.yaml rename to testData/valid/yaml/pseudo-w-parameter.yaml diff --git a/test/data/valid/yaml/valid_import_value.yaml b/testData/valid/yaml/valid_import_value.yaml similarity index 100% rename from test/data/valid/yaml/valid_import_value.yaml rename to testData/valid/yaml/valid_import_value.yaml diff --git a/test/data/valid/yaml/valid_minus_one_as_string.yaml b/testData/valid/yaml/valid_minus_one_as_string.yaml similarity index 100% rename from test/data/valid/yaml/valid_minus_one_as_string.yaml rename to testData/valid/yaml/valid_minus_one_as_string.yaml diff --git a/test/data/valid/yaml/valid_missing_template_format_version.yaml b/testData/valid/yaml/valid_missing_template_format_version.yaml similarity index 100% rename from test/data/valid/yaml/valid_missing_template_format_version.yaml rename to testData/valid/yaml/valid_missing_template_format_version.yaml diff --git a/test/data/valid/yaml/valid_shorthand_sub.yaml b/testData/valid/yaml/valid_shorthand_sub.yaml similarity index 100% rename from test/data/valid/yaml/valid_shorthand_sub.yaml rename to testData/valid/yaml/valid_shorthand_sub.yaml diff --git a/test/data/valid/yaml/valid_sub_operations.yaml b/testData/valid/yaml/valid_sub_operations.yaml similarity index 100% rename from test/data/valid/yaml/valid_sub_operations.yaml rename to testData/valid/yaml/valid_sub_operations.yaml diff --git a/test/data/valid/yaml/valid_unquoted_template_version.yaml b/testData/valid/yaml/valid_unquoted_template_version.yaml similarity index 100% rename from test/data/valid/yaml/valid_unquoted_template_version.yaml rename to testData/valid/yaml/valid_unquoted_template_version.yaml diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c6e376b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,53 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation: */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./lib", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + + /* Source Map Options */ + // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + } +} \ No newline at end of file