| 1 | /*
|
|---|
| 2 | Copyright 2018 Google LLC
|
|---|
| 3 |
|
|---|
| 4 | Use of this source code is governed by an MIT-style
|
|---|
| 5 | license that can be found in the LICENSE file or at
|
|---|
| 6 | https://opensource.org/licenses/MIT.
|
|---|
| 7 | */
|
|---|
| 8 |
|
|---|
| 9 | import {assert} from 'workbox-core/_private/assert.js';
|
|---|
| 10 | import {
|
|---|
| 11 | UnidentifiedQueueStoreEntry,
|
|---|
| 12 | QueueStoreEntry,
|
|---|
| 13 | QueueDb,
|
|---|
| 14 | } from './QueueDb.js';
|
|---|
| 15 | import '../_version.js';
|
|---|
| 16 |
|
|---|
| 17 | /**
|
|---|
| 18 | * A class to manage storing requests from a Queue in IndexedDB,
|
|---|
| 19 | * indexed by their queue name for easier access.
|
|---|
| 20 | *
|
|---|
| 21 | * Most developers will not need to access this class directly;
|
|---|
| 22 | * it is exposed for advanced use cases.
|
|---|
| 23 | */
|
|---|
| 24 | export class QueueStore {
|
|---|
| 25 | private readonly _queueName: string;
|
|---|
| 26 | private readonly _queueDb: QueueDb;
|
|---|
| 27 |
|
|---|
| 28 | /**
|
|---|
| 29 | * Associates this instance with a Queue instance, so entries added can be
|
|---|
| 30 | * identified by their queue name.
|
|---|
| 31 | *
|
|---|
| 32 | * @param {string} queueName
|
|---|
| 33 | */
|
|---|
| 34 | constructor(queueName: string) {
|
|---|
| 35 | this._queueName = queueName;
|
|---|
| 36 | this._queueDb = new QueueDb();
|
|---|
| 37 | }
|
|---|
| 38 |
|
|---|
| 39 | /**
|
|---|
| 40 | * Append an entry last in the queue.
|
|---|
| 41 | *
|
|---|
| 42 | * @param {Object} entry
|
|---|
| 43 | * @param {Object} entry.requestData
|
|---|
| 44 | * @param {number} [entry.timestamp]
|
|---|
| 45 | * @param {Object} [entry.metadata]
|
|---|
| 46 | */
|
|---|
| 47 | async pushEntry(entry: UnidentifiedQueueStoreEntry): Promise<void> {
|
|---|
| 48 | if (process.env.NODE_ENV !== 'production') {
|
|---|
| 49 | assert!.isType(entry, 'object', {
|
|---|
| 50 | moduleName: 'workbox-background-sync',
|
|---|
| 51 | className: 'QueueStore',
|
|---|
| 52 | funcName: 'pushEntry',
|
|---|
| 53 | paramName: 'entry',
|
|---|
| 54 | });
|
|---|
| 55 | assert!.isType(entry.requestData, 'object', {
|
|---|
| 56 | moduleName: 'workbox-background-sync',
|
|---|
| 57 | className: 'QueueStore',
|
|---|
| 58 | funcName: 'pushEntry',
|
|---|
| 59 | paramName: 'entry.requestData',
|
|---|
| 60 | });
|
|---|
| 61 | }
|
|---|
| 62 |
|
|---|
| 63 | // Don't specify an ID since one is automatically generated.
|
|---|
| 64 | delete entry.id;
|
|---|
| 65 | entry.queueName = this._queueName;
|
|---|
| 66 |
|
|---|
| 67 | await this._queueDb.addEntry(entry);
|
|---|
| 68 | }
|
|---|
| 69 |
|
|---|
| 70 | /**
|
|---|
| 71 | * Prepend an entry first in the queue.
|
|---|
| 72 | *
|
|---|
| 73 | * @param {Object} entry
|
|---|
| 74 | * @param {Object} entry.requestData
|
|---|
| 75 | * @param {number} [entry.timestamp]
|
|---|
| 76 | * @param {Object} [entry.metadata]
|
|---|
| 77 | */
|
|---|
| 78 | async unshiftEntry(entry: UnidentifiedQueueStoreEntry): Promise<void> {
|
|---|
| 79 | if (process.env.NODE_ENV !== 'production') {
|
|---|
| 80 | assert!.isType(entry, 'object', {
|
|---|
| 81 | moduleName: 'workbox-background-sync',
|
|---|
| 82 | className: 'QueueStore',
|
|---|
| 83 | funcName: 'unshiftEntry',
|
|---|
| 84 | paramName: 'entry',
|
|---|
| 85 | });
|
|---|
| 86 | assert!.isType(entry.requestData, 'object', {
|
|---|
| 87 | moduleName: 'workbox-background-sync',
|
|---|
| 88 | className: 'QueueStore',
|
|---|
| 89 | funcName: 'unshiftEntry',
|
|---|
| 90 | paramName: 'entry.requestData',
|
|---|
| 91 | });
|
|---|
| 92 | }
|
|---|
| 93 |
|
|---|
| 94 | const firstId = await this._queueDb.getFirstEntryId();
|
|---|
| 95 |
|
|---|
| 96 | if (firstId) {
|
|---|
| 97 | // Pick an ID one less than the lowest ID in the object store.
|
|---|
| 98 | entry.id = firstId - 1;
|
|---|
| 99 | } else {
|
|---|
| 100 | // Otherwise let the auto-incrementor assign the ID.
|
|---|
| 101 | delete entry.id;
|
|---|
| 102 | }
|
|---|
| 103 | entry.queueName = this._queueName;
|
|---|
| 104 |
|
|---|
| 105 | await this._queueDb.addEntry(entry);
|
|---|
| 106 | }
|
|---|
| 107 |
|
|---|
| 108 | /**
|
|---|
| 109 | * Removes and returns the last entry in the queue matching the `queueName`.
|
|---|
| 110 | *
|
|---|
| 111 | * @return {Promise<QueueStoreEntry|undefined>}
|
|---|
| 112 | */
|
|---|
| 113 | async popEntry(): Promise<QueueStoreEntry | undefined> {
|
|---|
| 114 | return this._removeEntry(
|
|---|
| 115 | await this._queueDb.getLastEntryByQueueName(this._queueName),
|
|---|
| 116 | );
|
|---|
| 117 | }
|
|---|
| 118 |
|
|---|
| 119 | /**
|
|---|
| 120 | * Removes and returns the first entry in the queue matching the `queueName`.
|
|---|
| 121 | *
|
|---|
| 122 | * @return {Promise<QueueStoreEntry|undefined>}
|
|---|
| 123 | */
|
|---|
| 124 | async shiftEntry(): Promise<QueueStoreEntry | undefined> {
|
|---|
| 125 | return this._removeEntry(
|
|---|
| 126 | await this._queueDb.getFirstEntryByQueueName(this._queueName),
|
|---|
| 127 | );
|
|---|
| 128 | }
|
|---|
| 129 |
|
|---|
| 130 | /**
|
|---|
| 131 | * Returns all entries in the store matching the `queueName`.
|
|---|
| 132 | *
|
|---|
| 133 | * @param {Object} options See {@link workbox-background-sync.Queue~getAll}
|
|---|
| 134 | * @return {Promise<Array<Object>>}
|
|---|
| 135 | */
|
|---|
| 136 | async getAll(): Promise<QueueStoreEntry[]> {
|
|---|
| 137 | return await this._queueDb.getAllEntriesByQueueName(this._queueName);
|
|---|
| 138 | }
|
|---|
| 139 |
|
|---|
| 140 | /**
|
|---|
| 141 | * Returns the number of entries in the store matching the `queueName`.
|
|---|
| 142 | *
|
|---|
| 143 | * @param {Object} options See {@link workbox-background-sync.Queue~size}
|
|---|
| 144 | * @return {Promise<number>}
|
|---|
| 145 | */
|
|---|
| 146 | async size(): Promise<number> {
|
|---|
| 147 | return await this._queueDb.getEntryCountByQueueName(this._queueName);
|
|---|
| 148 | }
|
|---|
| 149 |
|
|---|
| 150 | /**
|
|---|
| 151 | * Deletes the entry for the given ID.
|
|---|
| 152 | *
|
|---|
| 153 | * WARNING: this method does not ensure the deleted entry belongs to this
|
|---|
| 154 | * queue (i.e. matches the `queueName`). But this limitation is acceptable
|
|---|
| 155 | * as this class is not publicly exposed. An additional check would make
|
|---|
| 156 | * this method slower than it needs to be.
|
|---|
| 157 | *
|
|---|
| 158 | * @param {number} id
|
|---|
| 159 | */
|
|---|
| 160 | async deleteEntry(id: number): Promise<void> {
|
|---|
| 161 | await this._queueDb.deleteEntry(id);
|
|---|
| 162 | }
|
|---|
| 163 |
|
|---|
| 164 | /**
|
|---|
| 165 | * Removes and returns the first or last entry in the queue (based on the
|
|---|
| 166 | * `direction` argument) matching the `queueName`.
|
|---|
| 167 | *
|
|---|
| 168 | * @return {Promise<QueueStoreEntry|undefined>}
|
|---|
| 169 | * @private
|
|---|
| 170 | */
|
|---|
| 171 | async _removeEntry(
|
|---|
| 172 | entry?: QueueStoreEntry,
|
|---|
| 173 | ): Promise<QueueStoreEntry | undefined> {
|
|---|
| 174 | if (entry) {
|
|---|
| 175 | await this.deleteEntry(entry.id);
|
|---|
| 176 | }
|
|---|
| 177 | return entry;
|
|---|
| 178 | }
|
|---|
| 179 | }
|
|---|