UNPKG

@directus/api

Version:

Directus is a real-time API and App dashboard for managing SQL database content

104 lines (103 loc) 4.15 kB
import { EventEmitter, on } from 'events'; import { useBus } from '../../bus/index.js'; import { getSchema } from '../../utils/get-schema.js'; import { getPayload } from '../../websocket/utils/items.js'; import { getQuery } from './schema/parse-query.js'; const messages = createPubSub(new EventEmitter()); export function bindPubSub() { const messenger = useBus(); messenger.subscribe('websocket.event', (message) => { messages.publish(`${message['collection']}_mutated`, message); }); } export function createSubscriptionGenerator(gql, event) { return async function* (_x, _y, _z, request) { const fields = await parseFields(gql, request); const args = parseArguments(request); for await (const payload of messages.subscribe(event)) { const eventData = payload; if ('event' in args && eventData['action'] !== args['event']) { continue; // skip filtered events } const schema = await getSchema(); const subscription = { collection: eventData['collection'], event: eventData['action'], query: { fields }, }; if (eventData['action'] === 'delete') { // we have no data to send besides the key for (const key of eventData.keys) { yield { [event]: { key, data: null, event: eventData['action'] } }; } } if (eventData['action'] === 'create') { try { subscription.item = eventData['key']; const result = await getPayload(subscription, gql.accountability, schema, eventData); yield { [event]: { key: eventData['key'], data: result['data'], event: eventData['action'], }, }; } catch { // dont notify the subscription of permission errors } } if (eventData['action'] === 'update') { for (const key of eventData['keys']) { try { subscription.item = key; const result = await getPayload(subscription, gql.accountability, schema, eventData); yield { [event]: { key, data: result['data'], event: eventData['action'], }, }; } catch { // dont notify the subscription of permission errors } } } } }; } function createPubSub(emitter) { return { publish: (event, payload) => void emitter.emit(event, payload), subscribe: async function* (event) { const asyncIterator = on(emitter, event); for await (const [value] of asyncIterator) { yield value; } }, }; } async function parseFields(gql, request) { const selections = request.fieldNodes[0]?.selectionSet?.selections ?? []; const dataSelections = selections.reduce((result, selection) => { if (selection.kind === 'Field' && selection.name.value === 'data' && selection.selectionSet?.kind === 'SelectionSet') { return selection.selectionSet.selections; } return result; }, []); const { fields } = await getQuery({}, gql.schema, dataSelections, request.variableValues, gql.accountability); return fields ?? []; } function parseArguments(request) { const args = request.fieldNodes[0]?.arguments ?? []; return args.reduce((result, current) => { if ('value' in current.value && typeof current.value.value === 'string') { result[current.name.value] = current.value.value; } return result; }, {}); }