-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
util.ts
128 lines (107 loc) · 4.23 KB
/
util.ts
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
import { format as formatUrl } from 'url';
import * as jsonSchema from './json-schema';
export const ALL_METHODS = ['OPTIONS', 'GET', 'PUT', 'POST', 'DELETE', 'PATCH', 'HEAD'];
const ALLOWED_METHODS = ['ANY', ...ALL_METHODS];
export function validateHttpMethod(method: string, messagePrefix: string = '') {
if (!ALLOWED_METHODS.includes(method)) {
throw new Error(`${messagePrefix}Invalid HTTP method "${method}". Allowed methods: ${ALLOWED_METHODS.join(',')}`);
}
}
export function parseMethodOptionsPath(originalPath: string): { resourcePath: string, httpMethod: string } {
if (!originalPath.startsWith('/')) {
throw new Error(`Method options path must start with '/': ${originalPath}`);
}
const path = originalPath.slice(1); // trim trailing '/'
const components = path.split('/');
if (components.length < 2) {
throw new Error(`Method options path must include at least two components: /{resource}/{method} (i.e. /foo/bar/GET): ${path}`);
}
const httpMethod = components.pop()!.toUpperCase(); // last component is an HTTP method
if (httpMethod !== '*') {
validateHttpMethod(httpMethod, `${originalPath}: `);
}
let resourcePath = '/~1' + components.join('~1');
if (components.length === 1 && components[0] === '*') {
resourcePath = '/*';
} else if (components.length === 1 && components[0] === '') {
resourcePath = '/';
}
return {
httpMethod,
resourcePath,
};
}
export function parseAwsApiCall(path?: string, action?: string, actionParams?: { [key: string]: string }): { apiType: string, apiValue: string } {
if (actionParams && !action) {
throw new Error('"actionParams" requires that "action" will be set');
}
if (path && action) {
throw new Error(`"path" and "action" are mutually exclusive (path="${path}", action="${action}")`);
}
if (path) {
return {
apiType: 'path',
apiValue: path,
};
}
if (action) {
if (actionParams) {
action += '&' + formatUrl({ query: actionParams }).slice(1);
}
return {
apiType: 'action',
apiValue: action,
};
}
throw new Error('Either "path" or "action" are required');
}
export function validateInteger(property: number | undefined, messagePrefix: string) {
if (property && !Number.isInteger(property)) {
throw new Error(`${messagePrefix} should be an integer`);
}
}
export function validateDouble(property: number | undefined, messagePrefix: string) {
if (property && isNaN(property) && isNaN(parseFloat(property.toString()))) {
throw new Error(`${messagePrefix} should be an double`);
}
}
export class JsonSchemaMapper {
/**
* Transforms naming of some properties to prefix with a $, where needed
* according to the JSON schema spec
* @param schema The JsonSchema object to transform for CloudFormation output
*/
public static toCfnJsonSchema(schema: jsonSchema.JsonSchema): any {
const result = JsonSchemaMapper._toCfnJsonSchema(schema);
if (! ('$schema' in result)) {
result.$schema = jsonSchema.JsonSchemaVersion.DRAFT4;
}
return result;
}
private static readonly SchemaPropsWithPrefix: { [key: string]: string } = {
schema: '$schema',
ref: '$ref',
};
// The value indicates whether direct children should be key-mapped.
private static readonly SchemaPropsWithUserDefinedChildren: { [key: string]: boolean } = {
definitions: true,
properties: true,
patternProperties: true,
dependencies: true,
};
private static _toCfnJsonSchema(schema: any, preserveKeys = false): any {
if (schema == null || typeof schema !== 'object') {
return schema;
}
if (Array.isArray(schema)) {
return schema.map(entry => JsonSchemaMapper._toCfnJsonSchema(entry));
}
return Object.assign({}, ...Object.entries(schema).map(([key, value]) => {
const mapKey = !preserveKeys && (key in JsonSchemaMapper.SchemaPropsWithPrefix);
const newKey = mapKey ? JsonSchemaMapper.SchemaPropsWithPrefix[key] : key;
// If keys were preserved, don't consider SchemaPropsWithUserDefinedChildren for those keys (they are user-defined!)
const newValue = JsonSchemaMapper._toCfnJsonSchema(value, !preserveKeys && JsonSchemaMapper.SchemaPropsWithUserDefinedChildren[key]);
return { [newKey]: newValue };
}));
}
}