dashboard/shell/plugins/steve/worker/web-worker.basic.js

117 lines
3.7 KiB
JavaScript

import { SCHEMA } from '@shell/config/types';
import { hashObj } from '@shell/utils/crypto/browserHashUtils';
import { removeSchemaIndexFields } from '@shell/plugins/steve/schema.utils';
const SCHEMA_FLUSH_TIMEOUT = 2500;
const state = {
store: '', // Store name
flushTimer: undefined, // Timer to flush the schema change queue
queue: [], // Schema change queue
schemas: {} // Map of schema id to hash to track when a schema actually changes
};
function flush() {
state.queue.forEach((schema) => {
const hash = hashObj(schema);
const existing = state.schemas[schema.id];
if (!existing || (existing && existing !== hash)) {
// console.log(`${ schema.id } CHANGED ${ hash } > ${ existing }`);
state.schemas[schema.id] = hash;
const msg = {
data: schema,
resourceType: SCHEMA,
type: 'resource.change'
};
load(msg);
}
});
state.queue = [];
state.flushTimer = setTimeout(flush, SCHEMA_FLUSH_TIMEOUT);
}
state.flushTimer = setTimeout(flush, SCHEMA_FLUSH_TIMEOUT);
// Callback to the store's load function (in the main thread) to process a load
function load(data) {
self.postMessage({ load: data });
}
// used for dispatching a function in the worker, primarily for redirecting messages intended for the advanced worker back to the UI thread
function redispatch(msg) {
self.postMessage({ redispatch: msg });
}
/**
* These actions aren't applicable to the basic worker, so bounce back to ui thread
*
* These are called when a queue of actions is flushed. Queue is populated from requests made before we know if worker is basic or advanced.
*/
const advancedWorkerActions = {
watch: (msg) => {
redispatch({ send: msg });
},
createWatcher: (msg) => {
redispatch({ subscribe: msg });
}
};
const workerActions = {
onmessage: (e) => {
/* on the off chance there's more than key in the message, we handle them in the order that they "keys" method provides which is
// good enough for now considering that we never send more than one message action at a time right now */
const messageActions = Object.keys(e?.data);
messageActions.forEach((action) => {
if (workerActions[action]) {
workerActions[action](e?.data[action]);
} else {
console.warn('no associated action for:', action); // eslint-disable-line no-console
}
});
},
initWorker: ({ storeName }) => {
state.store = storeName;
},
destroyWorker: () => {
clearTimeout(state.flushTimer);
self.postMessage({ destroyWorker: true }); // we're only passing the boolean here because the key needs to be something truthy to ensure it's passed on the object.
},
// Called to load schema
loadSchemas: (schemas) => {
schemas.forEach((schema) => {
// These properties are added to the object, but aren't on the raw object, so remove them
// otherwise our comparison will show changes when there aren't any
removeSchemaIndexFields(schema);
state.schemas[schema.id] = hashObj(schema);
});
},
// Called when schema is updated
updateSchema: (schema) => {
// Add the schema to the queue to be checked to see if the schema really changed
state.queue.push(schema);
},
// Remove the cached schema
removeSchema: (id) => {
// Remove anything in the queue related to the schema - we don't want to send any pending updates later for a schema that has been removed
state.queue = state.queue.filter((schema) => schema.id !== id);
// Delete the schema from the map, so if it comes back we don't ignore it if the hash is the same
delete state.schemas[id];
},
...advancedWorkerActions
};
self.onmessage = workerActions.onmessage; // bind everything to the worker's onmessage handler via the workerAction