import { ServiceError, StatusObject } from '@grpc/grpc-js'; import { ValueOf } from 'type-fest'; import { generateId } from '../../../common/misc'; import * as models from '../../../models'; import type { GrpcMethodDefinition } from '../../../network/grpc/method'; import * as protoLoader from '../../../network/grpc/proto-loader'; export interface GrpcMessage { id: string; text: string; created: number; } export const GrpcActionTypeEnum = { reset: 'reset', clear: 'clear', start: 'start', stop: 'stop', responseMessage: 'responseMessage', requestMessage: 'requestStream', error: 'error', invalidate: 'invalidate', invalidateMany: 'invalidateMany', loadMethods: 'loadMethods', status: 'status', } as const; type GrpcActionType = ValueOf; interface Action { type: T; requestId: string; } interface ActionMany { type: T; requestIds: string[]; } interface Payload { payload: T; } type ResetAction = Action; type ClearAction = Action; type StartAction = Action; type StopAction = Action; type InvalidateAction = Action; export type RequestMessageAction = Action & Payload; export type ResponseMessageAction = Action & Payload; export type ErrorAction = Action & Payload; export type StatusAction = Action & Payload; export type LoadMethodsAction = Action & Payload; type InvalidateManyAction = ActionMany; export type GrpcAction = | ClearAction | ResetAction | StartAction | StopAction | ResponseMessageAction | RequestMessageAction | ErrorAction | StatusAction | InvalidateAction | InvalidateManyAction | LoadMethodsAction; export type GrpcActionMany = InvalidateManyAction; export type GrpcDispatch = (action?: GrpcAction) => void; const reset = (requestId: string): ResetAction => ({ type: GrpcActionTypeEnum.reset, requestId, }); const start = (requestId: string): StartAction => ({ type: GrpcActionTypeEnum.start, requestId, }); const stop = (requestId: string): StopAction => ({ type: GrpcActionTypeEnum.stop, requestId, }); const responseMessage = (requestId: string, value: Record): ResponseMessageAction => ({ type: GrpcActionTypeEnum.responseMessage, requestId, payload: { id: generateId(), text: JSON.stringify(value), created: Date.now(), }, }); const requestMessage = (requestId: string, text: string): RequestMessageAction => ({ type: GrpcActionTypeEnum.requestMessage, requestId, payload: { id: generateId(), text, created: Date.now(), }, }); const error = (requestId: string, error: ServiceError): ErrorAction => ({ type: GrpcActionTypeEnum.error, requestId, payload: error, }); const status = (requestId: string, status: StatusObject): StatusAction => ({ type: GrpcActionTypeEnum.status, requestId, payload: status, }); const clear = (requestId: string): ClearAction => ({ type: GrpcActionTypeEnum.clear, requestId, }); const invalidate = (requestId: string): InvalidateAction => ({ type: GrpcActionTypeEnum.invalidate, requestId, }); const invalidateMany = async (protoFileId: string) => { const impacted = await models.grpcRequest.findByProtoFileId(protoFileId); // skip invalidation if no requests are linked to the proto file if (!impacted?.length) { return undefined; } return { type: GrpcActionTypeEnum.invalidateMany, requestIds: impacted.map(g => g._id), } as InvalidateManyAction; }; const loadMethods = async (requestId: string, protoFileId: string) => { console.log(`[gRPC] loading proto file methods pf=${protoFileId}`); const protoFile = await models.protoFile.getById(protoFileId); const methods = await protoLoader.loadMethods(protoFile); return { type: GrpcActionTypeEnum.loadMethods, requestId, payload: methods, } as LoadMethodsAction; }; export const grpcActions = { reset, clear, start, stop, responseMessage, requestMessage, error, status, invalidate, invalidateMany, loadMethods, };