Skip to content
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
98a384c
chore: removed v2 from packages that don't use it
Sep 17, 2025
a7ed5bb
refactor: update amplify-cli, amplify-console-integ-tests, and amplif…
Sep 17, 2025
e4062a0
refactor: category-function and appsync-sim
Sep 17, 2025
856a4e6
fix: some adjustments
Sep 17, 2025
b2a9de6
fix: fix some e2e tests
Sep 18, 2025
53f8ed6
fix: unit tests
Sep 18, 2025
fabf072
chore: dedupe
Sep 18, 2025
9f05e7c
refactor: amplify-util-mock
Sep 19, 2025
85f572e
fix: mock tests and dynamodb sim
Sep 19, 2025
35b9d57
fix: dynamoDb creds
Sep 19, 2025
0dcd1dd
fix: update cred resolution
Sep 22, 2025
0436460
fix: switch from null to undefined
Sep 22, 2025
8971021
fix: adjust creds again
Sep 24, 2025
8de60b0
fix: resolve credentials
Sep 26, 2025
fea82b6
fix: marshall objects
Sep 26, 2025
058029c
chore: try checking if type is marshalled
Sep 26, 2025
f30585c
chore: adjustments
Sep 30, 2025
7529161
fix: swap dynamoClients
Oct 1, 2025
deec9b6
chore: dedupe
Oct 1, 2025
b80909d
fix: unmarshall
Oct 1, 2025
206f1e1
Revert "fix: unmarshall"
Oct 1, 2025
41629d1
Revert "chore: dedupe"
Oct 1, 2025
39f7a9f
Revert "fix: swap dynamoClients"
Oct 1, 2025
21c94af
chore: alter marshalling
Oct 2, 2025
6e9f1c3
Merge branch 'dev' into sdk-migrations-3
Oct 2, 2025
ce7cf35
chore: debugging
Oct 2, 2025
994d065
chore: debugging
Oct 2, 2025
fe5bb85
Merge branch 'dev' into sdk-migrations-3
Oct 2, 2025
eee1aa1
chore: debugging
Oct 2, 2025
323006e
chore: debugging
Oct 3, 2025
c14bb74
chore: debugging...
Oct 3, 2025
832e4f8
fix: json values
Oct 3, 2025
312e8c9
chore: debugging
Oct 3, 2025
4d94f89
chore: trying something
Oct 3, 2025
bf84a44
fix: special array handling
Oct 3, 2025
ad2c6b4
chore: debugging
Oct 3, 2025
38af22e
chore: debugging
Oct 3, 2025
1ae2ebe
chore: debugging
Oct 3, 2025
fa954b0
fix: malformed nextToken
Oct 3, 2025
3b1ee9c
chore: debugging
Oct 6, 2025
25a81ed
Merge branch 'dev' into sdk-migrations-3
Oct 6, 2025
96978a1
chore: dedupe
Oct 6, 2025
e6900b4
fix: test isolation
Oct 6, 2025
1ac9289
chore: revert test isolation
Oct 6, 2025
f4928e6
chore: remove non mock changes
Oct 6, 2025
c1b9b4a
chore: number
Oct 6, 2025
a50f79d
fix: unmarshalling
Oct 7, 2025
c562ba5
fix: tests
Oct 7, 2025
27fe8f8
fix: dealing with mapped types
Oct 8, 2025
3ba76ac
chore: clean up
Oct 8, 2025
8f9837a
fix: lint and minor package size bump
Oct 8, 2025
58ef1c7
fix: e2e test stability
Oct 9, 2025
75c479f
Merge branch 'dev' into sdk-migrations-3
Oct 9, 2025
d125cad
Revert "fix: e2e test stability"
Oct 9, 2025
c72da1d
Merge branch 'dev' into sdk-migrations-3
Oct 17, 2025
cf2f730
Merge branch 'dev' into sdk-migrations-3
Oct 17, 2025
c075284
Merge branch 'dev' into sdk-migrations-3
Oct 21, 2025
aa9a917
fix: move more tests to run as duo
Oct 21, 2025
b9005f8
Merge branch 'sdk-migrations-3' of github.com:aws-amplify/amplify-cli…
Oct 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/amplify-appsync-simulator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@
"dependencies": {
"@aws-amplify/amplify-cli-core": "4.4.2",
"@aws-amplify/amplify-prompts": "2.8.7",
"@aws-sdk/client-dynamodb": "^3.624.0",
"@aws-sdk/util-dynamodb": "^3.624.0",
"@graphql-tools/schema": "^8.3.1",
"@graphql-tools/utils": "^8.5.1",
"amplify-velocity-template": "1.4.17",
"aws-sdk": "^2.1464.0",
"chalk": "^4.1.1",
"cors": "^2.8.5",
"dataloader": "^2.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
import { AttributeMap } from 'aws-sdk/clients/dynamodb';
import { DynamoDB } from 'aws-sdk';
import { unmarshall, nullIfEmpty } from './utils';
import {
AttributeValue,
DynamoDBClient,
GetItemCommand,
PutItemCommand,
UpdateItemCommand,
UpdateItemCommandInput,
DeleteItemCommand,
QueryCommand,
QueryInput,
ScanCommand,
ScanCommandInput,
Select,
ReturnValue,
} from '@aws-sdk/client-dynamodb';
import { unmarshall, marshall, nullIfEmpty } from './utils';
import { AmplifyAppSyncSimulatorDataLoader } from '..';

type DynamoDBConnectionConfig = {
endpoint: string;
region: 'us-fake-1';
accessKeyId: 'fake';
secretAccessKey: 'fake';
credentials: {
accessKeyId: 'fake';
secretAccessKey: 'fake';
};
tableName: string;
};
type DynamoDBLoaderConfig = {
config: DynamoDBConnectionConfig;
options: object;
};
export class DynamoDBDataLoader implements AmplifyAppSyncSimulatorDataLoader {
private client: DynamoDB;
private client: DynamoDBClient;
private tableName: string;

constructor(private ddbConfig: DynamoDBLoaderConfig) {
Expand All @@ -24,7 +39,10 @@
throw new Error(`Invalid DynamoDBConfig ${JSON.stringify(ddbConfig, null, 4)}`);
}
this.tableName = tableName;
this.client = new DynamoDB({ ...ddbConfig.config, ...ddbConfig.options });
this.client = new DynamoDBClient({
...ddbConfig.config,
...ddbConfig.options,
});
}

async load(payload): Promise<object | null> {
Expand Down Expand Up @@ -53,11 +71,11 @@
throw new Error(`Unknown operation name: ${payload.operation}`);
}
} catch (e) {
if (e.code) {
if (e.name) {
console.log('Error while executing Local DynamoDB');
console.log(JSON.stringify(payload, null, 4));
console.log(e);
e.extensions = { errorType: 'DynamoDB:' + e.code };
e.extensions = { errorType: 'DynamoDB:' + e.name };
}
throw e;
}
Expand All @@ -67,45 +85,45 @@
try {
const items = await this.getAllItems();
for await (const item of items) {
await this.client
.deleteItem({
await this.client.send(
new DeleteItemCommand({
TableName: this.tableName,
Key: { id: item.id },
ReturnValues: 'ALL_OLD',
})
.promise();
ReturnValues: 'ALL_OLD' as ReturnValue,
}),
);
}
} catch (e) {
throw new Error(`Error while deleting all items from ${this.tableName}`);
}
return [this.tableName];
}
// Gets all the records from the DynamoDB local table
private async getAllItems(): Promise<Array<AttributeMap> | null> {
private async getAllItems(): Promise<Array<Record<string, AttributeValue>> | null> {
let items = [];
let data = await this.client.scan({ TableName: this.tableName }).promise();
let data = await this.client.send(new ScanCommand({ TableName: this.tableName }));
items = [...items, ...data.Items];
while (typeof data.LastEvaluatedKey !== 'undefined') {
data = await this.client
.scan({
data = await this.client.send(
new ScanCommand({
TableName: this.tableName,
ExclusiveStartKey: data.LastEvaluatedKey,
})
.promise();
}),
);
items = [...items, ...data.Items];
}
return items;
}

private async getItem(payload: any): Promise<object | null> {
const { consistentRead = false } = payload;
const result = await this.client
.getItem({
const result = await this.client.send(
new GetItemCommand({
TableName: this.tableName,
Key: payload.key,
Key: payload.key as Record<string, AttributeValue>,
ConsistentRead: consistentRead,
})
.promise();
}),
);

if (!result.Item) return null;
return unmarshall(result.Item);
Expand All @@ -117,66 +135,68 @@
attributeValues,
condition: {
// we only provide limited support for condition update expressions.
expression = null,
expressionNames = null,
expressionValues = null,
expression = undefined,
expressionNames = undefined,
expressionValues = undefined,
} = {},
} = payload;
await this.client
.putItem({

await this.client.send(
new PutItemCommand({
TableName: this.tableName,
Item: {
...attributeValues,
...key,
},
...attributeValues,
} as Record<string, AttributeValue>,
ConditionExpression: expression,
ExpressionAttributeNames: expressionNames,
ExpressionAttributeValues: expressionValues,
})
.promise();
ExpressionAttributeValues: expressionValues as Record<string, AttributeValue>,
}),
);

