UNPKG

integreat

Version:
147 lines 6.17 kB
import pProgress from 'p-progress'; import debugLib from 'debug'; import { QUEUE_SYMBOL } from './handlers/index.js'; import setupGetService from './utils/getService.js'; import { removeQueueFlag, setErrorOnAction, setResponseOnAction, setOptionsOnAction, setActionIds, } from './utils/action.js'; import { createErrorResponse, setOrigin } from './utils/response.js'; import { completeIdentOnAction } from './utils/completeIdent.js'; import { composeMiddleware } from './utils/composeMiddleware.js'; const debug = debugLib('great'); const shouldCompleteIdent = (action, options) => options.identConfig?.completeIdent && action.type !== 'GET_IDENT'; function getActionHandlerFromType(type, handlers) { const handler = type ? handlers[type] : undefined; if (typeof handler === 'function') { return handler; } else { return undefined; } } function cleanUpActionAndSetIds({ payload: { service, ...payload }, meta: { auth, ...meta } = {}, ...action }) { return setActionIds({ ...action, payload: { ...(service && { targetService: service }), ...payload }, meta: { ...meta, dispatchedAt: Date.now() }, }); } const cleanUpResponseAndSetAccessAndOrigin = (response, ident) => ({ ...response, access: { ident, ...response.access }, ...(response.status !== 'ok' ? { origin: response.origin || 'dispatch' } : {}), }); const adjustActionAfterIncomingMutation = ({ payload: { sourceService, ...payload } = {}, ...action }, originalAction) => ({ ...action, payload, meta: { ...action.meta, queue: action.meta?.queue ?? originalAction.meta?.queue, }, }); async function mutateIncomingAction(action, getService) { const { sourceService } = action.payload; if (typeof sourceService !== 'string') { return { action }; } const service = getService(undefined, sourceService); if (!service) { return { action: setErrorOnAction(action, `Source service '${sourceService}' not found`, 'dispatch', 'badrequest'), }; } const endpoint = await service.endpointFromAction(action, true); if (!endpoint) { return { action: setErrorOnAction(action, `No matching endpoint for incoming mapping on service '${sourceService}'`, 'dispatch', 'badrequest'), }; } let mutatedAction; const validateResponse = await endpoint.validateAction(action); if (validateResponse) { mutatedAction = setResponseOnAction(action, validateResponse); } else { mutatedAction = adjustActionAfterIncomingMutation(await service.mutateIncomingRequest(setOptionsOnAction(action, endpoint), endpoint), action); } return { action: mutatedAction, service, endpoint }; } async function mutateIncomingResponse(action, service, endpoint) { return service && endpoint ? await service.mutateIncomingResponse(setOptionsOnAction(action, endpoint), endpoint) : action.response || {}; } async function handleAction(handlerType, action, resources, handlers) { const handler = getActionHandlerFromType(handlerType, handlers); if (!handler) { return createErrorResponse(`No handler for ${String(handlerType)} action`, 'dispatch', 'badrequest'); } return setOrigin(await handler(action, resources), typeof handlerType === 'string' ? `handler:${handlerType}` : 'handler:queue'); } function prepareActionForQueue(action, options) { if (action.meta?.queue && !options.disableQueuing) { if (options.queueService) { return [action, true]; } else { return [removeQueueFlag(action), false]; } } else { return [action, false]; } } function addActionId(actionIds, action) { const actionId = action.meta?.id; actionIds.add(actionId); return actionId; } function removeActionId(actionIds, actionId, emit) { actionIds.delete(actionId); if (actionIds.size === 0) { emit('done'); } } export default function createDispatch({ handlers = {}, schemas = new Map(), services = {}, middleware = [], options, actionIds, emit, }) { const getService = setupGetService(schemas, services); const middlewareFn = middleware.length > 0 ? composeMiddleware(...middleware) : (next) => async (action) => next(action); const dispatch = (originalAction) => pProgress(async (setProgress) => { debug('Dispatch: %o', originalAction); if (!originalAction) { return { status: 'noaction', error: 'Dispatched no action', origin: 'dispatch', }; } let cleanedUpAction = cleanUpActionAndSetIds(originalAction); const actionId = addActionId(actionIds, cleanedUpAction); if (shouldCompleteIdent(cleanedUpAction, options)) { cleanedUpAction = await completeIdentOnAction(cleanedUpAction, dispatch); } const { action, service: incomingService, endpoint: incomingEndpoint, } = await mutateIncomingAction(cleanedUpAction, getService); let response; if (action.response?.status) { response = action.response; } else { try { const [nextAction, doQueue] = prepareActionForQueue(action, options); const next = async (action) => handleAction(doQueue ? QUEUE_SYMBOL : action.type, action, { dispatch, getService, options, setProgress }, handlers); response = setOrigin(await middlewareFn(next)(nextAction), 'middleware:dispatch'); } catch (err) { response = createErrorResponse(`Error thrown in dispatch: ${err instanceof Error ? err.message : String(err)}`, 'dispatch'); } } const cleanedUpResponse = cleanUpResponseAndSetAccessAndOrigin(await mutateIncomingResponse(setResponseOnAction(action, response), incomingService, incomingEndpoint), action.meta?.ident); removeActionId(actionIds, actionId, emit); return cleanedUpResponse; }); return dispatch; } //# sourceMappingURL=dispatch.js.map