refactor: 💡 reduce network requests & call indexedDB directly (#2217)
* refactor: 💡 reduce network requests & call indexedDB directly ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13251
This commit is contained in:
1 parent
c2c0b38187
commit
6c59baa413
9 files changed
+88
-69
No files matched your search
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
{
|
||||
|
||||
@@ -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',
|
||||
{
|
||||
|
||||
@@ -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',
|
||||
{
|
||||
|
||||
@@ -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',
|
||||
{
|
||||
|
||||
@@ -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',
|
||||
{
|
||||
|
||||
@@ -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<boolean>}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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',
|
||||
{
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
{{t 'resources.target.title_plural'}}
|
||||
<DocLink @doc='target' @iconSize='large' />
|
||||
</h2>
|
||||
{{#if @model.allTargets}}
|
||||
{{#if @model.targetsExist}}
|
||||
<p>{{t 'resources.target.description'}}</p>
|
||||
{{/if}}
|
||||
</page.header>
|
||||
|
||||
<page.actions>
|
||||
{{#if (can 'create model' this.scope collection='targets')}}
|
||||
{{#if @model.allTargets}}
|
||||
{{#if @model.targetsExist}}
|
||||
<Rose::LinkButton @route='scopes.scope.targets.new' @style='primary'>{{t
|
||||
'resources.target.titles.new'
|
||||
}}</Rose::LinkButton>
|
||||
@@ -29,7 +29,7 @@
|
||||
{{/if}}
|
||||
</page.actions>
|
||||
<page.body class='search-filtering'>
|
||||
{{#if @model.allTargets}}
|
||||
{{#if @model.targetsExist}}
|
||||
<Hds::SegmentedGroup as |S|>
|
||||
<S.TextInput
|
||||
@value={{this.search}}
|
||||
|
||||
Reference in new issue
Block a user