diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ef4bb17..a829608 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,7 +38,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -52,7 +52,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -65,4 +65,4 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/src/core.ts b/src/core.ts index 100691a..fe1ba41 100644 --- a/src/core.ts +++ b/src/core.ts @@ -12,6 +12,7 @@ import { debug as Debug } from 'debug'; import { createServer } from 'http'; import { promisify } from 'util'; import { logger as log } from './logger'; +import { MESSAGES } from './messages'; let _config: any = {}; @@ -26,25 +27,25 @@ let jsonParser = promisify(bodyParser.json({ limit: '1mb' })); */ const requestHandler = (request, response) => { - log.info(`Request recived, '${request.method} : ${request.url}'`); + log.info(MESSAGES.REQUEST_RECEIVED); debug('_config', _config); // Explicitly remove or override the X-Powered-By header response.setHeader('X-Powered-By', ''); return Promise.resolve().then(() => { // Should be a POST call. if (request.method && request.method !== 'POST') { - debug('Only POST call is supported.'); + debug(MESSAGES.ONLY_POST_SUPPORTED); return Promise.reject({ - body: `Only POST call is supported.`, + body: `Only POST requests are supported.`, statusCode: 400, statusMessage: 'Not allowed', }); } }).then(() => { // validate endpoint - debug(`${request.url} invoked`); + debug(MESSAGES.REQUEST_INVOKED(request.url)); if (_config && _config.listener && request.url !== _config.listener.endpoint) { - debug('url authentication failed'); + debug(MESSAGES.URL_AUTH_FAILED); return Promise.reject({ body: `${request.url} not found.`, statusCode: 404, @@ -53,12 +54,12 @@ const requestHandler = (request, response) => { } }).then(() => { // verify authorization - debug('validating basic auth', _config.listener); + debug(MESSAGES.VALIDATING_BASIC_AUTH, _config.listener); if (_config && _config.listener && _config.listener.basic_auth) { - debug('validating basic auth'); + debug(MESSAGES.VALIDATING_BASIC_AUTH); const creds = BasicAuth(request); if (!creds || (creds.name !== _config.listener.basic_auth.user || creds.pass !== _config.listener.basic_auth.pass)) { - debug('basic auth failed'); + debug(MESSAGES.BASIC_AUTH_FAILED); debug( 'expected %O but received %O', _config.listener.basic_auth, @@ -73,12 +74,12 @@ const requestHandler = (request, response) => { } }).then(() => { // validate custom headers - debug('validate custom headers'); + debug(MESSAGES.VALIDATING_CUSTOM_HEADERS); if (_config && _config.listener) { for (const headerKey in _config.listener.headers) { - debug('validating headers'); + debug(MESSAGES.VALIDATING_HEADERS); if (request.headers[headerKey] !== _config.listener.headers[headerKey]) { - debug(`${headerKey} was not found in req headers`); + debug(MESSAGES.HEADER_NOT_FOUND(headerKey)); return Promise.reject({ body: 'Header key mismatch.', statusCode: 417, @@ -88,7 +89,7 @@ const requestHandler = (request, response) => { } } }).then(async () => { - debug('parsing json'); + debug(MESSAGES.PARSING_JSON); try { if (_config.reqBodyLimit) { jsonParser = promisify(bodyParser.json({ limit: _config.reqBodyLimit })); @@ -103,13 +104,13 @@ const requestHandler = (request, response) => { locale = body.data.locale; } debug('_config.listener.actions[type]', _config.listener.actions[type]); - debug('event', event); + debug(MESSAGES.EVENT, event); // validate event:type if ( !_config.listener.actions[type] || _config.listener.actions[type].indexOf(event) === -1 ) { - debug(`${event}:${type} not defined for processing`); + debug(MESSAGES.EVENT_NOT_DEFINED(event, type)); return Promise.reject({ body: `${event}:${type} not defined for processing`, statusCode: 403, @@ -137,9 +138,9 @@ const requestHandler = (request, response) => { } data.event = event; _notify(data).then((data) => { - debug('Data [_notify]', data); + debug(MESSAGES.DATA_RECEIVED_NOTIFY, data); }).catch((error) => { - debug('Error [_notify]', error); + debug(MESSAGES.ERROR_OCCURRED_NOTIFY, error); }); return Promise.resolve({ statusCode: 200, statusMessage: 'OK', body: data }); } catch (err) { @@ -150,7 +151,7 @@ const requestHandler = (request, response) => { }); } }).then((value) => { - debug('Value', value); + debug(MESSAGES.VALUE, value); response.setHeader('Content-Type', 'application/json'); response.statusCode = value.statusCode; response.statusMessage = value.statusMessage; @@ -162,7 +163,7 @@ const requestHandler = (request, response) => { response.end(JSON.stringify(safeBody)); return; }).catch((error) => { - debug('Error', error); + debug(MESSAGES.ERROR, error); const safeError = { statusCode: error.statusCode || 500, statusMessage: error.statusMessage || 'Internal Server Error', diff --git a/src/index.ts b/src/index.ts index e9e608f..95115c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import { merge } from 'lodash'; import { createListener } from './core'; import { defaultConfig } from './defaults'; import { logger as log, setLogger } from './logger'; +import { MESSAGES } from './messages'; const debug = Debug('webhook:listener'); let notify; @@ -26,7 +27,7 @@ export function register(consumer: any) { if (typeof consumer !== 'function') { throw new Error('Provide function to notify consumer.'); } - debug('register called with %O', notify); + debug(MESSAGES.REGISTER_CALLED, notify); notify = consumer; return true; } @@ -44,7 +45,7 @@ export function start(userConfig: any, customLogger?: any) { if (customLogger) { setLogger(customLogger); } - debug('start called with %O', userConfig); + debug(MESSAGES.START_CALLED, userConfig); appConfig = merge(appConfig, userConfig) validateConfig(appConfig); @@ -59,10 +60,10 @@ export function start(userConfig: any, customLogger?: any) { ); } - debug('starting with config: ' + JSON.stringify(appConfig)); + debug(MESSAGES.STARTING_WITH_CONFIG(JSON.stringify(appConfig))); const port = process.env.PORT || appConfig.listener.port; const server = createListener(appConfig, notify).listen(port, () => { - log.info(`Server running at port ${port}`); + log.info(MESSAGES.SERVER_RUNNING(port)); }); return resolve(server); } catch (error) { @@ -102,14 +103,14 @@ function validateConfig(customConfig) { customConfig.listener.endpoint = '/' + customConfig.listener.endpoint; } } else { - throw new TypeError('Please provide valide listener.endpoint'); + throw new TypeError(MESSAGES.INVALID_LISTENER_ENDPOINT); } } if ( customConfig.listener.port && typeof customConfig.listener.port !== 'number' ) { - throw new TypeError('Please provide valide listener.port'); + throw new TypeError(MESSAGES.INVALID_LISTENER_PORT); } } } diff --git a/src/logger.ts b/src/logger.ts index cf58b22..6a09333 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,4 +1,5 @@ import { Console } from 'console'; +import { MESSAGES } from './messages'; let logger; logger = new Console(process.stdout, process.stderr); @@ -11,10 +12,10 @@ logger = new Console(process.stdout, process.stderr); function setLogger(customLogger) { const validator = validateLogger(customLogger); if (!validator) { - console.warn('Failed to register logger, using console for logging.'); + console.warn(MESSAGES.LOGGER_REGISTRATION_FAILED); } else { logger = customLogger; - logger.info('Logger registered successfully.'); + logger.info(MESSAGES.LOGGER_REGISTERED_SUCCESS); } } @@ -26,7 +27,7 @@ const validateLogger = (instance) => { const requiredFn = ['info', 'warn', 'log', 'error', 'debug']; requiredFn.forEach((name) => { if (typeof instance[name] !== 'function') { - console.warn(`Unable to register custom logger since '${name}()' does not exist on ${instance}!`); + console.warn(MESSAGES.UNABLE_TO_REGISTER_LOGGER(name, instance)); flag = true; } }); diff --git a/src/messages.ts b/src/messages.ts new file mode 100644 index 0000000..2f937e9 --- /dev/null +++ b/src/messages.ts @@ -0,0 +1,54 @@ +/*! + * contentstack-webhook-listener + * copyright (c) Contentstack LLC + * MIT Licensed + */ + +'use strict'; + +/** + * Centralized messages for the webhook listener. + * This file contains all log messages, debug messages, and error messages + * used throughout the application. + */ + +export const MESSAGES = { + // Debug messages for index.ts + REGISTER_CALLED: 'Register called with object: %O', + START_CALLED: 'Start called with object: %O', + STARTING_WITH_CONFIG: (config: string) => `Starting with config: ${config}`, + + // Log messages for index.ts + SERVER_RUNNING: (port: string | number) => `Server is running on port ${port}.`, + + // Error messages for index.ts + INVALID_LISTENER_ENDPOINT: 'Please provide a valid listener endpoint.', + INVALID_LISTENER_PORT: 'Please provide a valid listener port.', + + // Log messages for core.ts + REQUEST_RECEIVED: 'Request received.', + + // Debug messages for core.ts + ONLY_POST_SUPPORTED: 'Only POST requests are supported.', + REQUEST_INVOKED: (url: string) => `Request invoked: ${url}`, + URL_AUTH_FAILED: 'URL authentication failed.', + VALIDATING_BASIC_AUTH: 'Validating basic authentication...', + BASIC_AUTH_FAILED: 'Basic authentication failed.', + VALIDATING_CUSTOM_HEADERS: 'Validating custom headers...', + VALIDATING_HEADERS: 'Validating headers...', + HEADER_NOT_FOUND: (headerKey: string) => `Header '${headerKey}' was not found in the request.`, + PARSING_JSON: 'Parsing JSON...', + EVENT: 'Event', + EVENT_NOT_DEFINED: (event: string, type: string) => `Event '${event}:${type}' not defined for processing.`, + DATA_RECEIVED_NOTIFY: 'Data received for [_notify].', + ERROR_OCCURRED_NOTIFY: 'Error occurred in [_notify].', + VALUE: 'Value', + ERROR: 'Error', + + // Logger messages + LOGGER_REGISTRATION_FAILED: 'Failed to register logger.', + LOGGER_REGISTERED_SUCCESS: 'Logger registered successfully.', + UNABLE_TO_REGISTER_LOGGER: (name: string, instance: any) => + `Unable to register custom logger: '${name}()' does not exist on ${instance}.`, +}; + diff --git a/test/unit/index.test.js b/test/unit/index.test.js index d96aec5..fa84374 100644 --- a/test/unit/index.test.js +++ b/test/unit/index.test.js @@ -101,7 +101,7 @@ describe("Test start method without user config.", () => { .then(response => { expect(response.statusCode).toBe(400); expect(response.body.error.message).toBe( - "Only POST call is supported.", + "Only POST requests are supported.", ); }); }); @@ -147,7 +147,7 @@ describe("Test start method with custom logger", () => { }); }); -describe("Test start method with invalid user config", async () => { +describe("Test start method with invalid user config", () => { test("It should throw error when endpoint in config set to number", () => { let config; register(notify); @@ -160,7 +160,7 @@ describe("Test start method with invalid user config", async () => { start(config) .then(svr => {}) .catch(error => { - expect(error.message).toBe("Please provide valide listener.endpoint"); + expect(error.message).toBe("Please provide a valid listener endpoint."); }); }); @@ -176,7 +176,7 @@ describe("Test start method with invalid user config", async () => { start(config) .then(svr => {}) .catch(error => { - expect(error.message).toBe("Please provide valide listener.port"); + expect(error.message).toBe("Please provide a valid listener port."); }); }); }); @@ -223,7 +223,7 @@ describe("Test start method with user config", () => { .then(response => { expect(response.statusCode).toBe(400); expect(response.body.error.message).toBe( - "Only POST call is supported.", + "Only POST requests are supported.", ); }); });