From 241d358b672bd61fa1e0ba74e39630fb7e602a36 Mon Sep 17 00:00:00 2001 From: Krzysztof Nozderko Date: Tue, 19 Sep 2023 12:16:18 +0200 Subject: [PATCH] SNOW-856233 easy logging parser --- lib/configuration/client_configuration.js | 29 ++++ lib/configuration/configuration_parser.js | 50 +++++++ .../configuration_parser_test.js | 139 ++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 lib/configuration/client_configuration.js create mode 100644 lib/configuration/configuration_parser.js create mode 100644 test/unit/configuration/configuration_parser_test.js diff --git a/lib/configuration/client_configuration.js b/lib/configuration/client_configuration.js new file mode 100644 index 000000000..d7c719a4f --- /dev/null +++ b/lib/configuration/client_configuration.js @@ -0,0 +1,29 @@ +const Levels = Object.freeze({ + Off: 'OFF', + Error: 'ERROR', + Warn: 'WARN', + Info: 'INFO', + Debug: 'DEBUG', + Trace: 'TRACE' +}); + +const allLevels = Object.values(Levels); + +class ClientConfig { + constructor (logLevel, logPath) { + this.logLevel = logLevel; + this.logPath = logPath; + } +} + +exports.ClientConfig = ClientConfig; + +exports.Levels = Levels; + +exports.levelFromString = function (value) { + const level = value.toUpperCase(); + if (!allLevels.includes(level)) { + throw new Error('Unknown log level: ' + value); + } + return level; +}; diff --git a/lib/configuration/configuration_parser.js b/lib/configuration/configuration_parser.js new file mode 100644 index 000000000..25675e4a7 --- /dev/null +++ b/lib/configuration/configuration_parser.js @@ -0,0 +1,50 @@ +const Logger = require('../logger'); +const fsPromises = require('fs/promises'); +const clientConfiguration = require('./client_configuration'); + +exports.parse = async function (configFilePath) { + const configFile = await tryToFind(configFilePath); + return configFile == null ? null : tryToParse(configFile); +}; + +function tryToFind (filePath) { + if (!filePath) { + return Promise.resolve(null); + } + return fsPromises.readFile(filePath, { encoding: 'utf8' }) + .catch(err => { + const errorMessage = 'Finding easy logging configuration failed'; + Logger.getInstance().error(errorMessage + '\n' + err.toString()); + throw new Error(errorMessage); + }); +} + +function tryToParse (configurationJson) { + try { + const parsedConfiguration = JSON.parse(configurationJson); + validate(parsedConfiguration); + return new clientConfiguration.ClientConfig( + logLevel(parsedConfiguration), + logPath(parsedConfiguration) + ); + } catch (e) { + const errorMessage = 'Parsing easy logging configuration failed'; + Logger.getInstance().error(errorMessage + '\n' + e.toString()); + throw new Error(errorMessage); + } +} + +function validate (configuration) { + const level = logLevel(configuration); + if (level != null) { + clientConfiguration.levelFromString(level); + } +} + +function logLevel (configuration) { + return configuration.common.log_level; +} + +function logPath (configuration) { + return configuration.common.log_path; +} diff --git a/test/unit/configuration/configuration_parser_test.js b/test/unit/configuration/configuration_parser_test.js new file mode 100644 index 000000000..6f7882c12 --- /dev/null +++ b/test/unit/configuration/configuration_parser_test.js @@ -0,0 +1,139 @@ +const assert = require('assert'); +const configurationParser = require('./../../../lib/configuration/configuration_parser'); +const fsPromises = require('fs/promises'); +const os = require('os'); +const path = require('path'); +const uuidV4 = require('uuid/v4'); +const tempDir = path.join(os.tmpdir(), uuidV4()); + +describe('Configuration parsing tests', function () { + + before(async function () { + await fsPromises.mkdir(tempDir, { recursive: false }); + }); + + after(async function () { + await fsPromises.rm(tempDir, { recursive: true, force: true }); + }); + + it('should parse json', async function () { + // given + const fileName = 'config.json'; + const filePath = path.join(tempDir, fileName); + const fileContent = `{ + "common": { + "log_level": "info", + "log_path": "/some-path/some-directory" + } + }`; + await fsPromises.writeFile(filePath, fileContent, { encoding: 'utf8' }); + + // when + const configuration = await configurationParser.parse(filePath); + + // then + assert.equal(configuration.logLevel, 'info'); + assert.equal(configuration.logPath, '/some-path/some-directory'); + }); + + it('should fail when wrong level', async function () { + // given + const fileName = 'config_wrong_level.json'; + const filePath = path.join(tempDir, fileName); + const fileContent = `{ + "common": { + "log_level": "unknown", + "log_path": "/some-path/some-directory" + } + }`; + await fsPromises.writeFile(filePath, fileContent, { encoding: 'utf8' }); + + // expect + await assert.rejects( + async () => await configurationParser.parse(filePath), + (err) => { + assert.strictEqual(err.name, 'Error'); + assert.strictEqual(err.message, 'Parsing easy logging configuration failed'); + return true; + }); + }); + + const noValueFileContents = [ + `{ + "common": { + "log_level": null, + "log_path": null + } + }`, + `{ + "common": {} + }` + ]; + + noValueFileContents.forEach((fileContent, index) => { + it('should parse config without values ' + index.toString(), async function () { + // given + const fileName = 'config_nulls_' + index.toString() + '.json'; + const filePath = path.join(tempDir, fileName); + await fsPromises.writeFile(filePath, fileContent, { encoding: 'utf8' }); + + // when + const configuration = await configurationParser.parse(filePath); + + // then + assert.equal(configuration.logLevel, null); + assert.equal(configuration.logPath, null); + }); + }); + + const notGivenFileNames = [null, '', undefined]; + + notGivenFileNames.forEach((filePath, index) => { + it('should return null when config file not given ' + index.toString(), async function () { + // when + const configuration = await configurationParser.parse(filePath); + + // then + assert.strictEqual(configuration, null); + }); + }); + + it('should fail when config file does not exist', async function () { + // expect + await assert.rejects( + async () => await configurationParser.parse('./not-existing-config.json'), + (err) => { + assert.strictEqual(err.name, 'Error'); + assert.strictEqual(err.message, 'Finding easy logging configuration failed'); + return true; + }); + }); + + const wrongFileContents = [ + `{ + "common": { + "log_level": "unknown", + "log_path": "/some-path/some-directory" + } + }`, + '{}' + ]; + + wrongFileContents.forEach((fileContent, index) => { + it('should fail for wrong config content ' + index.toString(), async function () { + // given + const fileName = 'config_wrong_' + index.toString() + '.json'; + const filePath = path.join(tempDir, fileName); + await fsPromises.writeFile(filePath, fileContent, { encoding: 'utf8' }); + + // expect + await assert.rejects( + async () => await configurationParser.parse(filePath), + (err) => { + assert.strictEqual(err.name, 'Error'); + assert.strictEqual(err.message, 'Parsing easy logging configuration failed'); + return true; + }); + }); + }); +});