Skip to content

Updating unique template field does not remove unique record #509

@AlexStansfield

Description

@AlexStansfield

Describe the bug

Our user records have email as a unique field. However we do not hard delete user records, instead we soft delete them. We do this by setting the deletedAt field to a date.

So for this reason rather than setting the email field as unique we have an extra uniqueEmail field that uses a template. If there is no deletedAt then we want this field to have the email, and if there is a deletedAt we want this field to be removed. This should trigger the removal of the unique record for this field.

As this can't be done with a string template we used a function instead.

We're pretty sure this was working before but yesterday I found it no longer did when I went to delete a user and recreate it with the same email.

I did some investigation and found that in Model.update when it works out the hasUniqueProperties the value template function has not been run, so it doesn't know that uniqueEmail is now being set to null and so updateUnique should be called instead of updateItem.

To Reproduce

I've created a branch on my fork of this repository - https://github.com/AlexStansfield/dynamodb-onetable/tree/issue/unique-update-with-value-function

Here I added:

  • two new fields to the schema, deletedAt and uniqueEmail - here
  • a function to create the value for uniqueEmail (same as email if no deletedAt, null if there is deletedAt) - here
  • an extra test to unique.ts with the title "Soft delete user and create with same email". - here

You will see that it fails. It should be expected that the unique record should have been removed so first it fails where it expects the number of items returned in the scan is one less (i.e the unique record is gone). If you remove this expectation you will see the create then fails because it finds the unique record.

The User record however is updated and the uniqueEmail field has been removed.

Here is a console.log of the items that it gets from the scan after the "soft delete"

[
  {
    sk: { S: '_unique#' },
    _type: { S: '_Unique' },
    pk: { S: '_unique#User#interpolated#Judy Smith#judy-a@example.com' }
  },
  {
    interpolated: { S: 'Peter Smith#peter@example.com' },
    deletedAt: { N: '1700121064087' },
    _type: { S: 'User' },
    name: { S: 'Peter Smith' },
    sk: { S: 'User#' },
    pk: { S: 'User#Peter Smith' },
    email: { S: 'peter@example.com' }
  },
  {
    interpolated: { S: 'Judy Smith#judy-a@example.com' },
    uniqueEmail: { S: 'judy-a@example.com' },
    _type: { S: 'User' },
    name: { S: 'Judy Smith' },
    sk: { S: 'User#' },
    pk: { S: 'User#Judy Smith' },
    age: { N: '15' },
    email: { S: 'judy-a@example.com' }
  },
  {
    sk: { S: '_unique#' },
    _type: { S: '_Unique' },
    pk: { S: '_unique#User#interpolated#Peter Smith#peter@example.com' }
  },
  {
    sk: { S: '_unique#' },
    _type: { S: '_Unique' },
    pk: { S: '_unique#User#email#peter@example.com' }
  },
  {
    sk: { S: '_unique#' },
    _type: { S: '_Unique' },
    pk: { S: '_unique#User#uniqueEmail#peter@example.com' }
  },
  {
    sk: { S: '_unique#' },
    _type: { S: '_Unique' },
    pk: { S: '_unique#User#email#judy-a@example.com' }
  },
  {
    sk: { S: '_unique#' },
    _type: { S: '_Unique' },
    pk: { S: '_unique#User#uniqueEmail#judy-a@example.com' }
  }
]

As you can see the Peter Smith record has a deletedAt and no uniqueEmail, however this record remains:

  {
    sk: { S: '_unique#' },
    _type: { S: '_Unique' },
    pk: { S: '_unique#User#uniqueEmail#peter@example.com' }
  },

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions