-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathindex.js
executable file
·150 lines (130 loc) · 4.35 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/**
* Parser and schema for CloudFormation YAML template tags.
*
* There are some existing modules out there:
* https://github.com/yyolk/cloudformation-js-yaml-schema
* https://github.com/KoharaKazuya/js-yaml-schema-cfn
* But both are poorly documented, with insufficient tests, and don't fully work.
*
* This implementation is based on the official AWS python client:
* https://github.com/aws/aws-cli/blob/develop/awscli/customizations/cloudformation/yamlhelper.py
*/
"use strict";
const jsYaml = require('js-yaml');
/**
* Split a string on the given separator just once, returning an array of two parts, or null.
*/
function splitOne(str, sep) {
let index = str.indexOf(sep);
return index < 0 ? null : [str.slice(0, index), str.slice(index + sep.length)];
}
/**
* Returns true if obj is a representation of a CloudFormation intrinsic, i.e. an object with a
* single property at key keyName.
*/
function checkType(obj, keyName) {
return obj && typeof obj === 'object' && Object.keys(obj).length === 1 &&
obj.hasOwnProperty(keyName);
}
const overrides = {
// ShortHand notation for !GetAtt accepts Resource.Attribute format while the standard notation
// is to use an array [Resource, Attribute]. Convert shorthand to standard format.
GetAtt: {
parse: data => typeof data === 'string' ? splitOne(data, '.') : data,
dump: data => data.join('.')
}
};
function applyOverrides(data, tag, method) {
return overrides[tag] ? overrides[tag][method](data) : data;
}
/**
* Generic tag-creating helper. For the given name of the form 'Fn::Something' (or just
* 'Something'), creates a js-yaml Type object that can parse and dump this type. It creates it
* for all types of values, for simplicity and because that's how the official Python version
* works.
*/
function makeTagTypes(name) {
const parts = splitOne(name, '::');
const tag = parts ? parts[1] : name;
// Translate in the same way for all types, to match Python's generic translation.
return ['scalar', 'sequence', 'mapping'].map(kind => new jsYaml.Type('!' + tag, {
kind: kind,
construct: data => ({[name]: applyOverrides(data, tag, 'parse')}),
predicate: obj => checkType(obj, name),
represent: obj => applyOverrides(obj[name], tag, 'dump'),
}));
}
/**
* This list is from
* http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
* Note that the Python version handles ANY tag that starts with ! in the same way (translating it
* to Fn:: prefix, but js-yaml requires listing tags explicitly.
*/
const supportedFunctions = [
'Fn::Base64',
'Fn::Cidr',
'Fn::FindInMap',
'Fn::GetAtt',
'Fn::GetAZs',
'Fn::ImportValue',
'Fn::Join',
'Fn::Select',
'Fn::Split',
'Fn::Sub',
'Fn::Transform',
'Ref',
'Condition',
'Fn::And',
'Fn::Equals',
'Fn::If',
'Fn::Not',
'Fn::Or'
];
/**
* This list is from
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-rules.html
* This is not comprehensive, as it doesn't include
* - Supported Functions - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-rules.html#supported-rule-functions
* - Supported Attributes - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-rules.html#rules-parameter-attributes
* Note that the Python version handles ANY tag that starts with ! in the same way (translating it
* to Fn:: prefix, but js-yaml requires listing tags explicitly.
*/
const ruleFunctions = [
'Fn::And',
'Fn::Contains',
'Fn::EachMemberEquals',
'Fn::EachMemberIn',
'Fn::Equals',
'Fn::Not',
'Fn::Or',
'Fn::RefAll',
'Fn::ValueOf',
'Fn::ValueOfAll'
]
let allTagTypes = []
let allFunctions = [...supportedFunctions, ...ruleFunctions]
for (let name of allFunctions) {
allTagTypes.push(...makeTagTypes(name));
}
/**
* The actual js-yaml schema, extending the DEFAULT_SAFE_SCHEMA.
*/
const schema = jsYaml.CORE_SCHEMA.extend({
implicit: [],
explicit: allTagTypes,
});
exports.schema = schema;
/**
* Convenience function to parse the given yaml input.
*/
function yamlParse(input) {
return jsYaml.load(input, { schema: schema });
}
exports.yamlParse = yamlParse;
/**
* Convenience function to serialize the given object to Yaml.
*/
function yamlDump(input) {
return jsYaml.dump(input, { schema: schema });
}
exports.yamlDump = yamlDump;