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

Add a size method to the Queue class #2018

Merged
merged 2 commits into from
Apr 11, 2019
Merged
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
56 changes: 50 additions & 6 deletions packages/workbox-background-sync/Queue.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,32 @@ class Queue {
return this._removeRequest('shift');
}

/**
* Returns all the entries that have not expired (per `maxRetentionTime`).
* Any expired entries are removed from the queue.
*
* @return {Promise<Array<Object>>}
*/
async getAll() {
const allEntries = await this._queueStore.getAll();
const now = Date.now();

const unexpiredEntries = [];
for (const entry of allEntries) {
// Ignore requests older than maxRetentionTime. Call this function
// recursively until an unexpired request is found.
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
if (now - entry.timestamp > maxRetentionTimeInMs) {
await this._queueStore.deleteEntry(entry.id);
} else {
unexpiredEntries.push(convertEntry(entry));
}
}

return unexpiredEntries;
}


/**
* Adds the entry to the QueueStore and registers for a sync event.
*
Expand Down Expand Up @@ -204,7 +230,7 @@ class Queue {

/**
* Removes and returns the first or last (depending on `operation`) entry
* form the QueueStore that's not older than the `maxRetentionTime`.
* from the QueueStore that's not older than the `maxRetentionTime`.
*
* @param {string} operation ('pop' or 'shift')
* @return {Object|undefined}
Expand All @@ -214,18 +240,15 @@ class Queue {
const now = Date.now();
const entry = await this._queueStore[`${operation}Entry`]();

if (entry ) {
if (entry) {
// Ignore requests older than maxRetentionTime. Call this function
// recursively until an unexpired request is found.
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
if (now - entry.timestamp > maxRetentionTimeInMs) {
return this._removeRequest(operation);
}

entry.request = new StorableRequest(entry.requestData).toRequest();
delete entry.requestData;

return entry;
return convertEntry(entry);
}
}

Expand Down Expand Up @@ -347,4 +370,25 @@ class Queue {
}
}


/**
* Converts a QueueStore entry into the format exposed by Queue. This entails
* converting the request data into a real request and omitting the `id` and
* `queueName` properties.
*
* @param {Object} queueStoreEntry
* @return {Object}
* @private
*/
const convertEntry = (queueStoreEntry) => {
const queueEntry = {
request: new StorableRequest(queueStoreEntry.requestData).toRequest(),
timestamp: queueStoreEntry.timestamp,
};
if (queueStoreEntry.metadata) {
queueEntry.metadata = queueStoreEntry.metadata;
}
return queueEntry;
};

export {Queue};
36 changes: 31 additions & 5 deletions packages/workbox-background-sync/lib/QueueStore.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export class QueueStore {
// Pick an ID one less than the lowest ID in the object store.
entry.id = firstEntry.id - 1;
} else {
// Otherwise let the auto-incrementor assign the ID.
delete entry.id;
}
entry.queueName = this._queueName;
Expand Down Expand Up @@ -129,6 +130,35 @@ export class QueueStore {
return this._removeEntry({direction: 'next'});
}

/**
* Returns all entries in the store matching the `queueName`.
*
* @param {Object} options See workbox.backgroundSync.Queue~getAll}
* @return {Promise<Array<Object>>}
* @private
*/
async getAll() {
return await this._db.getAllMatching(OBJECT_STORE_NAME, {
index: INDEXED_PROP,
query: IDBKeyRange.only(this._queueName),
});
}

/**
* Deletes the entry for the given ID.
*
* WARNING: this method does not ensure the deleted enry belongs to this
* queue (i.e. matches the `queueName`). But this limitation is acceptable
* as this class is not publicly exposed. An additional check would make
* this method slower than it needs to be.
*
* @private
* @param {number} id
*/
async deleteEntry(id) {
await this._db.delete(OBJECT_STORE_NAME, id);
}

