All files / src/services handle-service.ts

100% Statements 32/32
100% Branches 14/14
100% Functions 6/6
100% Lines 31/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120                                                                                                    2x                         12x 12x 9x 9x 1x 1x     11x 11x 4x 4x 4x   7x 7x       13x   8x   1x   1x   1x   1x   1x     10x     19x 3x 1x     19x     10x 10x   10x 10x     10x        
import { CallData } from '../models/infrasctructure/call-data';
import { CallPayload } from '../models/infrasctructure/call-payload';
import { Logger } from '../utils/logger';
import { AnswerCallHandler } from './handle/answer-call-handler';
import { CallHandler } from './handle/call-handler';
import { CloseCallHandler } from './handle/close-call-handler';
import { DialCallHandler, DialCallHandlerConfig } from './handle/dial-call-handler';
import { IceCallHandler } from './handle/ice-call-handler';
import { OfferCallHandler } from './handle/offer-call-handler';
 
/**
 * Configuration interface for the handle service.
 * Extends DialCallHandlerConfig to provide configuration options for call handlers.
 */
export type HandleServiceConfig = {} & DialCallHandlerConfig;
 
/**
 * Service interface for dispatching incoming call payloads to appropriate handlers.
 * Acts as a central routing mechanism for all types of signaling messages.
 */
export interface HandleService {
    /**
     * Initializes the handle service and its associated call handlers.
     *
     * @param config - Configuration with options for various call handlers
     */
    initialize(config: HandleServiceConfig): void;
 
    /**
     * Processes an incoming call payload by routing it to the appropriate handler.
     * Parses, validates, and dispatches the call to the correct handler based on its type.
     *
     * @param payload - The incoming call payload to be processed
     * @returns Promise resolving when the call has been handled
     */
    call(payload: CallPayload): Promise<void>;
}
 
/**
 * Factory function that creates and returns an implementation of the HandleService interface.
 * Centralizes the routing logic for different types of call messages.
 *
 * @param logger - Logger instance for error and debugging information
 * @param dialCallHandler - Handler for 'dial' call requests
 * @param offerCallHandler - Handler for 'offer' call requests
 * @param answerCallHandler - Handler for 'answer' call requests
 * @param iceCallHandler - Handler for 'ice' call requests
 * @param closeCallHandler - Handler for 'close' call requests
 * @returns An implementation of the HandleService interface
 */
export function getHandleService(
    logger: Logger,
    dialCallHandler: DialCallHandler,
    offerCallHandler: OfferCallHandler,
    answerCallHandler: AnswerCallHandler,
    iceCallHandler: IceCallHandler,
    closeCallHandler: CloseCallHandler,
): HandleService {
    async function processCallInternal<TypeData extends CallData>(
        handler: CallHandler<TypeData>,
        payload: CallPayload,
        validate: boolean,
    ): Promise<boolean> {
        const request = handler.parse(payload);
        if (validate) {
            const isValid = handler.validate(request);
            if (!isValid) {
                logger.debug(`[handle-service] Call '${request.a}' is not valid, skipped.`);
                return true;
            }
        }
        logger.debug(`[handle-service] Processing incoming call '${request.a}' from ${request.b.a}...`);
        if (!(await handler.handle(request))) {
            logger.debug(`[handle-service] Postpone processing call '${payload.a}'.`, payload);
            queue.push(payload);
            return false;
        }
        logger.debug(`[handle-service] Successfully processed call '${payload.a}'`, payload);
        return true;
    }
 
    async function processCall(payload: CallPayload, validate: boolean = true): Promise<boolean> {
        switch (payload.a) {
            case 'dial':
                return await processCallInternal(dialCallHandler, payload, validate);
            case 'offer':
                return await processCallInternal(offerCallHandler, payload, validate);
            case 'answer':
                return await processCallInternal(answerCallHandler, payload, validate);
            case 'ice':
                return await processCallInternal(iceCallHandler, payload, validate);
            case 'close':
                return await processCallInternal(closeCallHandler, payload, validate);
        }
        return true;
    }
 
    let queue: Array<CallPayload> = [];
 
    async function processCallQueue() {
        for (let payload = queue.shift(); payload !== undefined; payload = queue.shift()) {
            if (!(await processCall(payload, false))) {
                break;
            }
        }
        setTimeout(processCallQueue, 500);
    }
 
    processCallQueue().then().catch(logger.error);
    return {
        initialize(config: HandleServiceConfig): void {
            dialCallHandler.initialize(config);
            logger.debug('[handle-service] Initialized.');
        },
        async call(payload: CallPayload): Promise<void> {
            await processCall(payload);
        },
    };
}