Skip to content

Commit

Permalink
Add a size method to the Queue class (#2018)
Browse files Browse the repository at this point in the history
* Add a getAll() method to the Queue class

* Fix typo
  • Loading branch information
philipwalton authored Apr 11, 2019
1 parent 632cdd1 commit 9124805
Show file tree
Hide file tree
Showing 5 changed files with 284 additions and 24 deletions.
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

0 comments on commit 9124805

Please sign in to comment.