// put does not return us anything useful so we need to fetch the object.

return this.getItem({ key, consistentRead: true });
}
private async query({ query: keyCondition, filter, index, nextToken, limit, scanIndexForward = true, consistentRead = false, select }) {
keyCondition = keyCondition || { expression: null };
filter = filter || { expression: null };

const params = {
TableName: this.tableName,
KeyConditionExpression: keyCondition.expression,
FilterExpression: filter.expression,
ExpressionAttributeValues: nullIfEmpty({
...(filter.expressionValues || {}),
...(keyCondition.expressionValues || {}),
}),
...(filter.expressionValues || undefined),
...(keyCondition.expressionValues || undefined),
} as Record<string, AttributeValue>),
ExpressionAttributeNames: nullIfEmpty({
...(filter.expressionNames || {}),
...(keyCondition.expressionNames || {}),
...(filter.expressionNames || undefined),
...(keyCondition.expressionNames || undefined),
}),
ExclusiveStartKey: nextToken ? JSON.parse(Buffer.from(nextToken, 'base64').toString()) : null,
IndexName: index,
Limit: limit,
ConsistentRead: consistentRead,
ScanIndexForward: scanIndexForward,
Select: select || 'ALL_ATTRIBUTES',
Select: select || ('ALL_ATTRIBUTES' as Select),
};
const {
Items: items,
ScannedCount: scannedCount,
LastEvaluatedKey: resultNextToken = null,
} = await this.client.query(params as any).promise();
} = await this.client.send(new QueryCommand(params as QueryInput));

