Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ unless the `client` options is provided to override them.
☣️ Legacy reap behaviors use DynamoDB [`scan`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/scancommand.html)
functionality that can incur significant costs. Should instead enable [DynamoDB TTL](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html)
and select the `expires` field. TODO should we just remove it since we're already making a breaking change?
- `expiresIn` Optional set the number of seconds for DynamoDB TTL. Defaults to the cookie's maxAge. Must be a positive integer.

## Usage

Expand All @@ -42,6 +43,8 @@ var options = {
],
// Optional skip throw missing special keys in session, if set true
skipThrowMissingSpecialKeys: true,
// Optional set the number of seconds for DynamoDB TTL
expiresIn: 3600,
};
```

Expand Down
7 changes: 7 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ declare namespace ConnectDynamoDB {
* Useful if the table already exists or if you want to skip existence checks in a serverless environment such as AWS Lambda.
*/
initialized?: boolean;
/**
* Set the number of seconds added to the item expiry time.
*
* Setting this to 0 will use the `maxAge` value from the session cookie.
* @default 0
*/
expiresIn?: number;
}

interface DynamoDBStoreOptionsSpecialKey {
Expand Down
22 changes: 21 additions & 1 deletion lib/connect-dynamodb.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ module.exports = function (connect) {
if (this.reapInterval > 0) {
this._reap = setInterval(this.reap.bind(this), this.reapInterval);
}
if (options.expiresIn) {
if (!Number.isInteger(options.expiresIn)) {
console.warn("`expiresIn` must be an integer. Reverting to default behaviour");
this.expiresIn = 0;
}
else if (options.expiresIn < 0) {
console.warn("Negative `expiresIn` values are not supported. Reverting to default behaviour");
this.expiresIn = 0;
} else {
this.expiresIn = options.expiresIn;
}
} else {
this.expiresIn = 0;
}
}

/*
Expand Down Expand Up @@ -344,11 +358,17 @@ module.exports = function (connect) {
*/
DynamoDBStore.prototype.getExpiresValue = function (sess) {
const now = Math.floor(Date.now() / 1000);
const expires =

if (this.expiresIn != 0) {
return now + this.expiresIn;
}
else {
const expires =
typeof sess.cookie.maxAge === "number"
? now + (sess.cookie.maxAge / 1000)
: now + oneDayInSeconds;
return expires;
}
};

/**
Expand Down
77 changes: 77 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const {
DynamoDBClient,
CreateTableCommand,
DeleteTableCommand,
GetItemCommand,
ScalarAttributeType,
} = require("@aws-sdk/client-dynamodb");
const ConnectDynamoDB = require(__dirname + "/../lib/connect-dynamodb.js");
Expand Down Expand Up @@ -45,6 +46,16 @@ describe("DynamoDBStore", () => {
const sessionId = Math.random().toString();

describe("Instantiation", () => {
let consoleWarnStub;

beforeEach(() => {
consoleWarnStub = sinon.stub(console, 'warn');
})

afterEach(() => {
consoleWarnStub.restore();
})

it("should be able to be created", () => {
store.should.be.an.instanceOf(DynamoDBStore);
});
Expand All @@ -68,6 +79,30 @@ describe("DynamoDBStore", () => {
})
.finally(done);
});

it("should store a valid expiresIn", () => {
const store = new DynamoDBStore({
table: "sessions-test",
expiresIn: 3600
});
store.expiresIn.should.equal(3600);
});

it("should revert expiresIn to 0 when set to a non-integer", () => {
const store = new DynamoDBStore({
table: "sessions-test",
expiresIn: 1.5
});
store.expiresIn.should.equal(0);
});

it("should revert expiresIn to 0 when set to a negative integer", () => {
const store = new DynamoDBStore({
table: "sessions-test",
expiresIn: -10
});
store.expiresIn.should.equal(0);
});
});

describe("Initializing", () => {
Expand Down Expand Up @@ -173,6 +208,16 @@ describe("DynamoDBStore", () => {
});

describe("Setting", () => {
let clock;

beforeEach(() => {
clock = sinon.useFakeTimers(1000000000);
})

afterEach(() => {
clock.restore();
})

it("should store data correctly", async () => {
return new Promise((resolve, reject) => {
const name = Math.random().toString();
Expand Down Expand Up @@ -231,6 +276,38 @@ describe("DynamoDBStore", () => {
});
});
});

it("should set correct expiry when expiresIn option is set", async () => {
const storeWithExpiry = new DynamoDBStore({
client: client,
table: tableName,
expiresIn: 1000
});

await new Promise((resolve, reject) => {
storeWithExpiry.set(
sessionId,
{
cookie: { maxAge: 2000 },
name: "test"
},
(err) => {
if (err) return reject(err);
resolve();
}
);
});

const result = await client.send(
new GetItemCommand({
TableName: tableName,
Key: { id: { S: "sess:" + sessionId } }
})
);

const expiryValue = parseInt(result.Item.expires.N);
expiryValue.should.equal(1000000 + 1000);
});
});

describe("Getting", () => {
Expand Down