Skip to content
This repository has been archived by the owner on Jul 17, 2022. It is now read-only.

Commit

Permalink
fix(dynamo): Ensure expires attribute is stored as a UNIX timestamp (#…
Browse files Browse the repository at this point in the history
…367)

* test(dynamodb): add tests for utils.format
Intentionally includes two failing tests having to do with the `expires` attribute.

* fix(dynamodb): save expires attribute as unix timestamp
  • Loading branch information
GiacoCorsiglia authored Jan 10, 2022
1 parent 4543e80 commit c262110
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 6 deletions.
15 changes: 9 additions & 6 deletions packages/dynamodb/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ export const format = {
const newObject: Record<string, unknown> = {}
for (const key in object) {
const value = object[key]
if (value instanceof Date) newObject[key] = value.toISOString()
// Converted to a string by `format.from
else if (key === "expires") newObject[key] = parseInt(value, 10)
else newObject[key] = value
if (value instanceof Date) {
// DynamoDB requires the TTL attribute be a UNIX timestamp (in secs).
if (key === "expires") newObject[key] = value.getTime() / 1000
else newObject[key] = value.toISOString()
} else newObject[key] = value
}
return newObject
},
Expand All @@ -33,8 +34,10 @@ export const format = {
// hack to keep type property in account
else if (key === "type" && ["SESSION", "VT", "USER"].includes(value))
continue
// DynamoDB cannot deal with large numbers, so we convert it to a string
else if (key === "expires") newObject[key] = value.toString()
// The expires property is stored as a UNIX timestamp in seconds, but
// JavaScript needs it in milliseconds, so multiply by 1000.
else if (key === "expires" && typeof value === "number")
newObject[key] = new Date(value * 1000)
else newObject[key] = value
}
return newObject as T
Expand Down
121 changes: 121 additions & 0 deletions packages/dynamodb/tests/format.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { format } from "../src/utils"

describe("dynamodb utils.format", () => {
it("format.to() preserves non-Date non-expires properties", () => {
expect(
format.to({
pk: "test-pk",
email: "[email protected]",
})
).toEqual({
pk: "test-pk",
email: "[email protected]",
})
})

it("format.to() converts non-expires Date properties to ISO strings", () => {
const date = new Date()
expect(
format.to({
dateProp: date,
})
).toEqual({
dateProp: date.toISOString(),
})
})

it("format.to() converts expires property to a UNIX timestamp", () => {
// DynamoDB requires that the property used for TTL is a UNIX timestamp.
const date = new Date()
const timestamp = date.getTime() / 1000
expect(
format.to({
expires: date,
})
).toEqual({
expires: timestamp,
})
})

it("format.from() preserves non-special attributes", () => {
expect(
format.from({
testAttr1: "test-value",
testAttr2: 5,
})
).toEqual({
testAttr1: "test-value",
testAttr2: 5,
})
})

it("format.from() removes dynamodb key attributes", () => {
expect(
format.from({
pk: "test-pk",
sk: "test-sk",
GSI1PK: "test-GSI1PK",
GSI1SK: "test-GSI1SK",
})
).toEqual({})
})

it("format.from() only removes type attribute from Session, VT, and User", () => {
expect(format.from({ type: "SESSION" })).toEqual({})
expect(format.from({ type: "VT" })).toEqual({})
expect(format.from({ type: "USER" })).toEqual({})
expect(format.from({ type: "ANYTHING" })).toEqual({ type: "ANYTHING" })
expect(format.from({ type: "ELSE" })).toEqual({ type: "ELSE" })
})

it("format.from() converts ISO strings to Date instances", () => {
const date = new Date()
expect(
format.from({
someDate: date.toISOString(),
})
).toEqual({
someDate: date,
})
})

it("format.from() converts expires attribute from timestamp to Date instance", () => {
// AdapterSession["expires"] and VerificationToken["expires"] are both meant
// to be Date instances.
const date = new Date()
const timestamp = date.getTime() / 1000
expect(
format.from({
expires: timestamp,
})
).toEqual({
expires: date,
})
})

it("format.from() converts expires attribute from ISO string to Date instance", () => {
// Due to a bug in an old version, some expires attributes were stored as
// ISO strings, so we need to handle those properly too.
const date = new Date()
expect(
format.from({
expires: date.toISOString(),
})
).toEqual({
expires: date,
})
})

it("format.from(format.to()) preserves expires attribute", () => {
const date = new Date()
expect(
format.from(
format.to({
expires: date,
})
)
).toEqual({
expires: date,
})
})
})

0 comments on commit c262110

Please sign in to comment.