dynamodb-toolbox: Queries constructed inconsistently when multiple Entity instances used

When the new Entity constructor is used multiple times to instantiate a query for the same entity, queries generated for DynamoDB appear to be inconsistent.

See below code (table.spec.ts):

export const UserEntity = {
  name: 'User',
  attributes: {
    email: { partitionKey: true },
    type: { sortKey: true, default: 'user' },
    name: { type: 'string', required: true },
    emailVerified: { type: 'boolean', required: true },
  },
} as const;

 it.only('Should be able to instantiate entity without deepCopy', async () => {
    AWS.config.logger = console;
    const table = await connectTable();
    const Users1 = new Entity({ ...deepCopy(UserEntity), table } as const);
    await Users1.put({
      email: 'joe@email.com',
      name: 'Joe',
      type: 'user',
      emailVerified: true,
    });

    const Users2 = new Entity({ ...deepCopy(UserEntity), table } as const);
    const { Item: user } = await Users2.get(
      { email: 'joe@email.com', type: 'user' },
      { attributes: ['email', 'name'] }
    );
    expect(user.name).toEqual('Joe');
    expect(user.email).toEqual('joe@email.com');
  });

This will generate the following get query to DynamoDB:

 console.log
    [AWS dynamodb 400 0.016s 0 retries] getItem({
      TableName: 'local-dynamodb',
      Key: { email: { S: 'joe@email.com' }, type: { S: 'user' } },
      ExpressionAttributeNames: { '#proj1': 'pk', '#proj2': 'name' },
      ProjectionExpression: '#proj1,#proj2'
    })

Since there is no correct mapping to pk and sk, DynamoDB will issue the following error:

ValidationException: One of the required keys was not given a value

      at Request.extractError (../../../../.yarn/cache/aws-sdk-npm-2.1222.0-957e2db879-2dc98ef933.zip/node_modules/aws-sdk/lib/protocol/json.js:52:27)

When changing the above code to use the same entity to do the put and get query, a correct get request is issued to DynamoDB.

e.g. using Users1 instead of Users2 to construct the get query:

const { Item: user } = await Users1.get(
      { email: 'joe@email.com', type: 'user' },
      { attributes: ['email', 'name'] }
    );

Will result in the following query to DynamoDB:

  console.log
    [AWS dynamodb 200 0.019s 0 retries] getItem({
      TableName: 'local-dynamodb',
      Key: { pk: { S: 'joe@email.com' }, sk: { S: 'user' } },
      ExpressionAttributeNames: { '#proj1': 'pk', '#proj2': 'name' },
      ProjectionExpression: '#proj1,#proj2'
    })

See in the above how Key contains the correctly mapped keys.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 15

Commits related to this issue

Most upvoted comments

Thanks, just noting here for reference that the error I get is exactly the same as originally reported.

So running (table.spec.ts):

 AWS.config.logger = console;
    const table = await connectTable();
    const Users1 = new Entity({ ...deepCopy(UserEntity), table } as const);
    await Users1.put({
      email: 'joe@email.com',
      name: 'Joe',
      type: 'user',
      emailVerified: true,
    });

    const Users2 = new Entity({ ...deepCopy(UserEntity), table } as const);
    const { Item: user } = await Users2.get(
      {
        email: 'joe@email.com',
      },
      { attributes: ['email', 'name'] }
    );

Will yield the query:

[AWS dynamodb 400 0.009s 0 retries] getItem({
      TableName: 'local-dynamodb',
      Key: { email: { S: 'joe@email.com' }, type: { S: 'user' } },
      ExpressionAttributeNames: { '#proj1': 'pk', '#proj2': 'name' },
      ProjectionExpression: '#proj1,#proj2'
    })

Using Users1 to run the second query will work correctly, so the suggested workaround of only instantiating an Entity once during the runtime of an application should be a valid workaround for now.

Yep just tested with 0.4.3 and deepCopy - behaviour is the same, so would think that reverting the change is not required!