Files
threshold-ui/addons/api/addon/services/indexed-db.js
T

147 lines
4.9 KiB
JavaScript
Raw Normal View History

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
2024-03-13 15:52:55 -04:00
import Service from '@ember/service';
2024-02-05 15:59:58 -05:00
import Dexie from 'dexie';
import { typeOf } from '@ember/utils';
// List of resources we support to be cached and searched in
// indexedDb. Any field that can be searched upon should be added to
// this index. Increment the version number of the database whenever
// indexes change.
export const modelIndexes = {
2024-03-13 15:52:55 -04:00
token: '&id, token',
target:
'&id, attributes.created_time, attributes.type, attributes.name, attributes.description, attributes.address, attributes.scope.scope_id',
session:
'&id, attributes.created_time, attributes.type, attributes.status, attributes.endpoint, attributes.target_id, attributes.user_id, attributes.scope.scope_id',
user: '&id, attributes.created_time, attributes.name, attributes.description, attributes.scope.scope_id',
group:
'&id, attributes.created_time, attributes.name, attributes.description, attributes.scope.scope_id',
role: '&id, attributes.created_time, attributes.name, attributes.description, attributes.scope.scope_id',
scope:
'&id, attributes.created_time, attributes.type, attributes.name, attributes.description, attributes.scope.scope_id',
'credential-store':
'&id, attributes.created_time, attributes.name, attributes.description, attributes.type, attributes.scope.scope_id',
'host-catalog':
'&id, attributes.created_time, attributes.type, attributes.name, attributes.description, attributes.scope.scope_id, attributes.plugin.name',
'auth-method':
'&id, attributes.created_time, attributes.type, attributes.name, attributes.description, attributes.is_primary, attributes.scope.scope_id',
2024-02-05 15:59:58 -05:00
};
export const formatDbName = (userId, clusterUrl) =>
`boundary-${userId}-${clusterUrl}`;
/**
2024-04-11 12:39:24 -04:00
* Recursively copy over the object and convert any booleans to "true" or "false" so they can be indexed.
* Also converts any ember-like arrays back to a standard array.
2024-02-05 15:59:58 -05:00
* @param result
* @param object
* @returns {*}
*/
2024-04-11 12:39:24 -04:00
const convertFields = (result, object) => {
2024-02-05 15:59:58 -05:00
Object.entries(object).forEach(([key, value]) => {
if (typeOf(value) === 'object') {
2024-04-11 12:39:24 -04:00
result[key] = convertFields({}, value);
2024-02-05 15:59:58 -05:00
} else if (typeOf(value) === 'boolean') {
result[key] = value ? 'true' : 'false';
2024-04-11 12:39:24 -04:00
} else if (typeOf(value) === 'array') {
result[key] = [...value];
2024-02-05 15:59:58 -05:00
} else {
result[key] = value;
}
});
return result;
};
/**
2024-04-11 12:39:24 -04:00
* Recursively copy over the object and convert any string "true" or "false" back to booleans.
* Will also convert any array fields back using their model transformation.
2024-02-05 15:59:58 -05:00
* @param result
* @param object
2024-04-11 12:39:24 -04:00
* @param schema
* @param serializer
2024-02-05 15:59:58 -05:00
* @returns {*}
*/
2024-04-11 12:39:24 -04:00
const unconvertFields = (result, object, schema, serializer) => {
2024-02-05 15:59:58 -05:00
Object.entries(object).forEach(([key, value]) => {
if (typeOf(value) === 'object') {
2024-04-11 12:39:24 -04:00
result[key] = unconvertFields({}, value, schema, serializer);
2024-02-05 15:59:58 -05:00
} else if (value === 'true' || value === 'false') {
2024-03-13 15:52:55 -04:00
result[key] = value === 'true';
2024-04-11 12:39:24 -04:00
} else if (typeOf(value) === 'array') {
const attribute = schema.attributes.get(key);
// If attribute type is array, we transform it back to a tracked array
if (attribute?.type === 'array') {
const transformer = serializer.transformFor(attribute.type);
result[key] = transformer.deserialize(value, attribute.options);
return;
}
result[key] = value;
2024-02-05 15:59:58 -05:00
} else {
result[key] = value;
}
});
return result;
};
/**
* Service to encapsulate the IndexedDB implementation. To use this service, call
* `setup` from the application root and
*/
export default class IndexedDbService extends Service {
// =attributes
#db;
get db() {
return this.#db;
}
setup(dbName) {
// Don't run setup again if we already have one or if we didn't get a name
if (this.#db || !dbName) {
return;
}
this.#db = new Dexie(dbName);
this.#db.version(1).stores(modelIndexes);
}
/**
2024-04-11 12:39:24 -04:00
* Cleanup the data by converting all fields to plain javascript objects.
* We will also convert booleans to "false" or "true".
2024-02-05 15:59:58 -05:00
* IndexedDB doesn't support indexes on boolean values so we have to
* convert them to be able to support using boolean values.
*
2024-04-11 12:39:24 -04:00
* Undo the operation by setting `convertFields` to false.
2024-02-05 15:59:58 -05:00
*
* @param {Object} data
2024-04-11 12:39:24 -04:00
* @param {Boolean} cleanData
* @param {Object} schema
* @param {Object} serializer
2024-02-05 15:59:58 -05:00
* @return {{id, type, attributes: {}, relationships: {}}}
*/
2024-04-11 12:39:24 -04:00
normalizeData({ data, cleanData, schema, serializer }) {
2024-02-05 15:59:58 -05:00
if (!data) {
return data;
}
let { id, type, attributes = {}, relationships = {} } = data;
return {
id,
type,
2024-04-11 12:39:24 -04:00
attributes: cleanData
? convertFields({}, attributes)
: unconvertFields({}, attributes, schema, serializer),
relationships: cleanData
? convertFields({}, relationships)
: unconvertFields({}, relationships, schema, serializer),
2024-02-05 15:59:58 -05:00
};
}
}