return {
items: items.map((item) => unmarshall(item)),
items: (items || []).map((item) => unmarshall(item)),
scannedCount,
nextToken: resultNextToken ? Buffer.from(JSON.stringify(resultNextToken)).toString('base64') : null,
};
}

private async updateItem(payload) {
const { key, update = {}, condition = {} } = payload;
const params: any = {

const params: UpdateItemCommandInput = {
TableName: this.tableName,
Key: key,
UpdateExpression: update.expression,
Expand All @@ -185,13 +205,15 @@
ExpressionAttributeNames: nullIfEmpty({
...(condition.expressionNames || {}),
...(update.expressionNames || {}),
}),
} as Record<string, string>),
ExpressionAttributeValues: nullIfEmpty({
...(condition.expressionValues || {}),
...(update.expressionValues || {}),
}),
} as Record<string, AttributeValue>),
ReturnValuesOnConditionCheckFailure: 'ALL_OLD',
};
const { Attributes: updated } = await this.client.updateItem(params).promise();

const { Attributes: updated } = await this.client.send(new UpdateItemCommand(params));
return unmarshall(updated);
}

Expand All @@ -200,29 +222,31 @@
key,
condition: {
// we only provide limited support for condition update expressions.
expression = null,
expressionNames = null,
expressionValues = null,
expression = undefined,
expressionNames = undefined,
expressionValues = undefined,
} = {},
} = payload;
const { Attributes: deleted } = await this.client
.deleteItem({
const { Attributes: deleted } = await this.client.send(
new DeleteItemCommand({
TableName: this.tableName,
Key: key,
Key: key as Record<string, AttributeValue>,
ReturnValues: 'ALL_OLD',
ConditionExpression: expression,
ExpressionAttributeNames: expressionNames,
ExpressionAttributeValues: expressionValues,
})
.promise();
ExpressionAttributeValues: expressionValues as Record<string, AttributeValue>,
}),
);