/**
* Removes and returns the first or last entry in the queue (based on the
* `direction` argument) matching the `queueName`.
Expand All @@ -145,11 +175,7 @@ export class QueueStore {
});

if (entry) {
await this._db.delete(OBJECT_STORE_NAME, entry.id);

// Dont' expose the ID or queueName;
delete entry.id;
delete entry.queueName;
await this.deleteEntry(entry.id);
return entry;
}
}
Expand Down
1 change: 0 additions & 1 deletion packages/workbox-expiration/Plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import './_version.mjs';
* used immediately.
*
* When using `maxEntries`, the entry least-recently requested will be removed from the cache first.
*
*
* @memberof workbox.expiration
*/
Expand Down
158 changes: 146 additions & 12 deletions test/workbox-background-sync/sw/lib/test-QueueStore.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,8 @@ describe(`QueueStore`, function() {
expect(sr2a.requestData).to.deep.equal(sr2.toObject());
expect(sr2a.timestamp).to.equal(2000);
expect(sr2a.metadata).to.deep.equal({name: 'meta2'});
// It should not return the ID or queue name.
expect(sr2a.id).to.be.undefined;
expect(sr2a.queueName).to.be.undefined;
expect(sr2a.id).to.equal(firstId + 1);
expect(sr2a.queueName).to.equal('b');

entries = await db.getAll('requests');
expect(entries).to.have.lengthOf(4);
Expand All @@ -434,9 +433,8 @@ describe(`QueueStore`, function() {
expect(sr1a.requestData).to.deep.equal(sr1.toObject());
expect(sr1a.timestamp).to.equal(1000);
expect(sr1a.metadata).to.deep.equal({name: 'meta1'});
// It should not return the ID or queue name.
expect(sr1a.id).to.be.undefined;
expect(sr1a.queueName).to.be.undefined;
expect(sr1a.id).to.equal(firstId);
expect(sr1a.queueName).to.equal('a');

entries = await db.getAll('requests');
expect(entries).to.have.lengthOf(3);
Expand Down Expand Up @@ -494,9 +492,8 @@ describe(`QueueStore`, function() {
expect(sr4a.requestData).to.deep.equal(sr4.toObject());
expect(sr4a.timestamp).to.equal(4000);
expect(sr4a.metadata).to.deep.equal({name: 'meta4'});
// It should not return the ID or queue name.
expect(sr4a.id).to.be.undefined;
expect(sr4a.queueName).to.be.undefined;
expect(sr4a.id).to.equal(firstId + 3);
expect(sr4a.queueName).to.equal('b');

entries = await db.getAll('requests');
expect(entries).to.have.lengthOf(4);
Expand All @@ -517,9 +514,8 @@ describe(`QueueStore`, function() {
expect(sr5a.requestData).to.deep.equal(sr5.toObject());
expect(sr5a.timestamp).to.equal(5000);
expect(sr5a.metadata).to.deep.equal({name: 'meta5'});
// It should not return the ID or queue name.
expect(sr5a.id).to.be.undefined;
expect(sr5a.queueName).to.be.undefined;
expect(sr5a.id).to.equal(firstId + 4);
expect(sr5a.queueName).to.equal('a');

entries = await db.getAll('requests');
expect(entries).to.have.lengthOf(3);
Expand All @@ -528,4 +524,142 @@ describe(`QueueStore`, function() {
expect(entries[2].id).to.equal(firstId + 2);
});
});

describe(`getAll`, function() {
it(`should return all entries in IDB with the right queue name`, async function() {
const queueStore1 = new QueueStore('a');
const queueStore2 = new QueueStore('b');

const sr1 = await StorableRequest.fromRequest(new Request('/one'));
const sr2 = await StorableRequest.fromRequest(new Request('/two'));
const sr3 = await StorableRequest.fromRequest(new Request('/three'));
const sr4 = await StorableRequest.fromRequest(new Request('/four'));
const sr5 = await StorableRequest.fromRequest(new Request('/five'));

await queueStore1.pushEntry({
requestData: sr1.toObject(),
timestamp: 1000,
metadata: {name: 'meta1'},
});

let entries = await db.getAll('requests');
const firstId = entries[0].id;

await queueStore2.pushEntry({
requestData: sr2.toObject(),
timestamp: 2000,
metadata: {name: 'meta2'},
});
await queueStore2.pushEntry({
requestData: sr3.toObject(),
timestamp: 3000,
metadata: {name: 'meta3'},
});
await queueStore2.pushEntry({
requestData: sr4.toObject(),
timestamp: 4000,
metadata: {name: 'meta4'},
});
await queueStore1.pushEntry({
requestData: sr5.toObject(),
timestamp: 5000,
metadata: {name: 'meta5'},
});

expect(await queueStore1.getAll()).to.deep.equal([
{
id: firstId,
queueName: 'a',
requestData: sr1.toObject(),
timestamp: 1000,
metadata: {name: 'meta1'},
},
{
id: firstId + 4,
queueName: 'a',
requestData: sr5.toObject(),
timestamp: 5000,
metadata: {name: 'meta5'},
},
]);

expect(await queueStore2.getAll()).to.deep.equal([
{
id: firstId + 1,
queueName: 'b',
requestData: sr2.toObject(),
timestamp: 2000,
metadata: {name: 'meta2'},
},
{
id: firstId + 2,
queueName: 'b',
requestData: sr3.toObject(),
timestamp: 3000,
metadata: {name: 'meta3'},
},
{
id: firstId + 3,
queueName: 'b',
requestData: sr4.toObject(),
timestamp: 4000,
metadata: {name: 'meta4'},
},
]);

await db.clear('requests');

expect(await queueStore1.getAll()).to.deep.equal([]);
expect(await queueStore2.getAll()).to.deep.equal([]);
});
});

describe(`delete`, function() {
it(`should delete an entry for the given ID`, async function() {
const queueStore = new QueueStore('a');

const sr1 = await StorableRequest.fromRequest(new Request('/one'));
const sr2 = await StorableRequest.fromRequest(new Request('/two'));
const sr3 = await StorableRequest.fromRequest(new Request('/three'));

await queueStore.pushEntry({
requestData: sr1.toObject(),
timestamp: 1000,
metadata: {name: 'meta1'},
});

let entries = await db.getAll('requests');
const firstId = entries[0].id;

await queueStore.pushEntry({
requestData: sr2.toObject(),
timestamp: 2000,
metadata: {name: 'meta2'},
});
await queueStore.pushEntry({
requestData: sr3.toObject(),
timestamp: 3000,
metadata: {name: 'meta3'},
});

await queueStore.deleteEntry(firstId + 1);

expect(await db.getAll('requests')).to.deep.equal([
{
id: firstId,
queueName: 'a',
requestData: sr1.toObject(),
timestamp: 1000,
metadata: {name: 'meta1'},
},
{
id: firstId + 2,
queueName: 'a',
requestData: sr3.toObject(),
timestamp: 3000,
metadata: {name: 'meta3'},
},
]);
});
});
});
Loading