Skip to content

feat(dynamodb): complete the helper module and add toSubscriptionFilter - #300

Merged
bentsku merged 1 commit into
mainfrom
aws-1740-appsync-utils-dynamodb-module
Sep 3, 2026
Merged

feat(dynamodb): complete the helper module and add toSubscriptionFilter#300
bentsku merged 1 commit into
mainfrom
aws-1740-appsync-utils-dynamodb-module

Conversation

@bentsku

@bentsku bentsku commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Problem

The @aws-appsync/utils/dynamodb module implemented get, put, remove, scan, update and operations, and those dropped most of their arguments. A filter or projection handed to scan was passed through raw rather than turned into an expression, and condition, consistentRead, projection and _version were discarded everywhere else.

The update expression builder was also wrong for anything but a single operation: it joined clauses with a bare comma and repeated the SET keyword per clause. AWS emits one keyword per group, SET before REMOVE, with aliases numbered in the order the clauses come out rather than the order they were written. The existing snapshots never caught it because every one of them used a single operation.

What this adds

query, sync, batchGet, batchPut, batchDelete, transactGet, transactWrite, the module-level set helpers, and operations.remove / operations.updateListItem, plus the missing arguments on the helpers that already existed.

util.transform.toSubscriptionFilter is new. It expands a filter into the disjunctive normal form subscriptions are delivered against: every operator of a field, every member of an or and every rule is an alternative, so the groups multiply out. Unlike the DynamoDB transforms it answers an object rather than a JSON string.

util.dynamodb.fromS3ObjectJson now parses the JSON string form instead of throwing, which un-skips its test.

The expression builder that already backed util.transform is reused rather than duplicated: it now also exposes the expression as an object, which is the form a request embeds.

Recorded, not inferred

Everything here comes from EvaluateCode, and the published type definitions turned out wrong in three places:

  • toNumberSet([1,2,3]) gives {"NS":[1,2,3]}, not the stringified ["1","2","3"] the JSDoc shows.
  • a condition carries no returnValuesOnConditionCheckFailure, although the type marks it required.
  • sync is accepted. The comment claiming AWS rejects it, and the test skipped on that basis, were stale.

Also recorded: a projection aliases every path segment, so nested.field becomes #expName_2.#expName_3; an expression that binds no value carries no expressionValues at all, while an update expression keeps an empty one; and increment() with no argument steps by one.

Tests

42 new snapshots recorded from EvaluateCode, covering each helper minimally and fully argued, the update clause grouping and alias numbering, projections that reuse a path segment, and the subscription filter across its operators, nesting, ignoredFields, rules and the inputs it cannot read. sync and fromS3ObjectJson are un-skipped.

npm test: 412 passed, 1 skipped, 404 snapshots. A probe comparing all 63 recorded inputs against the implementation reports no mismatches, and test_in_docker.sh still installs and imports.

The one remaining skip is operations.updateListItem returned on its own: AWS answers an opaque value for that marker alone, with no enumerable keys, serialising to nothing, so it cannot be compared. What it builds is covered by the update tests.

One divergence worth knowing

Clauses come out in the order the fields were written. AWS orders them by its own object iteration, which for some sets of field names is neither the written order nor sorted, so a condition over several fields can be joined in a different order there. AND commutes, so the two mean the same thing, and it is noted in the code.

🤖 Generated with Claude Code

The `@aws-appsync/utils/dynamodb` module implemented `get`, `put`,
`remove`, `scan`, `update` and `operations`, and those dropped most of
their arguments: a `filter` or `projection` handed to `scan` was passed
through raw instead of being turned into an expression, and `condition`,
`consistentRead`, `projection` and `_version` were silently discarded
everywhere else.

Adds `query`, `sync`, `batchGet`, `batchPut`, `batchDelete`,
`transactGet`, `transactWrite`, the module level set helpers, and
`operations.remove` / `operations.updateListItem`, and completes the
arguments of the helpers that already existed.

The update expression builder was also wrong for anything but a single
operation: it joined clauses with a bare comma and repeated the `SET`
keyword per clause, where AWS emits one keyword per group, `SET` before
`REMOVE`, with the aliases numbered in the order the clauses come out.

`util.transform.toSubscriptionFilter` is new, expanding a filter into the
disjunctive normal form AppSync delivers subscriptions against, and
`util.dynamodb.fromS3ObjectJson` now parses the JSON string form instead
of throwing, which un-skips its test.

Everything is recorded from `EvaluateCode`. The published types proved
wrong in three places along the way: `toNumberSet` does not stringify,
a `condition` carries no `returnValuesOnConditionCheckFailure`, and
`sync` is accepted despite the stale comment claiming otherwise.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@bentsku
bentsku merged commit 3269a69 into main Sep 3, 2026
4 checks passed
@bentsku
bentsku deleted the aws-1740-appsync-utils-dynamodb-module branch September 3, 2026 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant