diff --git a/packages/byods/src/Errors/catalog/BYODSDeviceError.ts b/packages/byods/src/Errors/catalog/BYODSDeviceError.ts new file mode 100644 index 00000000000..a46a0d85d84 --- /dev/null +++ b/packages/byods/src/Errors/catalog/BYODSDeviceError.ts @@ -0,0 +1,70 @@ +/* eslint-disable valid-jsdoc */ +//import {RegistrationStatus} from '../../common/types'; + +import {ErrorContext, ErrorMessage, ErrorObject, ERROR_TYPE} from '../types'; +import ExtendedError from './ExtendedError'; + +/** + * Any error reported from Calling client should be stored here. + */ +export class BYODSError extends ExtendedError { + // public status: RegistrationStatus = RegistrationStatus.INACTIVE; + + /** + * Instantiate the Error class with these parameters. + * + * @param {ErrorMessage} msg - Custom error message. + * @param {ErrorContext} context - The context in which the error occurred. + * @param {ERROR_TYPE} type - The type of the error. + * @param {RegistrationStatus} status - Mobius status, should be default. + */ + constructor( + msg: ErrorMessage, + // context: ErrorContext, + type: ERROR_TYPE, + // status: RegistrationStatus + ) { + super(msg,type); + // this.context = context; + // this.status = status; + } + + + + /** + * Class method exposed to callers to allow storing of error object. + * + * @param error - Error Object. + */ + public setError(error: ErrorObject) { + this.message = error.message; + // this.context = error.context; + this.type = error.type; + } + + /** + * Class method exposed to callers to retrieve error object. + * + * @returns Error. + */ + public getError(): ErrorObject { + return { message: this.message, type: this.type }; + } +} + +/** + * Instantiate CallingClientError. + * + * @param msg - Custom error message. + * @param context - Error context. + * @param type - Error Type. + * @param status - Mobius Status, should be default. + * @returns CallingClientError instance. + */ +export const createClientError = ( + msg: ErrorMessage, + context: ErrorContext, + type: ERROR_TYPE, + // status: RegistrationStatus + // ) => new CallingClientError(msg, context, type, status); +) => new BYODSError(msg,type); diff --git a/packages/byods/src/Errors/catalog/ExtendedError.ts b/packages/byods/src/Errors/catalog/ExtendedError.ts new file mode 100644 index 00000000000..0ce1527bb59 --- /dev/null +++ b/packages/byods/src/Errors/catalog/ExtendedError.ts @@ -0,0 +1,22 @@ +/* eslint-disable valid-jsdoc */ +import {ErrorContext, ErrorMessage, ERROR_TYPE} from '../types'; + +/** + * + */ +export default class ExtendedError extends Error { + public type: ERROR_TYPE; + + // public context: ErrorContext; + + /** + * @param msg - TODO. + * @param context - TODO. + * @param type - TODO. + */ + constructor(msg: ErrorMessage,type: ERROR_TYPE) { + super(msg); + this.type = type || ERROR_TYPE.DEFAULT; + // this.context = context; + } +} diff --git a/packages/byods/src/Errors/index.ts b/packages/byods/src/Errors/index.ts new file mode 100644 index 00000000000..563d98c1628 --- /dev/null +++ b/packages/byods/src/Errors/index.ts @@ -0,0 +1 @@ +export {BYODSError as BYODSError} from './catalog/BYODSDeviceError'; diff --git a/packages/byods/src/Errors/types.ts b/packages/byods/src/Errors/types.ts new file mode 100644 index 00000000000..ab33e4d7ba0 --- /dev/null +++ b/packages/byods/src/Errors/types.ts @@ -0,0 +1,60 @@ +import {IMetaContext} from '../Logger/types'; + +export type ErrorMessage = string; + +export enum ERROR_LAYER { + CALL_CONTROL = 'call_control', + MEDIA = 'media', +} + +export enum ERROR_TYPE { + CALL_ERROR = 'call_error', + DEFAULT = 'default_error', + FORBIDDEN_ERROR = 'forbidden', + NOT_FOUND = 'not_found', + REGISTRATION_ERROR = 'registration_error', + SERVICE_UNAVAILABLE = 'service_unavailable', + TIMEOUT = 'timeout', + TOKEN_ERROR = 'token_error', + SERVER_ERROR = 'server_error', +} + +export enum ERROR_CODE { + UNAUTHORIZED = 401, + FORBIDDEN = 403, + DEVICE_NOT_FOUND = 404, + INTERNAL_SERVER_ERROR = 500, + NOT_IMPLEMENTED = 501, + SERVICE_UNAVAILABLE = 503, + BAD_REQUEST = 400, + REQUEST_TIMEOUT = 408, + TOO_MANY_REQUESTS = 429, +} + +export enum CALL_ERROR_CODE { + INVALID_STATUS_UPDATE = 111, + DEVICE_NOT_REGISTERED = 112, + CALL_NOT_FOUND = 113, + ERROR_PROCESSING = 114, + USER_BUSY = 115, + PARSING_ERROR = 116, + TIMEOUT_ERROR = 117, + NOT_ACCEPTABLE = 118, + CALL_REJECTED = 119, + NOT_AVAILABLE = 120, +} + +export enum DEVICE_ERROR_CODE { + DEVICE_LIMIT_EXCEEDED = 101, + DEVICE_CREATION_DISABLED = 102, + DEVICE_CREATION_FAILED = 103, +} + +export interface ErrorContext extends IMetaContext {} + +export type ErrorObject = { + message: ErrorMessage; + type: ERROR_TYPE; + context: ErrorContext; +}; + diff --git a/packages/byods/src/Logger/index.ts b/packages/byods/src/Logger/index.ts new file mode 100644 index 00000000000..e2fde90bd25 --- /dev/null +++ b/packages/byods/src/Logger/index.ts @@ -0,0 +1,211 @@ +/* eslint-disable valid-jsdoc */ +import {BYODS_PACKAGE_NAME} from '../constants'; +import {IMetaContext} from './types'; +import ExtendedError from '../Errors/catalog/ExtendedError'; +import {LOGGING_LEVEL, LogContext, LOGGER, LOG_PREFIX} from './types'; + +/* + * These are the order of log levels :- + * error - 1 + * warn - 2 + * log - 3 + * info - 4 + * trace - 5 + * + * Where log level n denotes that level 1 -> level n will be logged. + */ + +let currentLogLevel = LOGGING_LEVEL.error; + +/** + * A wrapper around console which prints to stderr or stdout + * based on the level defined. + * + * @param message - Log Message to print. + * @param level - Log level. + */ +const writeToConsole = (message: string, level: LOGGER) => { + switch (level) { + case LOGGER.INFO: + case LOGGER.LOG: { + // eslint-disable-next-line no-console + console.log(message); + break; + } + case LOGGER.WARN: { + console.warn(message); + break; + } + case LOGGER.ERROR: { + console.error(message); + break; + } + case LOGGER.TRACE: { + // eslint-disable-next-line no-console + console.trace(message); + break; + } + default: { + // Since this is internal , we shouldn't reach here + } + } +}; + +/** + * Format the Log message as 'timestamp Calling SDK - [level]: file:example.ts - method:methodName - Actual log message'. + * + * @param context - File and method. + * @param level - Log level. + * @returns - Formatted string. + */ +const format = (context: IMetaContext, level: string): string => { + const timestamp = new Date().toUTCString(); + + return `${BYODS_PACKAGE_NAME}: ${timestamp}: ${level}: ${LOG_PREFIX.FILE}:${context.file} - ${LOG_PREFIX.METHOD}:${context.method}`; +}; + +/** + * Used by the Calling Client to initialize the logger module + * with a certain level. + * + * @param level - Log Level. + */ +const setLogger = (level: string, module: string) => { + switch (level) { + case LOGGER.WARN: { + currentLogLevel = LOGGING_LEVEL.warn; + break; + } + case LOGGER.LOG: { + currentLogLevel = LOGGING_LEVEL.log; + break; + } + case LOGGER.INFO: { + currentLogLevel = LOGGING_LEVEL.info; + break; + } + case LOGGER.TRACE: { + currentLogLevel = LOGGING_LEVEL.trace; + break; + } + default: { + currentLogLevel = LOGGING_LEVEL.error; + } + } + + const message = `Logger initialized for module: ${module} with level: ${currentLogLevel}`; + + writeToConsole( + `${format({file: 'logger.ts', method: 'setLogger'}, '')} - ${LOG_PREFIX.MESSAGE}:${message}`, + LOGGER.INFO + ); +}; + +/** + * To retrieve the current log level. + * + * @returns - Log level. + */ +const getLogLevel = (): LOGGER => { + let level; + + switch (currentLogLevel) { + case LOGGING_LEVEL.warn: { + level = LOGGER.WARN; + break; + } + case LOGGING_LEVEL.log: { + level = LOGGER.LOG; + break; + } + case LOGGING_LEVEL.info: { + level = LOGGER.INFO; + break; + } + case LOGGING_LEVEL.trace: { + level = LOGGER.TRACE; + break; + } + default: { + level = LOGGER.ERROR; + } + } + + return level; +}; + +/** + * Can be used to print only useful information. + * + * @param message - Caller emitted string. + * @param context - File and method which called. + */ +const logMessage = (message: string, context: LogContext) => { + if (currentLogLevel >= LOGGING_LEVEL.log) { + writeToConsole(`${format(context, '[LOG]')} - ${LOG_PREFIX.MESSAGE}:${message}`, LOGGER.LOG); + } +}; + +/** + * Can be used to print informational messages. + * + * @param message - Caller emitted string. + * @param context - File and method which called. + */ +const logInfo = (message: string, context: LogContext) => { + if (currentLogLevel >= LOGGING_LEVEL.info) { + writeToConsole(`${format(context, '[INFO]')} - ${LOG_PREFIX.MESSAGE}:${message}`, LOGGER.INFO); + } +}; + +/** + * Can be used to print warning messages. + * + * @param message - Caller emitted string. + * @param context - File and method which called. + */ +const logWarn = (message: string, context: LogContext) => { + if (currentLogLevel >= LOGGING_LEVEL.warn) { + writeToConsole(`${format(context, '[WARN]')} - ${LOG_PREFIX.MESSAGE}:${message}`, LOGGER.WARN); + } +}; + +/** + * Can be used to print the stack trace of the entire call path. + * + * @param message - Caller emitted string. + * @param context - File and method which called. + */ +const logTrace = (message: string, context: LogContext) => { + if (currentLogLevel >= LOGGING_LEVEL.trace) { + writeToConsole( + `${format(context, '[TRACE]')} - ${LOG_PREFIX.MESSAGE}:${message}`, + LOGGER.TRACE + ); + } +}; + +/** + * Can be used to print only errors. + * + * @param error - Error string . + * @param context - File and method which called. + */ +const logError = (error: ExtendedError, context: LogContext) => { + if (currentLogLevel >= LOGGING_LEVEL.error) { + writeToConsole( + `${format(context, '[ERROR]')} - !${LOG_PREFIX.ERROR}!${LOG_PREFIX.MESSAGE}:${error.message}`, + LOGGER.ERROR + ); + } +}; + +export default { + log: logMessage, + error: logError, + info: logInfo, + warn: logWarn, + trace: logTrace, + setLogger, + getLogLevel, +}; diff --git a/packages/byods/src/Logger/types.ts b/packages/byods/src/Logger/types.ts new file mode 100644 index 00000000000..5c0c5c9f8e4 --- /dev/null +++ b/packages/byods/src/Logger/types.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-empty-interface */ +// import {IMetaContext} from '../common/types'; +export interface IMetaContext { + file?: string; + method?: string; +} + +export interface LogContext extends IMetaContext {} + +export enum LOG_PREFIX { + MAIN = 'CALLING_SDK', + FILE = 'file', + METHOD = 'method', + EVENT = 'event', + MESSAGE = 'message', + ERROR = 'error', +} + +export enum LOGGING_LEVEL { + error = 1, + warn = 2, + log = 3, + info = 4, + trace = 5, +} + +export enum LOGGER { + ERROR = 'error', + WARN = 'warn', + INFO = 'info', + LOG = 'log', + TRACE = 'trace', +} diff --git a/packages/byods/src/base-client/constant.ts b/packages/byods/src/base-client/constant.ts new file mode 100644 index 00000000000..26f1cf7d04b --- /dev/null +++ b/packages/byods/src/base-client/constant.ts @@ -0,0 +1 @@ +export const BYODS_BASE_CLIENT_FILE = 'base-client'; \ No newline at end of file diff --git a/packages/byods/src/base-client/index.ts b/packages/byods/src/base-client/index.ts index 12b10dca82f..bf599e49a47 100644 --- a/packages/byods/src/base-client/index.ts +++ b/packages/byods/src/base-client/index.ts @@ -3,13 +3,17 @@ import fetch, {Response, RequestInit} from 'node-fetch'; import TokenManager from '../token-manager'; import DataSourceClient from '../data-source-client'; import {HttpClient, ApiResponse} from '../http-client/types'; +import {BYODSConfig} from '../token-manager/type'; +import {BYODS_BASE_CLIENT_FILE} from './constant'; +import log from '../Logger'; +import {LOGGER} from '../Logger/types'; export default class BaseClient { private baseUrl: string; private headers: Record; private tokenManager: TokenManager; private orgId: string; - + private sdkConfig?: BYODSConfig; public dataSource: DataSourceClient; /** @@ -25,13 +29,17 @@ export default class BaseClient { baseUrl: string, headers: Record, tokenManager: TokenManager, - orgId: string + orgId: string, + config?:BYODSConfig ) { this.baseUrl = baseUrl; this.headers = headers; this.tokenManager = tokenManager; this.orgId = orgId; this.dataSource = new DataSourceClient(this.getHttpClientForOrg()); + this.sdkConfig = config; + const logLevel = this.sdkConfig?.logger?.level ? this.sdkConfig.logger.level : LOGGER.ERROR; + log.setLogger(logLevel, BYODS_BASE_CLIENT_FILE); } /** diff --git a/packages/byods/src/base-client/type.ts b/packages/byods/src/base-client/type.ts new file mode 100644 index 00000000000..60146fd2f69 --- /dev/null +++ b/packages/byods/src/base-client/type.ts @@ -0,0 +1,107 @@ +import {LOGGER} from '../Logger/types'; +import {BYODSError} from '../Errors'; + +export interface LoggerConfig { + level: LOGGER; +} + +export interface BYODSConfig { + logger?: LoggerConfig; +} + +export type BYODSErrorEmitterCallback = ( + err: BYODSError, + finalError?: boolean +) => void; + +/** + * An interface for the `CallingClient` module. + * The `CallingClient` module is designed to provide a set of APIs related to line registration and calling functionalities within the SDK. + * + * @example + * ```javascript + * const callingClient = createClient(webex, callingConfig); + * ``` + */ + + /** + * Represents the `mediaEngine for managing media-related operations within the CallingClient. + * The media engine provides access to audio and video devices such as cameras, microphones, and speakers within the media layer. + * + * @public + * @example + * ``` + * const microphones = await callingClient.mediaEngine.Media.getMicrophones(); + * const speakers = await callingClient.mediaEngine.Media.getSpeakers(); + * const cameras = await callingClient.mediaEngine.Media.getCameras(); + * ``` + */ + + /** + * @ignore + */ + getLoggingLevel(); LOGGER; + + /** + * Retrieves details of the line object(s) belonging to a user. + * + * This method gathers all the {@link ILine} objects and organizes them into a dictionary + * where keys represent `lineId`s and values are arrays of {@link ILine} objects registered with + * the `callingClient` + * + * @example + * ```typescript + * const lines = callingClient.getLines(); + * ``` + * The `lines` response object will have `lineId` as its key and + * a list {@link ILine} objects as it's value. + * ``` + * { + * 'lineId1': lineObj1, + * 'lineId2': lineObj2, + * } + * ``` + */ + // getLines(): Record; + + /** + * Retrieves a dictionary of active calls grouped by `lineId`. + * + * This method gathers active {@link ICall} objects and organizes them into a dictionary + * where keys represent `lineId`s and values are arrays of {@link ICall} objects of active calls associated + * with each line. + * + * @example + * ```typescript + * const activeCalls = callingClient.getActiveCalls(); + * ``` + * The `activeCalls` response object will have `lineId` as its key and + * a list {@link ICall} objects as it's value. + * + * ``` + * { + * 'line1': [call1, call2], + * 'line2': [call3], + * } + * ``` + */ + // getActiveCalls(): Record; + + /** + * Retrieves the {@link ICall} object for the currently connected call in the client. + * + * This method iterates through active call objects and returns the call + * that is currently connected (not on hold). + * + * @example + * ```typescript + * const connectedCall : ICall = callingClient.getConnectedCall(); + * ``` + * The `connectedCall` object will be the Call object of the connected call with the client + */ + // getConnectedCall(): ICall | undefined; + +function getLoggingLevel() { + throw new Error('Function not implemented.'); +} + diff --git a/packages/byods/src/byods/constant.ts b/packages/byods/src/byods/constant.ts new file mode 100644 index 00000000000..925eb9601a9 --- /dev/null +++ b/packages/byods/src/byods/constant.ts @@ -0,0 +1 @@ +export const BYODS_FILE = 'byods'; diff --git a/packages/byods/src/byods/index.ts b/packages/byods/src/byods/index.ts index 7f2996ad557..d91ab7e32c3 100644 --- a/packages/byods/src/byods/index.ts +++ b/packages/byods/src/byods/index.ts @@ -10,6 +10,10 @@ import { } from '../constants'; import {SDKConfig} from '../types'; import TokenManager from '../token-manager'; +import {BYODSConfig} from '../token-manager/type'; +import {BYODS_FILE} from './constant'; +import log from '../Logger'; +import {LOGGER} from '../Logger/types'; /** * The BYoDS SDK. @@ -24,6 +28,7 @@ export default class BYODS { private env: 'production' | 'integration'; private config: SDKConfig; private baseUrl: string; + private sdkConfig?: BYODSConfig; /** * The token manager for the SDK. @@ -37,9 +42,12 @@ export default class BYODS { * @example * const sdk = new BYODS({ clientId: 'your-client-id', clientSecret: 'your-client-secret' }); */ - constructor({clientId, clientSecret}: SDKConfig) { + constructor({clientId, clientSecret}: SDKConfig,config?:BYODSConfig) { this.config = {clientId, clientSecret}; this.tokenManager = new TokenManager(clientId, clientSecret); + this.sdkConfig = config; + const logLevel = this.sdkConfig?.logger?.level ? this.sdkConfig.logger.level : LOGGER.ERROR; + log.setLogger(logLevel, BYODS_FILE); /** * The environment variable `process.env.BYODS_ENVIRONMENT` determines the environment in which the SDK operates. diff --git a/packages/byods/src/byods/type.ts b/packages/byods/src/byods/type.ts new file mode 100644 index 00000000000..60146fd2f69 --- /dev/null +++ b/packages/byods/src/byods/type.ts @@ -0,0 +1,107 @@ +import {LOGGER} from '../Logger/types'; +import {BYODSError} from '../Errors'; + +export interface LoggerConfig { + level: LOGGER; +} + +export interface BYODSConfig { + logger?: LoggerConfig; +} + +export type BYODSErrorEmitterCallback = ( + err: BYODSError, + finalError?: boolean +) => void; + +/** + * An interface for the `CallingClient` module. + * The `CallingClient` module is designed to provide a set of APIs related to line registration and calling functionalities within the SDK. + * + * @example + * ```javascript + * const callingClient = createClient(webex, callingConfig); + * ``` + */ + + /** + * Represents the `mediaEngine for managing media-related operations within the CallingClient. + * The media engine provides access to audio and video devices such as cameras, microphones, and speakers within the media layer. + * + * @public + * @example + * ``` + * const microphones = await callingClient.mediaEngine.Media.getMicrophones(); + * const speakers = await callingClient.mediaEngine.Media.getSpeakers(); + * const cameras = await callingClient.mediaEngine.Media.getCameras(); + * ``` + */ + + /** + * @ignore + */ + getLoggingLevel(); LOGGER; + + /** + * Retrieves details of the line object(s) belonging to a user. + * + * This method gathers all the {@link ILine} objects and organizes them into a dictionary + * where keys represent `lineId`s and values are arrays of {@link ILine} objects registered with + * the `callingClient` + * + * @example + * ```typescript + * const lines = callingClient.getLines(); + * ``` + * The `lines` response object will have `lineId` as its key and + * a list {@link ILine} objects as it's value. + * ``` + * { + * 'lineId1': lineObj1, + * 'lineId2': lineObj2, + * } + * ``` + */ + // getLines(): Record; + + /** + * Retrieves a dictionary of active calls grouped by `lineId`. + * + * This method gathers active {@link ICall} objects and organizes them into a dictionary + * where keys represent `lineId`s and values are arrays of {@link ICall} objects of active calls associated + * with each line. + * + * @example + * ```typescript + * const activeCalls = callingClient.getActiveCalls(); + * ``` + * The `activeCalls` response object will have `lineId` as its key and + * a list {@link ICall} objects as it's value. + * + * ``` + * { + * 'line1': [call1, call2], + * 'line2': [call3], + * } + * ``` + */ + // getActiveCalls(): Record; + + /** + * Retrieves the {@link ICall} object for the currently connected call in the client. + * + * This method iterates through active call objects and returns the call + * that is currently connected (not on hold). + * + * @example + * ```typescript + * const connectedCall : ICall = callingClient.getConnectedCall(); + * ``` + * The `connectedCall` object will be the Call object of the connected call with the client + */ + // getConnectedCall(): ICall | undefined; + +function getLoggingLevel() { + throw new Error('Function not implemented.'); +} + diff --git a/packages/byods/src/data-source-client/constants.ts b/packages/byods/src/data-source-client/constants.ts index 2a8462b19c4..efef9e0821d 100644 --- a/packages/byods/src/data-source-client/constants.ts +++ b/packages/byods/src/data-source-client/constants.ts @@ -1 +1,2 @@ export const DATASOURCE_ENDPOINT = '/dataSources'; +export const BYODS_DATA_SOURCE_CLIENT_FILE = 'data-source-client'; diff --git a/packages/byods/src/data-source-client/index.ts b/packages/byods/src/data-source-client/index.ts index c1b5d7abd62..f097d9a0c7a 100644 --- a/packages/byods/src/data-source-client/index.ts +++ b/packages/byods/src/data-source-client/index.ts @@ -1,12 +1,17 @@ import {DataSourceRequest, DataSourceResponse} from './types'; import {DATASOURCE_ENDPOINT} from './constants'; import {HttpClient, ApiResponse} from '../http-client/types'; +import {BYODSConfig} from '../token-manager/type'; +import {BYODS_DATA_SOURCE_CLIENT_FILE} from '../data-source-client/constants'; +import log from '../Logger'; +import {LOGGER} from '../Logger/types'; /** * Client for interacting with the /dataSource API. */ export default class DataSourceClient { private httpClient: HttpClient; + private sdkConfig?: BYODSConfig; /** * Creates an instance of DataSourceClient. @@ -15,8 +20,11 @@ export default class DataSourceClient { * const httpClient = new HttpClient(); * const client = new DataSourceClient(httpClient); */ - constructor(httpClient: HttpClient) { + constructor(httpClient: HttpClient,config?:BYODSConfig) { this.httpClient = httpClient; + this.sdkConfig = config; + const logLevel = this.sdkConfig?.logger?.level ? this.sdkConfig.logger.level : LOGGER.ERROR; + log.setLogger(logLevel, BYODS_DATA_SOURCE_CLIENT_FILE); } /** diff --git a/packages/byods/src/data-source-client/types.ts b/packages/byods/src/data-source-client/types.ts index 923b0af6c2b..9005d9e83d8 100644 --- a/packages/byods/src/data-source-client/types.ts +++ b/packages/byods/src/data-source-client/types.ts @@ -1,3 +1,28 @@ +import {LOGGER} from '../Logger/types'; +import {BYODSError} from '../Errors'; + +export interface LoggerConfig { + level: LOGGER; +} + +export interface BYODSConfig { + logger?: LoggerConfig; +} + +export type BYODSErrorEmitterCallback = ( + err: BYODSError, + finalError?: boolean +) => void; + + /** + * @ignore + */ + getLoggingLevel(); LOGGER; + + function getLoggingLevel() { + throw new Error('Function not implemented.'); + } + /** * Represents the response from a data source. * @@ -58,6 +83,7 @@ export interface DataSourceResponse { * The error message associated with the data source response, if any. */ errorMessage?: string; + } /** @@ -96,3 +122,4 @@ export interface DataSourceRequest { */ tokenLifetimeMinutes: number; } + diff --git a/packages/byods/src/token-manager/constant.ts b/packages/byods/src/token-manager/constant.ts new file mode 100644 index 00000000000..1a80b45c609 --- /dev/null +++ b/packages/byods/src/token-manager/constant.ts @@ -0,0 +1 @@ +export const BYODS_TOKEN_MANAGER_FILE = 'token-manager'; \ No newline at end of file diff --git a/packages/byods/src/token-manager/index.ts b/packages/byods/src/token-manager/index.ts index c88de4ecc7c..558355489c6 100644 --- a/packages/byods/src/token-manager/index.ts +++ b/packages/byods/src/token-manager/index.ts @@ -1,7 +1,12 @@ import fetch, {Response} from 'node-fetch'; - +import log from '../Logger'; +import {LOGGER} from '../Logger/types'; import {APPLICATION_ID_PREFIX, PRODUCTION_BASE_URL} from '../constants'; import {TokenResponse, OrgServiceAppAuthorization, ServiceAppAuthorizationMap} from '../types'; +import ExtendedError from 'Errors/catalog/ExtendedError'; +import { ERROR_TYPE } from 'Errors/types'; +import {BYODSConfig} from '../token-manager/type'; +import { BYODS_TOKEN_MANAGER_FILE } from './constant'; /** * The token manager for the BYoDS SDK. @@ -12,6 +17,7 @@ export default class TokenManager { private clientSecret: string; private serviceAppId: string; private baseUrl: string; + private sdkConfig?: BYODSConfig; /** * Creates an instance of TokenManager. @@ -22,14 +28,17 @@ export default class TokenManager { * @example * const tokenManager = new TokenManager('your-client-id', 'your-client-secret'); */ - constructor(clientId: string, clientSecret: string, baseUrl: string = PRODUCTION_BASE_URL) { + constructor(clientId: string, clientSecret: string, baseUrl: string = PRODUCTION_BASE_URL,config?:BYODSConfig) { if (!clientId || !clientSecret) { throw new Error('clientId and clientSecret are required'); } this.clientId = clientId; this.clientSecret = clientSecret; this.baseUrl = baseUrl; + this.sdkConfig = config; this.serviceAppId = Buffer.from(`${APPLICATION_ID_PREFIX}${clientId}`).toString('base64'); + const logLevel = this.sdkConfig?.logger?.level ? this.sdkConfig.logger.level : LOGGER.ERROR; + log.setLogger(logLevel, BYODS_TOKEN_MANAGER_FILE); } /** @@ -114,7 +123,12 @@ export default class TokenManager { const data: TokenResponse = (await response.json()) as TokenResponse; this.updateServiceAppToken(data, orgId); } catch (error) { - console.error('Error retrieving token after authorization:', error); + log.error( + new ExtendedError( + 'Error retrieving token after authorization', ERROR_TYPE.REGISTRATION_ERROR + ),{ file: 'BYODS_TOKEN_MANAGER_FILE', method: 'getServiceAppTokenUsingPAT' } + ); + throw error; } } @@ -179,7 +193,12 @@ export default class TokenManager { const data: TokenResponse = (await response.json()) as TokenResponse; this.updateServiceAppToken(data, orgId); } catch (error) { - console.error('Error saving service app registration:', error); + log.error( + new ExtendedError( + 'Error saving service app registration', ERROR_TYPE.REGISTRATION_ERROR + ),{ file: 'BYODS_TOKEN_MANAGER_FILE', method: 'saveServiceAppRegistration' } + ); + throw error; } } diff --git a/packages/byods/src/token-manager/type.ts b/packages/byods/src/token-manager/type.ts new file mode 100644 index 00000000000..60146fd2f69 --- /dev/null +++ b/packages/byods/src/token-manager/type.ts @@ -0,0 +1,107 @@ +import {LOGGER} from '../Logger/types'; +import {BYODSError} from '../Errors'; + +export interface LoggerConfig { + level: LOGGER; +} + +export interface BYODSConfig { + logger?: LoggerConfig; +} + +export type BYODSErrorEmitterCallback = ( + err: BYODSError, + finalError?: boolean +) => void; + +/** + * An interface for the `CallingClient` module. + * The `CallingClient` module is designed to provide a set of APIs related to line registration and calling functionalities within the SDK. + * + * @example + * ```javascript + * const callingClient = createClient(webex, callingConfig); + * ``` + */ + + /** + * Represents the `mediaEngine for managing media-related operations within the CallingClient. + * The media engine provides access to audio and video devices such as cameras, microphones, and speakers within the media layer. + * + * @public + * @example + * ``` + * const microphones = await callingClient.mediaEngine.Media.getMicrophones(); + * const speakers = await callingClient.mediaEngine.Media.getSpeakers(); + * const cameras = await callingClient.mediaEngine.Media.getCameras(); + * ``` + */ + + /** + * @ignore + */ + getLoggingLevel(); LOGGER; + + /** + * Retrieves details of the line object(s) belonging to a user. + * + * This method gathers all the {@link ILine} objects and organizes them into a dictionary + * where keys represent `lineId`s and values are arrays of {@link ILine} objects registered with + * the `callingClient` + * + * @example + * ```typescript + * const lines = callingClient.getLines(); + * ``` + * The `lines` response object will have `lineId` as its key and + * a list {@link ILine} objects as it's value. + * ``` + * { + * 'lineId1': lineObj1, + * 'lineId2': lineObj2, + * } + * ``` + */ + // getLines(): Record; + + /** + * Retrieves a dictionary of active calls grouped by `lineId`. + * + * This method gathers active {@link ICall} objects and organizes them into a dictionary + * where keys represent `lineId`s and values are arrays of {@link ICall} objects of active calls associated + * with each line. + * + * @example + * ```typescript + * const activeCalls = callingClient.getActiveCalls(); + * ``` + * The `activeCalls` response object will have `lineId` as its key and + * a list {@link ICall} objects as it's value. + * + * ``` + * { + * 'line1': [call1, call2], + * 'line2': [call3], + * } + * ``` + */ + // getActiveCalls(): Record; + + /** + * Retrieves the {@link ICall} object for the currently connected call in the client. + * + * This method iterates through active call objects and returns the call + * that is currently connected (not on hold). + * + * @example + * ```typescript + * const connectedCall : ICall = callingClient.getConnectedCall(); + * ``` + * The `connectedCall` object will be the Call object of the connected call with the client + */ + // getConnectedCall(): ICall | undefined; + +function getLoggingLevel() { + throw new Error('Function not implemented.'); +} + diff --git a/packages/byods/test/unit/spec/Logger/index.text.ts b/packages/byods/test/unit/spec/Logger/index.text.ts new file mode 100644 index 00000000000..b4da3862ded --- /dev/null +++ b/packages/byods/test/unit/spec/Logger/index.text.ts @@ -0,0 +1,77 @@ +import ExtendedError from '../../../../src/Errors/catalog/ExtendedError'; +import {LOGGER} from '../../../../src/Logger/types'; +import log from '../../../../src/Logger'; + +describe('Coverage tests for logger', () => { + let logLevel: LOGGER; + + const logSpy = jest.spyOn(console, 'log'); + const traceSpy = jest.spyOn(console, 'trace'); + const warnSpy = jest.spyOn(console, 'warn'); + const errorSpy = jest.spyOn(console, 'error'); + + beforeEach(() => { + logLevel = LOGGER.ERROR; + }); + + const fakePrint = 'Example log statement'; + const dummyContext = { + file: 'logger.test.ts', + method: 'dummy', + }; + + it('Set the log level to error and verify that all levels are not executed except error', () => { + log.info(fakePrint, dummyContext); + expect(logSpy).not.toHaveBeenCalledTimes(1); + + log.log(fakePrint, dummyContext); + expect(logSpy).not.toHaveBeenCalledTimes(1); + + log.warn(fakePrint, dummyContext); + expect(warnSpy).not.toHaveBeenCalledTimes(1); + + log.trace(fakePrint, dummyContext); + expect(traceSpy).not.toHaveBeenCalledTimes(1); + + log.error(new Error(fakePrint) as ExtendedError, dummyContext); + expect(errorSpy).toHaveBeenCalledTimes(1); + }); + + it('Set the logger and verify the level', () => { + expect(logLevel).toStrictEqual(LOGGER.ERROR); + log.setLogger(LOGGER.TRACE); + expect(log.getLogLevel()).toStrictEqual(LOGGER.TRACE); + }); + + it('Set the log level to Info and verify levels below info are executed or not', () => { + log.setLogger(LOGGER.INFO); + + log.info(fakePrint, dummyContext); + expect(logSpy).toHaveBeenCalledTimes(2); + + log.log(fakePrint, dummyContext); + expect(logSpy).toHaveBeenCalledTimes(3); + + log.warn(fakePrint, dummyContext); + expect(warnSpy).toHaveBeenCalledTimes(1); + + log.trace(fakePrint, dummyContext); + expect(traceSpy).not.toHaveBeenCalledTimes(1); + }); + + it('Set the log level to Trace and verify that all levels are executed', () => { + log.setLogger(LOGGER.TRACE); + + log.info(fakePrint, dummyContext); + expect(logSpy).toHaveBeenCalledTimes(2); // one during initialization and one with the statement + + log.log(fakePrint, dummyContext); + expect(logSpy).toHaveBeenCalledTimes(3); // +1 because both info and log internally use console.log + + log.warn(fakePrint, dummyContext); + expect(warnSpy).toHaveBeenCalledTimes(1); + + log.trace(fakePrint, dummyContext); + expect(traceSpy).toHaveBeenCalledTimes(1); + }); +});