123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- import { reactive, ref, computed } from "vue";
- import { defineStore } from "pinia";
- import { generateUuid } from "@common/utils/generateUuid";
- import { forEachIn } from "@common/utils/forEachIn";
- import { useWebsocketStore } from "./websocket";
- import Model from "@/Model";
- export const useModelStore = defineStore("model", () => {
- const { runJob, subscribe, subscribeMany, unsubscribe } =
- useWebsocketStore();
- const models = ref([]);
- const permissions = ref({});
- const createdSubcription = ref(null);
- const subscriptions = ref({
- created: {},
- updated: {},
- deleted: {}
- });
- const loadedModelIds = computed(() =>
- models.value.map(model => `${model._name}.${model._id}`)
- );
- const getUserModelPermissions = async (modelName: string) => {
- if (permissions.value[modelName]) return permissions.value[modelName];
- const data = await runJob("data.users.getModelPermissions", {
- modelName
- });
- permissions.value[modelName] = data;
- return permissions.value[modelName];
- };
- const hasPermission = async (modelName: string, permission: string) => {
- const data = await getUserModelPermissions(modelName);
- return !!data[permission];
- };
- const unregisterModels = async modelIds =>
- forEachIn(
- Array.isArray(modelIds) ? modelIds : [modelIds],
- async modelId => {
- const model = models.value.find(model => model._id === modelId);
- if (!model || model.getUses() > 1) {
- model?.removeUse();
- return;
- }
- await model.unregisterRelations();
- const { updated, deleted } = model.getSubscriptions() ?? {};
- if (updated)
- await unsubscribe(
- `model.${model.getName()}.updated.${modelId}`,
- updated
- );
- if (deleted)
- await unsubscribe(
- `model.${model.getName()}.deleted.${modelId}`,
- deleted
- );
- models.value.splice(
- models.value.findIndex(model => model._id === modelId),
- 1
- );
- }
- );
- const onCreatedCallback = async (modelName: string, data) => {
- if (!subscriptions.value.created[modelName]) return;
- await forEachIn(
- Object.values(subscriptions.value.created[modelName]),
- async subscription => subscription(data) // TODO: Error handling
- );
- };
- const onCreated = async (
- modelName: string,
- callback: (data?: any) => any
- ) => {
- if (!createdSubcription.value)
- createdSubcription.value = await subscribe(
- `model.${modelName}.created`,
- data => onCreatedCallback(modelName, data)
- );
- const uuid = generateUuid();
- subscriptions.value.created[modelName] ??= {};
- subscriptions.value.created[modelName][uuid] = callback;
- return uuid;
- };
- const onUpdated = async (
- modelName: string,
- callback: (data?: any) => any
- ) => {
- const uuid = generateUuid();
- subscriptions.value.updated[modelName] ??= {};
- subscriptions.value.updated[modelName][uuid] = callback;
- return uuid;
- };
- const onUpdatedCallback = async (modelName: string, { doc }) => {
- const model = models.value.find(model => model._id === doc._id);
- if (model) model.updateData(doc);
- if (!subscriptions.value.updated[modelName]) return;
- await forEachIn(
- Object.values(subscriptions.value.updated[modelName]),
- async subscription => subscription(data) // TODO: Error handling
- );
- };
- const onDeleted = async (
- modelName: string,
- callback: (data?: any) => any
- ) => {
- const uuid = generateUuid();
- subscriptions.value.deleted[modelName] ??= {};
- subscriptions.value.deleted[modelName][uuid] = callback;
- return uuid;
- };
- const onDeletedCallback = async (modelName: string, data) => {
- const { oldDoc } = data;
- if (subscriptions.value.deleted[modelName])
- await forEachIn(
- Object.values(subscriptions.value.deleted[modelName]),
- async subscription => subscription(data) // TODO: Error handling
- );
- const index = models.value.findIndex(model => model._id === oldDoc._id);
- if (index > -1) await unregisterModels(oldDoc._id);
- };
- const removeCallback = async (
- modelName: string,
- type: "created" | "updated" | "deleted",
- uuid: string
- ) => {
- if (
- !subscriptions.value[type][modelName] ||
- !subscriptions.value[type][modelName][uuid]
- )
- return;
- delete subscriptions.value[type][modelName][uuid];
- if (
- type === "created" &&
- Object.keys(subscriptions.value.created[modelName]).length === 0
- ) {
- await unsubscribe(
- `model.${modelName}.created`,
- createdSubcription.value
- );
- createdSubcription.value = null;
- }
- };
- const registerModels = async (
- docs,
- relations?: Record<string, string | string[]>
- ) => {
- const documents = Array.isArray(docs) ? docs : [docs];
- const existingsRefs = documents.filter(document =>
- models.value.find(
- model =>
- model._id === document._id && model._name === document._name
- )
- );
- await forEachIn(existingsRefs, async model => {
- model.addUse();
- if (relations && relations[model._name])
- await model.loadRelations(relations[model._name]);
- });
- if (documents.length === existingsRefs.length) return existingsRefs;
- const missingDocuments = documents.filter(
- document =>
- !loadedModelIds.value.includes(
- `${document._name}.${document._id}`
- )
- );
- const channels = Object.fromEntries(
- missingDocuments.flatMap(document => [
- [
- `model.${document._name}.updated.${document._id}`,
- data => onUpdatedCallback(document._name, data)
- ],
- [
- `model.${document._name}.deleted.${document._id}`,
- data => onDeletedCallback(document._name, data)
- ]
- ])
- );
- const subscriptions = Object.entries(await subscribeMany(channels));
- const newRefs = await forEachIn(missingDocuments, async document => {
- const refSubscriptions = subscriptions.filter(([, { channel }]) =>
- channel.endsWith(document._id)
- );
- const [updated] = refSubscriptions.find(([, { channel }]) =>
- channel.includes("updated")
- );
- const [deleted] = refSubscriptions.find(([, { channel }]) =>
- channel.includes("deleted")
- );
- if (!updated || !deleted) return null;
- const model = reactive(new Model(document));
- model.setSubscriptions(updated, deleted);
- model.addUse();
- if (relations && relations[model._name])
- await model.loadRelations(relations[model._name]);
- return model;
- });
- models.value.push(...newRefs);
- return existingsRefs.concat(newRefs);
- };
- const findById = async (modelName: string, _id) => {
- const existingModel = models.value.find(model => model._id === _id);
- if (existingModel) return existingModel;
- return runJob(`data.${modelName}.findById`, { _id });
- };
- const findManyById = async (modelName: string, _ids: string[]) => {
- const existingModels = models.value.filter(model =>
- _ids.includes(model._id)
- );
- const existingIds = existingModels.map(model => model._id);
- const missingIds = _ids.filter(_id => !existingIds.includes(_id));
- let fetchedModels = [];
- if (missingIds.length > 0)
- fetchedModels = (await runJob(`data.${modelName}.findManyById`, {
- _ids: missingIds
- })) as unknown[];
- const allModels = existingModels.concat(fetchedModels);
- return Object.fromEntries(
- _ids.map(_id => [_id, allModels.find(model => model._id === _id)])
- );
- };
- const loadModels = async (
- modelName: string,
- modelIdsOrModelId: string | string[],
- relations?: Record<string, string | string[]>
- ) => {
- const modelIds = Array.isArray(modelIdsOrModelId)
- ? modelIdsOrModelId
- : [modelIdsOrModelId];
- const missingModelIds = modelIds.filter(
- modelId => !loadedModelIds.value.includes(`${modelName}.${modelId}`)
- );
- const missingModels = Object.values(
- await findManyById(modelName, missingModelIds)
- );
- await registerModels(missingModels, relations);
- return models.value.filter(
- model => modelIds.includes(model._id) && model._name === modelName
- );
- };
- return {
- models,
- permissions,
- subscriptions,
- onCreated,
- onUpdated,
- onDeleted,
- removeCallback,
- registerModels,
- unregisterModels,
- getUserModelPermissions,
- hasPermission,
- findById,
- findManyById,
- loadModels
- };
- });
|