return unmarshall(deleted);
}

private async scan(payload) {
const { filter, index, limit, consistentRead = false, nextToken, select, totalSegments, segment } = payload;

const params = {
console.log('SCAN PAYLOAD:', JSON.stringify(payload, null, 2));

const params: ScanCommandInput = {
TableName: this.tableName,
ExclusiveStartKey: nextToken ? JSON.parse(Buffer.from(nextToken, 'base64').toString()) : null,
IndexName: index,
Expand All @@ -239,14 +263,21 @@
...(filter.expressionNames || undefined),
}),
ExpressionAttributeValues: {
...(filter.expressionValues || undefined),
...(filter.expressionValues || {}),
},
});
}
const { Items: items, ScannedCount: scannedCount, LastEvaluatedKey: resultNextToken = null } = await this.client.scan(params).promise();
const {
Items: items,
ScannedCount: scannedCount,
LastEvaluatedKey: resultNextToken = null,
} = await this.client.send(new ScanCommand(params));

console.log('SCAN PARAMS:', JSON.stringify(params, null, 2));
console.log('SCAN RESULT ITEMS:', items?.length || 0);

return {
items: items.map((item) => unmarshall(item)),
items: (items || []).map((item) => unmarshall(item)),
scannedCount,
nextToken: resultNextToken ? Buffer.from(JSON.stringify(resultNextToken)).toString('base64') : null,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { Converter } from 'aws-sdk/clients/dynamodb';
import { unmarshall as awsUnmarshall, marshall as awsMarshall } from '@aws-sdk/util-dynamodb';
import { AttributeValue } from '@aws-sdk/client-dynamodb';

export function nullIfEmpty(obj: object): object | null {
export function nullIfEmpty<T extends Record<string, string | AttributeValue>>(obj: T): T | null {
return Object.keys(obj).length === 0 ? null : obj;
}

export function marshall(obj: object) {
if (!obj || Object.keys(obj).length === 0) {
return undefined;
}
return awsMarshall(obj);
}

export function unmarshall(raw, isRaw = true) {
const content = isRaw ? Converter.unmarshall(raw) : raw;
const content = isRaw ? awsUnmarshall(raw) : raw;
// Because of the funky set type used in the aws-sdk, we need to further unwrap
// to find if there is a set that needs to be unpacked into an array.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
import { Converter } from 'aws-sdk/clients/dynamodb';
import { DynamoDBSet } from 'aws-sdk/lib/dynamodb/set';
import { marshall } from '@aws-sdk/util-dynamodb';

import { toJSON } from '../value-mapper/to-json';

export const dynamodbUtils = {
toDynamoDB(value: any) {
return Converter.input(toJSON(value));
if (Array.isArray(value)) {
return { L: marshall(toJSON(value)) };
}
const jsonValue = toJSON(value);
if (jsonValue !== null && typeof jsonValue === 'object' && !Array.isArray(jsonValue)) {
return {
M: marshall(jsonValue, {
removeUndefinedValues: true,
convertEmptyValues: true,
}),
};
}
return marshall(jsonValue, {
removeUndefinedValues: true,
convertEmptyValues: true,
});
},
$toSet(values, fn = (value) => value) {
return this.toDynamoDB(new DynamoDBSet([].concat(values).map((value) => fn(value))));
return this.toDynamoDB(new Set([].concat(values).map((value) => fn(value))));
},
toDynamoDBJson(value) {
return JSON.stringify(this.toDynamoDB(value));
Expand Down Expand Up @@ -76,13 +90,13 @@ export const dynamodbUtils = {
return JSON.stringify(this.toMap(value));
},
toMapValues(values) {
return Object.entries(toJSON(values)).reduce(
(sum, [key, value]) => ({
const jsonValues = toJSON(values);
return Object.entries(jsonValues).reduce((sum, [key, value]) => {
return {
...sum,
[key]: this.toDynamoDB(value),
}),
{},
);
};
}, {});
},
toMapValuesJson(values) {
return JSON.stringify(this.toMapValues(values));
Expand Down
Loading
Loading