diff --git a/addons/api/addon/handlers/indexed-db-handler.js b/addons/api/addon/handlers/indexed-db-handler.js index 57baecdc0..2946ef893 100644 --- a/addons/api/addon/handlers/indexed-db-handler.js +++ b/addons/api/addon/handlers/indexed-db-handler.js @@ -19,7 +19,11 @@ export default class IndexedDbHandler { switch (context.request.op) { case 'query': { const { store, data } = context.request; - const { type, query, options: { pushToStore = true } = {} } = data; + const { + type, + query, + options: { pushToStore = true, peekIndexedDB = false } = {}, + } = data; const supportedModels = Object.keys(modelIndexes); const { db: indexedDb } = this.indexedDb ?? {}; @@ -31,66 +35,68 @@ export default class IndexedDbHandler { let { page, pageSize, query: queryObj, ...remainingQuery } = query; - const listToken = await indexedDb.token.get(type); - const adapter = store.adapterFor(type); - const schema = store.modelFor(type); - - let payload; - try { - payload = await adapter.query(store, schema, { - ...remainingQuery, - scope_id: 'global', - recursive: true, - list_token: listToken?.token, - }); - } catch (e) { - // If we get any error that's not a rate limiting error, - // assume the token is not good anymore. We'll delete the token and - // clear the DB and try again without a token. - // TODO: Probably should check for 400 and bad token error specifically instead? - if (e?.errors[0].status !== 429) { - await indexedDb[type].clear(); - await indexedDb.token.delete(type); + if (!peekIndexedDB) { + const listToken = await indexedDb.token.get(type); + const adapter = store.adapterFor(type); + const schema = store.modelFor(type); + let payload; + try { payload = await adapter.query(store, schema, { ...remainingQuery, scope_id: 'global', recursive: true, - list_token: null, + list_token: listToken?.token, }); - } else { - throw e; + } catch (e) { + // If we get any error that's not a rate limiting error, + // assume the token is not good anymore. We'll delete the token and + // clear the DB and try again without a token. + // TODO: Probably should check for 400 and bad token error specifically instead? + if (e?.errors[0].status !== 429) { + await indexedDb[type].clear(); + await indexedDb.token.delete(type); + + payload = await adapter.query(store, schema, { + ...remainingQuery, + scope_id: 'global', + recursive: true, + list_token: null, + }); + } else { + throw e; + } } + + // Store the token we just got back from the payload + await indexedDb.token.put({ + id: type, + token: payload.list_token, + }); + + // Remove any records from the DB if the API indicates they've been deleted + if (payload.removed_ids?.length > 0) { + await indexedDb[type].bulkDelete(payload.removed_ids); + } + + const serializer = store.serializerFor(type); + const normalizedPayload = serializer.normalizeResponse( + store, + schema, + payload, + null, + 'query', + ); + + const { data: payloadData } = normalizedPayload; + + // Store the new data we just got back from the API refresh + const items = payloadData.map((datum) => + this.indexedDb.normalizeData(datum, true), + ); + await indexedDb[type].bulkPut(items); } - // Store the token we just got back from the payload - await indexedDb.token.put({ - id: type, - token: payload.list_token, - }); - - // Remove any records from the DB if the API indicates they've been deleted - if (payload.removed_ids?.length > 0) { - await indexedDb[type].bulkDelete(payload.removed_ids); - } - - const serializer = store.serializerFor(type); - const normalizedPayload = serializer.normalizeResponse( - store, - schema, - payload, - null, - 'query', - ); - - const { data: payloadData } = normalizedPayload; - - // Store the new data we just got back from the API refresh - const items = payloadData.map((datum) => - this.indexedDb.normalizeData(datum, true), - ); - await indexedDb[type].bulkPut(items); - const indexedDbResults = await queryIndexedDb( indexedDb, type, diff --git a/ui/admin/app/routes/scopes/scope/credential-stores/index.js b/ui/admin/app/routes/scopes/scope/credential-stores/index.js index d78dd23a4..c7ac59114 100644 --- a/ui/admin/app/routes/scopes/scope/credential-stores/index.js +++ b/ui/admin/app/routes/scopes/scope/credential-stores/index.js @@ -82,7 +82,7 @@ export default class ScopesScopeCredentialStoresIndexRoute extends Route { if (totalItems > 0) { return true; } - const options = { pushToStore: false }; + const options = { pushToStore: false, peekIndexedDB: true }; const credentialStore = await this.store.query( 'credential-store', { diff --git a/ui/admin/app/routes/scopes/scope/groups/index.js b/ui/admin/app/routes/scopes/scope/groups/index.js index d59b8d6f8..fa6f930f1 100644 --- a/ui/admin/app/routes/scopes/scope/groups/index.js +++ b/ui/admin/app/routes/scopes/scope/groups/index.js @@ -63,7 +63,7 @@ export default class ScopesScopeGroupsIndexRoute extends Route { if (totalItems > 0) { return true; } - const options = { pushToStore: false }; + const options = { pushToStore: false, peekIndexedDB: true }; const group = await this.store.query( 'group', { diff --git a/ui/admin/app/routes/scopes/scope/host-catalogs/index.js b/ui/admin/app/routes/scopes/scope/host-catalogs/index.js index 2fc327fca..12c8334fd 100644 --- a/ui/admin/app/routes/scopes/scope/host-catalogs/index.js +++ b/ui/admin/app/routes/scopes/scope/host-catalogs/index.js @@ -70,7 +70,7 @@ export default class ScopesScopeHostCatalogsIndexRoute extends Route { return true; } - const options = { pushToStore: false }; + const options = { pushToStore: false, peekIndexedDB: true }; const hostCatalogs = await this.store.query( 'host-catalog', { diff --git a/ui/admin/app/routes/scopes/scope/roles/index.js b/ui/admin/app/routes/scopes/scope/roles/index.js index 2701f9aa5..7ce202aed 100644 --- a/ui/admin/app/routes/scopes/scope/roles/index.js +++ b/ui/admin/app/routes/scopes/scope/roles/index.js @@ -66,7 +66,7 @@ export default class ScopesScopeRolesIndexRoute extends Route { if (totalItems > 0) { return true; } - const options = { pushToStore: false }; + const options = { pushToStore: false, peekIndexedDB: true }; const role = await this.store.query( 'role', { diff --git a/ui/admin/app/routes/scopes/scope/scopes/index.js b/ui/admin/app/routes/scopes/scope/scopes/index.js index 31000e5db..89c9bc213 100644 --- a/ui/admin/app/routes/scopes/scope/scopes/index.js +++ b/ui/admin/app/routes/scopes/scope/scopes/index.js @@ -67,7 +67,7 @@ export default class ScopesScopeScopesIndexRoute extends Route { if (totalItems > 0) { return true; } - const options = { pushToStore: false }; + const options = { pushToStore: false, peekIndexedDB: true }; const scopes = await this.store.query( 'scope', { diff --git a/ui/admin/app/routes/scopes/scope/targets/index.js b/ui/admin/app/routes/scopes/scope/targets/index.js index 40e729861..a9d42527a 100644 --- a/ui/admin/app/routes/scopes/scope/targets/index.js +++ b/ui/admin/app/routes/scopes/scope/targets/index.js @@ -45,7 +45,7 @@ export default class ScopesScopeTargetsIndexRoute extends Route { /** * Loads queried targets, the number of targets under current scope, and * active sessions filtering options. - * @returns {Promise<{totalItems: number, targets: [TargetModel], allTargets: [TargetModel] }> } + * @returns {Promise<{totalItems: number, targets: [TargetModel], targetsExist: boolean }> } */ async model({ search, availableSessions, types, page, pageSize }) { const scope = this.modelFor('scopes.scope'); @@ -73,10 +73,9 @@ export default class ScopesScopeTargetsIndexRoute extends Route { }); this.addActiveSessionFilters(filters, availableSessions, sessions); - await this.getAllTargets(scope_id); - let targets; let totalItems = 0; + let targetsExist = false; if (this.can.can('list model', scope, { collection: 'targets' })) { targets = await this.store.query('target', { query: { search, filters }, @@ -84,24 +83,38 @@ export default class ScopesScopeTargetsIndexRoute extends Route { pageSize, }); totalItems = targets.meta?.totalItems; + targetsExist = await this.getTargetsExist(scope_id, totalItems); } - return { targets, allTargets: this.allTargets, totalItems }; + return { targets, targetsExist, totalItems }; } - async getAllTargets(scopeId) { - const options = { pushToStore: false }; - this.allTargets = await this.store.query( + /** + * Sets targetsExist to true if there exists any targets. + * @param {string} scope_id + * @param {number} totalItems + * @returns {Promise} + */ + async getTargetsExist(scope_id, totalItems) { + if (totalItems > 0) { + return true; + } + const options = { pushToStore: false, peekIndexedDB: true }; + const targets = await this.store.query( 'target', { + scope_id, query: { filters: { - scope_id: [{ equals: scopeId }], + scope_id: [{ equals: scope_id }], }, }, + page: 1, + pageSize: 1, }, options, ); + return targets.length > 0; } /** diff --git a/ui/admin/app/routes/scopes/scope/users/index.js b/ui/admin/app/routes/scopes/scope/users/index.js index 3e39061e2..24d5e1422 100644 --- a/ui/admin/app/routes/scopes/scope/users/index.js +++ b/ui/admin/app/routes/scopes/scope/users/index.js @@ -67,7 +67,7 @@ export default class ScopesScopeUsersIndexRoute extends Route { if (totalItems > 0) { return true; } - const options = { pushToStore: false }; + const options = { pushToStore: false, peekIndexedDB: true }; const user = await this.store.query( 'user', { diff --git a/ui/admin/app/templates/scopes/scope/targets/index.hbs b/ui/admin/app/templates/scopes/scope/targets/index.hbs index f65bedbc0..f2a9ba90e 100644 --- a/ui/admin/app/templates/scopes/scope/targets/index.hbs +++ b/ui/admin/app/templates/scopes/scope/targets/index.hbs @@ -14,14 +14,14 @@ {{t 'resources.target.title_plural'}} - {{#if @model.allTargets}} + {{#if @model.targetsExist}}

{{t 'resources.target.description'}}

{{/if}} {{#if (can 'create model' this.scope collection='targets')}} - {{#if @model.allTargets}} + {{#if @model.targetsExist}} {{t 'resources.target.titles.new' }} @@ -29,7 +29,7 @@ {{/if}} - {{#if @model.allTargets}} + {{#if @model.targetsExist}}