-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
fields.ts
510 lines (463 loc) · 17.5 KB
/
fields.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
import { Token, IResolvable, JsonNull } from '@aws-cdk/core';
import { findReferencedPaths, jsonPathString, JsonPathToken, renderObject, renderInExpression, jsonPathFromAny } from './private/json-path';
/**
* Extract a field from the State Machine data or context
* that gets passed around between states
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-paths.html
*/
export class JsonPath {
/**
* Special string value to discard state input, output or result.
*/
public static readonly DISCARD = Token.asString(JsonNull.INSTANCE, { displayHint: 'DISCARD (JSON `null`)' });
/**
* Instead of using a literal string, get the value from a JSON path
*/
public static stringAt(path: string): string {
validateJsonPath(path);
return new JsonPathToken(path).toString();
}
/**
* Instead of using a literal string list, get the value from a JSON path
*/
public static listAt(path: string): string[] {
// does not apply to task context
validateDataPath(path);
return Token.asList(new JsonPathToken(path));
}
/**
* Instead of using a literal number, get the value from a JSON path
*/
public static numberAt(path: string): number {
validateJsonPath(path);
return Token.asNumber(new JsonPathToken(path));
}
/**
* Reference a complete (complex) object in a JSON path location
*/
public static objectAt(path: string): IResolvable {
validateJsonPath(path);
return new JsonPathToken(path);
}
/**
* Use the entire data structure
*
* Will be an object at invocation time, but is represented in the CDK
* application as a string.
*/
public static get entirePayload(): string {
return new JsonPathToken('$').toString();
}
/**
* Determines if the indicated string is an encoded JSON path
*
* @param value string to be evaluated
*/
public static isEncodedJsonPath(value: string): boolean {
return !!jsonPathString(value);
}
/**
* Return the Task Token field
*
* External actions will need this token to report step completion
* back to StepFunctions using the `SendTaskSuccess` or `SendTaskFailure`
* calls.
*/
public static get taskToken(): string {
return new JsonPathToken('$$.Task.Token').toString();
}
/**
* Use the entire context data structure
*
* Will be an object at invocation time, but is represented in the CDK
* application as a string.
*/
public static get entireContext(): string {
return new JsonPathToken('$$').toString();
}
/**
* Make an intrinsic States.Array expression
*
* Combine any number of string literals or JsonPath expressions into an array.
*
* Use this function if the value of an array element directly has to come
* from a JSON Path expression (either the State object or the Context object).
*
* If the array contains object literals whose values come from a JSON path
* expression, you do not need to use this function.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static array(...values: string[]): string {
return new JsonPathToken(`States.Array(${values.map(renderInExpression).join(', ')})`).toString();
}
/**
* Make an intrinsic States.ArrayPartition expression
*
* Use this function to partition a large array. You can also use this intrinsic to slice the data and then send the payload in smaller chunks.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static arrayPartition(array: any, chunkSize: number): string {
return new JsonPathToken(`States.ArrayPartition(${[array, chunkSize].map(renderInExpression).join(', ')})`).toString();
}
/**
* Make an intrinsic States.ArrayContains expression
*
* Use this function to determine if a specific value is present in an array. For example, you can use this function to detect if there was an error in a Map state iteration.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static arrayContains(array: any, value: any): string {
return new JsonPathToken(`States.ArrayContains(${[array, value].map(renderInExpression).join(', ')})`).toString();
}
/**
* Make an intrinsic States.ArrayRange expression
*
* Use this function to create a new array containing a specific range of elements. The new array can contain up to 1000 elements.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static arrayRange(start: number, end: number, step: number): string {
return new JsonPathToken(`States.ArrayRange(${[start, end, step].map(renderInExpression).join(', ')})`).toString();
}
/**
* Make an intrinsic States.ArrayGetItem expression
*
* Use this function to get a specified index's value in an array.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static arrayGetItem(array: any, index: number): string {
return new JsonPathToken(`States.ArrayGetItem(${[array, index].map(renderInExpression).join(', ')})`).toString();
}
/**
* Make an intrinsic States.ArrayLength expression
*
* Use this function to get the length of an array.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static arrayLength(array: any): string {
return new JsonPathToken(`States.ArrayLength(${renderInExpression(array)})`).toString();
}
/**
* Make an intrinsic States.ArrayUnique expression
*
* Use this function to get the length of an array.
* Use this function to remove duplicate values from an array and returns an array containing only unique elements. This function takes an array, which can be unsorted, as its sole argument.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static arrayUnique(array: any): string {
return new JsonPathToken(`States.ArrayUnique(${renderInExpression(array)})`).toString();
}
/**
* Make an intrinsic States.Base64Encode expression
*
* Use this function to encode data based on MIME Base64 encoding scheme. You can use this function to pass data to other AWS services without using an AWS Lambda function.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static base64Encode(input: string): string {
return new JsonPathToken(`States.Base64Encode(${renderInExpression(input)})`).toString();
}
/**
* Make an intrinsic States.Base64Decode expression
*
* Use this function to decode data based on MIME Base64 decoding scheme. You can use this function to pass data to other AWS services without using a Lambda function.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static base64Decode(base64: string): string {
return new JsonPathToken(`States.Base64Decode(${renderInExpression(base64)})`).toString();
}
/**
* Make an intrinsic States.Hash expression
*
* Use this function to calculate the hash value of a given input. You can use this function to pass data to other AWS services without using a Lambda function.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static hash(data: any, algorithm: string): string {
return new JsonPathToken(`States.Hash(${[data, algorithm].map(renderInExpression).join(', ')})`).toString();
}
/**
* Make an intrinsic States.JsonMerge expression
*
* Use this function to merge two JSON objects into a single object.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static jsonMerge(value1: any, value2: any): string {
return new JsonPathToken(`States.JsonMerge(${[value1, value2].map(renderInExpression).join(', ')}, false)`).toString();
}
/**
* Make an intrinsic States.MathRandom expression
*
* Use this function to return a random number between the specified start and end number. For example, you can use this function to distribute a specific task between two or more resources.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static mathRandom(start: number, end: number): string {
return new JsonPathToken(`States.MathRandom(${[start, end].map(renderInExpression).join(', ')})`).toString();
}
/**
* Make an intrinsic States.MathAdd expression
*
* Use this function to return the sum of two numbers. For example, you can use this function to increment values inside a loop without invoking a Lambda function.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static mathAdd(num1: number, num2: number): string {
return new JsonPathToken(`States.MathAdd(${[num1, num2].map(renderInExpression).join(', ')})`).toString();
}
/**
* Make an intrinsic States.StringSplit expression
*
* Use this function to split a string into an array of values. This function takes two arguments.The first argument is a string and the second argument is the delimiting character that the function will use to divide the string.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static stringSplit(inputString: string, splitter: string): string {
return new JsonPathToken(`States.StringSplit(${[inputString, splitter].map(renderInExpression).join(', ')})`).toString();
}
/**
* Make an intrinsic States.UUID expression
*
* Use this function to return a version 4 universally unique identifier (v4 UUID) generated using random numbers. For example, you can use this function to call other AWS services or resources that need a UUID parameter or insert items in a DynamoDB table.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static uuid(): string {
return new JsonPathToken('States.UUID()').toString();
}
/**
* Make an intrinsic States.Format expression
*
* This can be used to embed JSON Path variables inside a format string.
*
* For example:
*
* ```ts
* sfn.JsonPath.format('Hello, my name is {}.', sfn.JsonPath.stringAt('$.name'))
* ```
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static format(formatString: string, ...values: string[]): string {
const allArgs = [formatString, ...values];
return new JsonPathToken(`States.Format(${allArgs.map(renderInExpression).join(', ')})`).toString();
}
/**
* Make an intrinsic States.StringToJson expression
*
* During the execution of the Step Functions state machine, parse the given
* argument as JSON into its object form.
*
* For example:
*
* ```ts
* sfn.JsonPath.stringToJson(sfn.JsonPath.stringAt('$.someJsonBody'))
* ```
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static stringToJson(jsonString: string): IResolvable {
return new JsonPathToken(`States.StringToJson(${renderInExpression(jsonString)})`);
}
/**
* Make an intrinsic States.JsonToString expression
*
* During the execution of the Step Functions state machine, encode the
* given object into a JSON string.
*
* For example:
*
* ```ts
* sfn.JsonPath.jsonToString(sfn.JsonPath.objectAt('$.someObject'))
* ```
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html
*/
public static jsonToString(value: any): string {
const path = jsonPathFromAny(value);
if (!path) {
throw new Error('Argument to JsonPath.jsonToString() must be a JsonPath object');
}
return new JsonPathToken(`States.JsonToString(${path})`).toString();
}
private constructor() {}
}
/**
* Extract a field from the State Machine data that gets passed around between states
*
* @deprecated replaced by `JsonPath`
*/
export class Data {
/**
* Instead of using a literal string, get the value from a JSON path
*/
public static stringAt(path: string): string {
validateDataPath(path);
return new JsonPathToken(path).toString();
}
/**
* Instead of using a literal string list, get the value from a JSON path
*/
public static listAt(path: string): string[] {
validateDataPath(path);
return Token.asList(new JsonPathToken(path));
}
/**
* Instead of using a literal number, get the value from a JSON path
*/
public static numberAt(path: string): number {
validateDataPath(path);
return Token.asNumber(new JsonPathToken(path));
}
/**
* Use the entire data structure
*
* Will be an object at invocation time, but is represented in the CDK
* application as a string.
*/
public static get entirePayload(): string {
return new JsonPathToken('$').toString();
}
/**
* Determines if the indicated string is an encoded JSON path
*
* @param value string to be evaluated
*/
public static isJsonPathString(value: string): boolean {
return !!jsonPathString(value);
}
private constructor() {}
}
/**
* Extract a field from the State Machine Context data
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html#wait-token-contextobject
*
* @deprecated replaced by `JsonPath`
*/
export class Context {
/**
* Instead of using a literal string, get the value from a JSON path
*/
public static stringAt(path: string): string {
validateContextPath(path);
return new JsonPathToken(path).toString();
}
/**
* Instead of using a literal number, get the value from a JSON path
*/
public static numberAt(path: string): number {
validateContextPath(path);
return Token.asNumber(new JsonPathToken(path));
}
/**
* Return the Task Token field
*
* External actions will need this token to report step completion
* back to StepFunctions using the `SendTaskSuccess` or `SendTaskFailure`
* calls.
*/
public static get taskToken(): string {
return new JsonPathToken('$$.Task.Token').toString();
}
/**
* Use the entire context data structure
*
* Will be an object at invocation time, but is represented in the CDK
* application as a string.
*/
public static get entireContext(): string {
return new JsonPathToken('$$').toString();
}
private constructor() {}
}
/**
* Helper functions to work with structures containing fields
*/
export class FieldUtils {
/**
* Render a JSON structure containing fields to the right StepFunctions structure
*/
public static renderObject(obj?: { [key: string]: any }): { [key: string]: any } | undefined {
return renderObject(obj);
}
/**
* Return all JSON paths used in the given structure
*/
public static findReferencedPaths(obj?: { [key: string]: any }): string[] {
return Array.from(findReferencedPaths(obj)).sort();
}
/**
* Returns whether the given task structure contains the TaskToken field anywhere
*
* The field is considered included if the field itself or one of its containing
* fields occurs anywhere in the payload.
*/
public static containsTaskToken(obj?: { [key: string]: any }): boolean {
const paths = findReferencedPaths(obj);
return paths.has('$$.Task.Token') || paths.has('$$.Task') || paths.has('$$');
}
private constructor() {}
}
function validateJsonPath(path: string) {
const intrinsicFunctionNames = [
// Intrinsics for arrays
'Array',
'ArrayPartition',
'ArrayContains',
'ArrayRange',
'ArrayGetItem',
'ArrayLength',
'ArrayUnique',
// Intrinsics for data encoding and decoding
'Base64Encode',
'Base64Decode',
// Intrinsic for hash calculation
'Hash',
// Intrinsics for JSON data manipulation
'JsonMerge',
'StringToJson',
'JsonToString',
// Intrinsics for Math operations
'MathRandom',
'MathAdd',
// Intrinsic for String operation
'StringSplit',
// Intrinsic for unique identifier generation
'UUID',
// Intrinsic for generic operation
'Format',
];
const intrinsicFunctionFullNames = intrinsicFunctionNames.map((fn) => `States.${fn}`);
if (path !== '$'
&& !path.startsWith('$.')
&& path !== '$$'
&& !path.startsWith('$$.')
&& !path.startsWith('$[')
&& intrinsicFunctionFullNames.every(fn => !path.startsWith(fn))
) {
const lastItem = intrinsicFunctionFullNames.pop();
const intrinsicFunctionsStr = intrinsicFunctionFullNames.join(', ') + ', or ' + lastItem;
throw new Error(`JSON path values must be exactly '$', '$$', start with '$.', start with '$$.', start with '$[', or start with an intrinsic function: ${intrinsicFunctionsStr}. Received: ${path}`);
}
}
function validateDataPath(path: string) {
if (path !== '$'
&& !path.startsWith('$[')
&& !path.startsWith('$.')) {
throw new Error("Data JSON path values must either be exactly equal to '$', start with '$[' or start with '$.'");
}
}
function validateContextPath(path: string) {
if (path !== '$$' && !path.startsWith('$$.')) {
throw new Error("Context JSON path values must either be exactly equal to '$$' or start with '$$.'");
}
}