Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add aws-dynamodb driver #234

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Prev Previous commit
Next Next commit
chore(aws-dynamodb) improve tests
daaru00 committed May 25, 2023
commit 75c0fa6d94142a4deed9d21b8422eb3aface1764
3 changes: 2 additions & 1 deletion docs/content/6.drivers/aws-dynamodb.md
Original file line number Diff line number Diff line change
@@ -20,7 +20,8 @@ const storage = createStorage({
driver: dynamoDbCacheDriver({
table: "my-persistent-storage", // required
region: "us-east-1", // optional, retrieved via environment variables
credentials: { // optional, retrieved by AWS SDK via environment variables
credentials: {
// optional, retrieved by AWS SDK via environment variables
accessKeyId: "xxxxxxxxxx", // DO NOT HARD-CODE SECRETS
secretAccessKey: "xxxxxxxxxxxxxxxxxxxx", // DO NOT HARD-CODE SECRETS
},
14 changes: 9 additions & 5 deletions src/drivers/aws-dynamodb.ts
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ export default defineDriver((opts: DynamoDBStorageOptions) => {
opts.expireIn =
opts.expireIn === undefined ? 0 : parseInt(`${opts.expireIn}`);
if (Number.isNaN(opts.expireIn) || opts.expireIn < 0) {
throw createError(DRIVER_NAME, "expireIn");
throw createError(DRIVER_NAME, "Invalid option `expireIn`.");
}

let client;
@@ -66,6 +66,10 @@ export default defineDriver((opts: DynamoDBStorageOptions) => {
return client;
}

function getTimestamp(): number {
return Math.round(Date.now() / 1000);
}

function createObject(key: string, value: any = undefined, ttl: number = 0) {
const obj = {};
obj[opts.attributes.key] = key;
@@ -88,13 +92,13 @@ export default defineDriver((opts: DynamoDBStorageOptions) => {
);

if (!item) {
return undefined;
return null;
}

if (opts.expireIn > 0) {
const timestamp = Math.round(Date.now() / 1000);
const timestamp = getTimestamp();
if (timestamp > parseInt(item.ttl || 0)) {
return undefined;
return null;
}
}

@@ -128,7 +132,7 @@ export default defineDriver((opts: DynamoDBStorageOptions) => {
);

if (opts.expireIn > 0) {
const timestamp = Math.round(Date.now() / 1000);
const timestamp = getTimestamp();
items = items.filter((item) => parseInt(item.ttl || 0) >= timestamp);
}

60 changes: 51 additions & 9 deletions test/drivers/aws-dynamodb.test.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
import { describe, beforeAll, afterAll } from "vitest";
import { describe, beforeAll, afterAll, expect, it } from "vitest";
import driver, { DynamoDBStorageOptions } from "../../src/drivers/aws-dynamodb";
import { testDriver } from "./utils";
import { fromIni } from "@aws-sdk/credential-providers";
import { fromIni, fromEnv } from "@aws-sdk/credential-providers";
import {
DynamoDBClient,
CreateTableCommand,
UpdateTimeToLiveCommand,
DeleteTableCommand,
waitUntilTableExists,
waitUntilTableNotExists,
GetItemCommand,
} from "@aws-sdk/client-dynamodb";

const TABLE_OPERATIONS_TIMEOUT_SECONDS = 30; // table need at lest 30s from creation to become available

describe("drivers: aws-dynamodb", () => {
// Load AWS credentials

const region = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION;
const credentials = fromIni({
profile:
process.env.AWS_PROFILE || process.env.AWS_DEFAULT_PROFILE || "default",
const profile = process.env.AWS_PROFILE || process.env.AWS_DEFAULT_PROFILE;
const credentials = profile
? fromIni({
profile: profile,
})
: fromEnv();

// Init client for test purpose

const client = new DynamoDBClient({
region,
credentials,
});

// Setup test driver options

const options: DynamoDBStorageOptions = {
table: "tmp-unstorage-tests",
region,
@@ -29,12 +43,10 @@ describe("drivers: aws-dynamodb", () => {
value: "value",
ttl: "ttl",
},
expireIn: 300,
};

const client = new DynamoDBClient({
region: process.env.AWS_REGION,
credentials,
});
// Test hooks

beforeAll(async () => {
await client.send(
@@ -85,7 +97,37 @@ describe("drivers: aws-dynamodb", () => {
);
}, (TABLE_OPERATIONS_TIMEOUT_SECONDS + 2) * 1000);

// Common tests

testDriver({
driver: driver(options),
additionalTests: (ctx) => {
// Additional tests

it("should set TTL attribute on item", async () => {
const timestamp = Math.round(Date.now() / 1000);

await ctx.storage.setItem("test-with-ttl", "ok");

const key = {};
key[options.attributes?.key as string] = {
S: "test-with-ttl",
};

const { Item: item } = await client.send(
new GetItemCommand({
TableName: options.table,
Key: key,
})
);

expect(item).not.toBeUndefined();
expect(item?.[options.attributes?.value as string].S).toBe("ok");
expect(item?.[options.attributes?.ttl as string].N).not.toBeUndefined();
expect(
parseInt(item?.[options.attributes?.ttl as string].N as string)
).toBeGreaterThanOrEqual(timestamp + (options.expireIn || 0));
});
},
});
});