Skip to content

Commit

Permalink
fix(sfn): can't override toStateJson() from other languages
Browse files Browse the repository at this point in the history
If any part of a state's JSON representation is `null`, that value will
be replaced by `undefined` when jsii sends data to the other language,
resulting in a change of semantics.

Multi-language APIs cannot differentiate between `null` and `undefined`
as non-JS languages typically fail to distinguish between them... In
order to address that, a `JsonNull` value was added which serializes to
`null` (via Javascript's standard `toJSON` method), which must be used
in such cases where `null` may need to cross the language boundary.

The `JsonPath.DISCARD` value is now a string-token representation of the
`JsonNull` instance.

Fixes #14639
  • Loading branch information
RomainMuller committed Mar 13, 2023
1 parent 65693b1 commit 521e154
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 4 deletions.
6 changes: 3 additions & 3 deletions packages/@aws-cdk/aws-stepfunctions/lib/fields.ts
@@ -1,4 +1,4 @@
import { Token, IResolvable } from '@aws-cdk/core';
import { Token, IResolvable, JsonNull } from '@aws-cdk/core';
import { findReferencedPaths, jsonPathString, JsonPathToken, renderObject, renderInExpression, jsonPathFromAny } from './private/json-path';

/**
Expand All @@ -9,9 +9,9 @@ import { findReferencedPaths, jsonPathString, JsonPathToken, renderObject, rende
*/
export class JsonPath {
/**
* Special string value to discard state input, output or result
* Special string value to discard state input, output or result.
*/
public static readonly DISCARD = 'DISCARD';
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
Expand Down
3 changes: 2 additions & 1 deletion packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts
@@ -1,3 +1,4 @@
import { Token } from '@aws-cdk/core';
import { IConstruct, Construct, Node } from 'constructs';
import { Condition } from '../condition';
import { FieldUtils, JsonPath } from '../fields';
Expand Down Expand Up @@ -579,7 +580,7 @@ export function renderJsonPath(jsonPath?: string): undefined | null | string {
if (jsonPath === undefined) { return undefined; }
if (jsonPath === JsonPath.DISCARD) { return null; }

if (!jsonPath.startsWith('$')) {
if (!Token.isUnresolved(jsonPath) && !jsonPath.startsWith('$')) {
throw new Error(`Expected JSON path to start with '$', got: ${jsonPath}`);
}
return jsonPath;
Expand Down
19 changes: 19 additions & 0 deletions packages/@aws-cdk/core/lib/token.ts
Expand Up @@ -232,6 +232,25 @@ export class Tokenization {
}
}

/**
* An object which serializes to the JSON `null` literal, and which can safely
* be passed across languages where `undefined` and `null` are not different.
*/
export class JsonNull {
/** The canonical instance of `JsonNull`. */
public static readonly INSTANCE = new JsonNull();

private constructor() { }

public toJSON(): any {
return null;
}

public toString(): string {
return 'null';
}
}

/**
* Options for the 'reverse()' operation
*/
Expand Down

0 comments on commit 521e154

Please sign in to comment.