@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
104 lines (103 loc) • 4.15 kB
JavaScript
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;
}, {});
}