diff --git a/.eslintrc b/.eslintrc index f6000cb..8c3d232 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,6 +15,27 @@ "parserOptions": { "ecmaVersion": 2020 }, + "rules": { + "camelcase": [2, {"properties": "always"}], + "comma-dangle": [2, "never"], + "dot-location": [2, "property"], + "lines-around-comment": 0, + "max-len": [2, {"code": 100, "comments": 80}], + "newline-after-var": 0, + "no-alert": 2, + "no-console": 2, + "no-debugger": 2, + "no-else-return": 2, + "no-nested-ternary": 0, + "no-unmodified-loop-condition": 0, + "no-unused-vars": 1, + "object-curly-spacing": [2, "always"], + "operator-linebreak": [2, "after"], + "quotes": [2, "single"], + "require-unicode-regexp": 1, + "space-before-function-paren": 2, + "strict": 0 + }, "overrides": [ { "files": ["*.ts"], @@ -55,9 +76,10 @@ "operator-linebreak": [2, "after"], "quotes": [2, "single"], "require-unicode-regexp": 1, - "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}], // JSLint style + "space-before-function-paren": 2, "strict": 0, "@typescript-eslint/restrict-plus-operands": 0, + "@typescript-eslint/restrict-template-expressions": 0, "jsdoc/check-alignment": 1, "jsdoc/check-param-names": 1, "jsdoc/check-tag-names": 1, @@ -91,12 +113,6 @@ } } } - }, - { - "files": ["test/unit-tests/runtime.ts"], - "rules": { - "no-console": 0 - } } ] } diff --git a/demos/index.html b/demos/index.html new file mode 100644 index 0000000..ace3f6e --- /dev/null +++ b/demos/index.html @@ -0,0 +1,10 @@ + + + + + Morningstar Connectors Demos + + + Highcharts Stock demo + + \ No newline at end of file diff --git a/package-build.json b/package-build.json index 78f496e..ad0e727 100644 --- a/package-build.json +++ b/package-build.json @@ -7,9 +7,11 @@ "author": "Highsoft AS", "bugs": "https://www.highcharts.com/support/", "homepage": "https://www.highcharts.com/", + "bin": "bin/morningstar-connectors.js", "main": "es-modules/index.js", "types": "es-modules/index.d.ts", "files": [ + "bin/", "es-modules/", "LICENSE.md", "morningstar-*", diff --git a/package-lock.json b/package-lock.json index 8562629..c781796 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,12 @@ "name": "@highcharts/morningstar-connectors", "version": "0.0.1-dev", "license": "UNLICENSED", + "bin": { + "morningstar-connectors": "bin/morningstar-connectors.js" + }, "devDependencies": { "@types/jsdom": "^21.1.7", + "@types/node": "^20.0.0", "@typescript-eslint/eslint-plugin": "^7.17.0", "eslint": "^8.56.0", "eslint-plugin-brackets": "^0.1.3", @@ -23,7 +27,7 @@ "webpack-cli": "^5.1.4" }, "peerDependencies": { - "@highcharts/dashboards": "^2.2.0", + "@highcharts/dashboards": ">=2.2.0", "highcharts": ">=11.4.0" } }, @@ -435,13 +439,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.0.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.0.2.tgz", - "integrity": "sha512-yPL6DyFwY5PiMVEwymNeqUTKsDczQBJ/5T7W/46RwLU/VH+AA8aT5TZkvBviLKLbbm0hlfftEkGrNzfRk/fofQ==", + "version": "20.14.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.14.tgz", + "integrity": "sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.11.1" + "undici-types": "~5.26.4" } }, "node_modules/@types/tough-cookie": { @@ -4086,9 +4090,9 @@ } }, "node_modules/undici-types": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz", - "integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==", + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index ee9ad96..45b9e1a 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,13 @@ "bugs": "https://github.com/highcharts/morningstar-connectors/issues", "homepage": "https://github.com/highcharts/morningstar-connectors#readme", "repository": "https://github.com/highcharts/morningstar-connectors.git", + "bin": "bin/morningstar-connectors.js", "main": "code/es-modules/index.js", "types": "code/es-modules/index.d.ts", "devDependencies": { "@typescript-eslint/eslint-plugin": "^7.17.0", "@types/jsdom": "^21.1.7", + "@types/node": "^20.0.0", "eslint": "^8.56.0", "eslint-plugin-brackets": "^0.1.3", "eslint-plugin-jsdoc": "^48.8.3", @@ -30,19 +32,21 @@ ] }, "peerDependencies": { - "@highcharts/dashboards": "^2.2.0", + "@highcharts/dashboards": ">=2.2.0", "highcharts": ">=11.4.0" }, "scripts": { "build": "npm run webpack && npm run build:prepare && npm run build:copy && npm run build:pack && npm run build:cleanup", "build:cleanup": "rm -rf build/package/", - "build:copy": "cp -R code/* LICENSE.md README.md build/package/ && cp package-build.json build/package/package.json", + "build:copy": "cp -R bin code/* LICENSE.md README.md build/package/ && cp package-build.json build/package/package.json", "build:pack": "npm pack build/package/ --pack-destination build/", "build:prepare": "rm -rf build/ ; mkdir -p build/package/", "demos": "npm run webpack && npm run demos:server", - "demos:server": "(sleep 1 ; open http://localhost:8080) & python3 -m http.server --bind localhost 8080", + "demos:server": "(sleep 1 ; open http://localhost:8080) & node bin/morningstar-connectors demos", "prepare": "rm -rf '.husky/_' ; husky", - "scripts": "rm -rf code/ ; tsc -p src/", + "scripts": "npm run scripts:bin && npm run scripts:code", + "scripts:bin": "rm -rf bin/ ; tsc -p src/CLI/ ; chmod +x bin/*.js", + "scripts:code": "rm -rf code/ ; tsc -p src/", "reset": "rm -rf bin/ build/ code/ node_modules/ ; npm i", "test": "npm run test:unit-tests", "test:morningstar": "npx -y newman run --bail tmp/Collection.json -e tmp/Environment.json", diff --git a/src/CLI/Library/Args.ts b/src/CLI/Library/Args.ts new file mode 100644 index 0000000..5d54846 --- /dev/null +++ b/src/CLI/Library/Args.ts @@ -0,0 +1,113 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Sophie Bremer + * + * */ + + +'use strict'; + + +/* * + * + * Constants + * + * */ + + +export const SHORTCUTS: Record = { + '?': 'help', + h: 'help', + v: 'version' +}; + + +/* * + * + * Interface + * + * */ + + +export type Args = Record; + + +export type ArgValue = (true|string|Array); + + +/* * + * + * Functions + * + * */ + + +/** + * Converts CLI arguments into a flexible dictionary. + * + * @param argv + * Arguments to convert. + * + * @param shortcuts + * Shortcut arguments as a map to their full counterpart. + * + * @return + * Converted CLI arguments. + */ +function getArgs ( + argv = process.argv, + shortcuts = SHORTCUTS +): Args { + const args: Args = {}; + + let currentKey: string = '_'; + let currentValue: ArgValue; + + for (const arg of argv) { + if ( + currentKey === '_' && + arg.startsWith('/') + ) { + continue; + } + if (arg.startsWith('--')) { + currentKey = arg.substring(2); + args[currentKey] = true; + } else if (arg.startsWith('-')) { + currentKey = shortcuts[arg.substring(1)]; + args[currentKey] = true; + } else { + currentValue = args[currentKey]; + if (currentKey === '_') { + args[currentKey] = arg; + } else if (currentValue instanceof Array) { + currentValue.push(arg); + } else if (typeof currentValue === 'string') { + args[currentKey] = [currentValue, arg]; + } else { + args[currentKey] = arg; + } + } + } + + return args; +} + + +/* * + * + * Default Export + * + * */ + + +export default { + getArgs +}; diff --git a/src/CLI/Library/Server.ts b/src/CLI/Library/Server.ts new file mode 100644 index 0000000..079b3d1 --- /dev/null +++ b/src/CLI/Library/Server.ts @@ -0,0 +1,250 @@ +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Sophie Bremer + * + * */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import * as FS from 'node:fs'; +import * as HTTP from 'node:http'; +import * as Path from 'node:path'; + + +/* * + * + * Constants + * + * */ + + +export const DEFAULT_PORT = 8080; + + +export const MIMES: Record = { + css: 'text/css', + eot: 'application/vnd.ms-fontobject', + js: 'application/javascript', + json: 'application/json', + html: 'text/html', + ico: 'image/x-icon', + map: 'application/json', + png: 'image/png', + svg: 'image/svg+xml', + ttf: 'font/ttf', + txt: 'text/plain', + woff: 'font/woff', + woff2: 'font/woff2', + xml: 'application/xml' +}; + + +const PATH_ESCAPE = /\.\.?\/|\/\.|\/\//u; + + +/* * + * + * Functions + * + * */ + + +/** + * Removes path elements that could result in a folder escape. + * + * @param path + * Path to sanitize. + * + * @return + * Sanitized path. + */ +function sanitizePath (path: string) { + path = (new URL(path, 'http://localhost')).pathname; + + while (PATH_ESCAPE.test(path)) { + path = path.replace(PATH_ESCAPE, ''); + } + + return path; +} + + +/* * + * + * Class + * + * */ + + +export class Server { + + + /* * + * + * Constructor + * + * */ + + + public constructor ( + folder: string = process.cwd() + ) { + this.folder = folder; + this.http = new HTTP.Server((req, res) => this.handle(req, res)); + } + + + /* * + * + * Properties + * + * */ + + + public folder: string; + + + public readonly http: HTTP.Server; + + + /* * + * + * Functions + * + * */ + + + /** + * Handles incoming HTTP messages with a related response. + * + * @param request + * Incoming HTTP message. + * + * @param response + * Outgoing HTTP message. + */ + public handle ( + request: HTTP.IncomingMessage, + response: HTTP.ServerResponse + ): void { + let folder = this.folder; + let path = sanitizePath(request.url || '/index.html'); + + if (path.startsWith('/code/')) { + if (FS.existsSync('code')) { + // Runs in repository + folder = '.'; + } else { + // Runs in package + folder = Path.join(__dirname, '..'); + path = path.substring(5); + } + } + + let file = Path.posix.basename(path); + + if (path.endsWith('/')) { + file = 'index.html'; + } else { + file = Path.posix.basename(path); + path = Path.posix.dirname(path) + '/'; + } + + let ext = Path.posix.extname(file).substring(1); + + if (!MIMES[ext]) { + ext = 'html'; + file += '.html'; + } + + let filePath = Path.posix + .join(folder.replace(Path.sep, Path.posix.sep), path, file) + .replace(Path.posix.sep, Path.sep); + + while (filePath.startsWith(Path.sep)) { + filePath = filePath.substring(1); + } + + FS.readFile( + filePath, + (error, data) => { + if (error) { + // eslint-disable-next-line no-console + console.error(error.message); + response.writeHead(404); + response.end('404: Path not found', 'utf-8'); + } else { + response.writeHead(200, { 'Content-Type': MIMES[ext] }); + response.end(data); + } + } + ); + + } + + + /** + * Starts the server. + * + * @param port + * HTTP port to use. + * + * @param folder + * Local folder to serve. + * + * @return + * Server instance for reference. + */ + public start ( + port: number = DEFAULT_PORT, + folder?: string + ): Server { + + this.folder = (folder || this.folder); + this.http.listen(port); + + return this; + } + + + /** + * Stops the server. + * + * @return + * Server instance for reference. + */ + public stop (): Server { + + this.http.closeAllConnections(); + + return this; + } + + +} + + +/* * + * + * Default Export + * + * */ + + +export default Server; diff --git a/src/CLI/morningstar-connectors.ts b/src/CLI/morningstar-connectors.ts new file mode 100644 index 0000000..be7fe7b --- /dev/null +++ b/src/CLI/morningstar-connectors.ts @@ -0,0 +1,142 @@ +#!/usr/bin/env node +/* * + * + * (c) 2009-2024 Highsoft AS + * + * License: www.highcharts.com/license + * + * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! + * + * Authors: + * - Sophie Bremer + * + * */ + + +/* eslint-disable no-console */ + + +'use strict'; + + +/* * + * + * Imports + * + * */ + + +import Args, { SHORTCUTS as ARGS_SHORTCUTS } from './Library/Args'; +import * as FS from 'node:fs/promises'; +import * as Path from 'node:path'; +import Server, { DEFAULT_PORT } from './Library/Server'; + + +/* * + * + * Constants + * + * */ + + +export const HELP = ` +npx morningstar-connectors [COMMAND] [OPTIONS] + +COMMAND: + + demos Opens demos in your web browser. + + docs Opens docs in your browser. + +OPTIONS: + + --environment -e [path] Postman environment file to use. + + --help -h This help. + + --port -p [int] Port to use for the local web server. + (default: ${DEFAULT_PORT}) + + --version -v Version of the Morningstar connectors package. + +EXAMPLE: + +npx morningstar-connectors demo -e postman_environment.json -p 8080 + +`; + + +const SHORTCUTS = { + ...ARGS_SHORTCUTS, + e: 'environment', + p: 'port' +}; + + +/* * + * + * Functions + * + * */ + + +export async function main (): Promise { + const args = Args.getArgs(process.argv, SHORTCUTS); + + if (args.help) { + console.info(HELP); + return; + } + + if (args.version) { + console.info( + (JSON.parse( + await FS.readFile( + // Path relative to bin folder + Path.join(__dirname, '..', 'package.json'), + 'utf8' + ) + ) as Record).version + ); + return; + } + + const port = parseInt(( + typeof args.port === 'string' ? + args.port : + `${DEFAULT_PORT}` + ), 10); + + let server: Server; + + switch (args._) { + + case 'demo': + case 'demos': + server = new Server('demos'); + break; + + default: + console.info(HELP); + throw new Error(`No valid command provided. (${'' + args._})`); + + } + + server.start(port); + + console.info(`Content available at http://localhost:${port}.`); + +} + + +/* * + * + * Runtime + * + * */ + + +main().catch(error => { + console.error(`${error}`); + process.exit(1); +}); diff --git a/src/CLI/tsconfig.json b/src/CLI/tsconfig.json new file mode 100644 index 0000000..f76d352 --- /dev/null +++ b/src/CLI/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "declaration": false, + "sourceMap": false, + "module": "CommonJS", + "outDir": "../../bin/" + }, + "files": [ + "morningstar-connectors.ts" + ] +} \ No newline at end of file diff --git a/src/RNANews/RNANewsConnector.ts b/src/RNANews/RNANewsConnector.ts index f380486..f2c50d8 100644 --- a/src/RNANews/RNANewsConnector.ts +++ b/src/RNANews/RNANewsConnector.ts @@ -46,7 +46,7 @@ import RNANewsJSON from './RNANewsJSON'; * @param {number | string} date date as a timestamp of formatted string * @return {string} date formatted as `yyyy-MM-dd`. */ -function validateAndFormatDate(date: number | string): string { +function validateAndFormatDate (date: number | string): string { let timestamp: number; if (typeof date === 'string') { // Check if string is a number, likely a timestamp @@ -96,7 +96,7 @@ export class RNANewsConnector extends MorningstarConnector { * @param {RNANewsOptions} [options] * Options for the connector and converter. */ - public constructor( + public constructor ( options: RNANewsOptions = {} ) { super(options); @@ -132,7 +132,7 @@ export class RNANewsConnector extends MorningstarConnector { * @return {Promise} * Same connector instance with modified table. */ - public override async load(): Promise { + public override async load (): Promise { const options = this.options; const { security, diff --git a/src/RNANews/RNANewsConverter.ts b/src/RNANews/RNANewsConverter.ts index e0525ef..8f12bf9 100644 --- a/src/RNANews/RNANewsConverter.ts +++ b/src/RNANews/RNANewsConverter.ts @@ -53,7 +53,7 @@ export class RNANewsConverter extends MorningstarConverter { * @param {RNANewsConverterOptions} [options] * Options for the converter. */ - constructor( + constructor ( options: RNANewsConverterOptions = {} ) { super(options); @@ -90,7 +90,7 @@ export class RNANewsConverter extends MorningstarConverter { * Options for the parser * */ - public parse( + public parse ( options: RNANewsConverterOptions ): (boolean|undefined) { @@ -140,7 +140,7 @@ export class RNANewsConverter extends MorningstarConverter { * @return {DataTable} * Table from the parsed RNANews */ - public override getTable(): External.DataTable { + public override getTable (): External.DataTable { return MorningstarConverter.getTableFromColumns(this.columns, this.header); } diff --git a/src/RNANews/RNANewsJSON.ts b/src/RNANews/RNANewsJSON.ts index 9a49204..8d56ba9 100644 --- a/src/RNANews/RNANewsJSON.ts +++ b/src/RNANews/RNANewsJSON.ts @@ -71,7 +71,7 @@ export namespace RNANewsJSON { * */ - export function isResponse( + export function isResponse ( json?: unknown ): json is Response { return ( @@ -83,7 +83,7 @@ export namespace RNANewsJSON { } - function isResponseItem( + function isResponseItem ( json?: unknown ): json is ResponseItem { return ( @@ -96,7 +96,7 @@ export namespace RNANewsJSON { } - function isRNANewsResponseItem( + function isRNANewsResponseItem ( json?: unknown ): json is RNANewsResponseItem { return ( diff --git a/src/Shared/LocalizationOptions.ts b/src/Shared/LocalizationOptions.ts index 31be8be..9561c33 100644 --- a/src/Shared/LocalizationOptions.ts +++ b/src/Shared/LocalizationOptions.ts @@ -22,7 +22,10 @@ * */ -export type Currency = ('AUD'|'CAD'|'CNY'|'EUR'|'GBP'|'INR'|'JPY'|'USD'); +/** + * Currency to use for value conversions. Use `BAS` for base currency. + */ +export type Currency = ('AUD'|'BAS'|'CAD'|'CNY'|'EUR'|'GBP'|'INR'|'JPY'|'USD'); export interface DateOptions { @@ -45,7 +48,10 @@ export interface LocalizationOptions { country: string; /** - * The currency to use. + * ISO 4217 currency code to use for value conversions, or `BAS` for base + * currency. + * + * @default "USD" */ currency: Currency; diff --git a/src/Shared/MorningstarAPI.ts b/src/Shared/MorningstarAPI.ts index f6d9957..52c65d1 100644 --- a/src/Shared/MorningstarAPI.ts +++ b/src/Shared/MorningstarAPI.ts @@ -47,7 +47,7 @@ export class MorningstarAPI { * */ - public constructor( + public constructor ( options: MorningstarAPIOptions = {} ) { @@ -107,14 +107,14 @@ export class MorningstarAPI { * */ - public delay( + public delay ( milliseconds: number ): Promise { return new Promise(resolve => window.setTimeout(resolve, milliseconds)); } - public async fetch( + public async fetch ( url: MorningstarURL, requestInit: RequestInit = {} ): Promise { diff --git a/src/Shared/MorningstarAccess.ts b/src/Shared/MorningstarAccess.ts index 63cc62a..ce32a11 100644 --- a/src/Shared/MorningstarAccess.ts +++ b/src/Shared/MorningstarAccess.ts @@ -78,7 +78,7 @@ interface OAuth2TokenJSON { * */ -function isOAuth2MessageJSON( +function isOAuth2MessageJSON ( json: unknown ): json is OAuth2MessageJSON { return ( @@ -89,7 +89,7 @@ function isOAuth2MessageJSON( } -function isOAuth2TokenJSON( +function isOAuth2TokenJSON ( json: unknown ): json is OAuth2TokenJSON { return ( @@ -119,7 +119,7 @@ export class MorningstarAccess { * */ - public constructor( + public constructor ( options: MorningstarAccessOptions = {} ) { @@ -184,7 +184,7 @@ export class MorningstarAccess { * */ - public async authenticate( + public async authenticate ( username?: string, password?: string ): Promise { @@ -268,7 +268,7 @@ export class MorningstarAccess { } - public authorizeRequest( + public authorizeRequest ( requestInit: RequestInit ): RequestInit { const token = this.token; @@ -302,7 +302,7 @@ export class MorningstarAccess { } - protected setPayload( + protected setPayload ( username?: string, password?: string ): void { @@ -321,7 +321,7 @@ export class MorningstarAccess { } - protected setTimeout( + protected setTimeout ( seconds: number ): void { @@ -335,7 +335,7 @@ export class MorningstarAccess { } - public setToken( + public setToken ( token: string ): void { this.authorized = true; diff --git a/src/Shared/MorningstarConnector.ts b/src/Shared/MorningstarConnector.ts index d26800d..aefa860 100644 --- a/src/Shared/MorningstarConnector.ts +++ b/src/Shared/MorningstarConnector.ts @@ -46,7 +46,7 @@ export abstract class MorningstarConnector extends External.DataConnector { * */ - public constructor( + public constructor ( options: MorningstarOptions = {} ) { super(options); @@ -78,7 +78,7 @@ export abstract class MorningstarConnector extends External.DataConnector { * */ - public override async load(): Promise { + public override async load (): Promise { const options = this.options; // Expecting async Postman options diff --git a/src/Shared/MorningstarConverter.ts b/src/Shared/MorningstarConverter.ts index c990ff3..4ba7f12 100644 --- a/src/Shared/MorningstarConverter.ts +++ b/src/Shared/MorningstarConverter.ts @@ -43,7 +43,7 @@ export abstract class MorningstarConverter extends External.DataConverter { * */ - public constructor( + public constructor ( options: MorningstarConverterOptions = {} ) { super(options); @@ -73,12 +73,12 @@ export abstract class MorningstarConverter extends External.DataConverter { * */ - public abstract override parse( + public abstract override parse ( options: MorningstarConverterOptions ): void; - public override getTable(): External.DataTable { + public override getTable (): External.DataTable { return this.table; } diff --git a/src/Shared/MorningstarError.ts b/src/Shared/MorningstarError.ts index 0aad8a0..ed5c2ff 100644 --- a/src/Shared/MorningstarError.ts +++ b/src/Shared/MorningstarError.ts @@ -32,7 +32,7 @@ export class MorningstarError extends Error { * */ - public constructor( + public constructor ( request: RequestInit, response: Response ) { diff --git a/src/Shared/MorningstarOptions.ts b/src/Shared/MorningstarOptions.ts index 7743e4b..1a888d8 100644 --- a/src/Shared/MorningstarOptions.ts +++ b/src/Shared/MorningstarOptions.ts @@ -126,6 +126,11 @@ export interface MorningstarOptions extends External.DataConnectorOptions { */ export interface MorningstarPostmanOptions { + /** + * Postman JSON to use. + */ + environmentJSON?: unknown; + /** * URL or path to the shared Postman Environment to use. */ diff --git a/src/Shared/MorningstarPostman.ts b/src/Shared/MorningstarPostman.ts index f05447a..e5756c5 100644 --- a/src/Shared/MorningstarPostman.ts +++ b/src/Shared/MorningstarPostman.ts @@ -47,10 +47,15 @@ export namespace MorningstarPostman { * */ - export async function getAPIOptions( + export async function getAPIOptions ( options: MorningstarPostmanOptions = {} ): Promise<(MorningstarAPIOptions|undefined)> { + if (options.environmentJSON) { + return getAPIOptionsFromPostmanEnvironment( + PostmanEnvironment.fromJSON(options.environmentJSON) + ); + } if (options.environmentURL) { return getAPIOptionsFromPostmanEnvironment( await PostmanEnvironment.fromURL(options.environmentURL) @@ -61,7 +66,7 @@ export namespace MorningstarPostman { } - function getAPIOptionsFromPostmanEnvironment( + function getAPIOptionsFromPostmanEnvironment ( postmanEnvironment: PostmanEnvironment ): MorningstarAPIOptions { const apiOptions: MorningstarAPIOptions = {}; diff --git a/src/Shared/MorningstarRegion.ts b/src/Shared/MorningstarRegion.ts index d68f481..91ad98f 100644 --- a/src/Shared/MorningstarRegion.ts +++ b/src/Shared/MorningstarRegion.ts @@ -77,7 +77,7 @@ export namespace MorningstarRegion { * */ - export function detect(): Name { + export function detect (): Name { const country = window.navigator.language.toUpperCase().match(/-(\w\w)/u); if (country) { diff --git a/src/Shared/MorningstarURL.ts b/src/Shared/MorningstarURL.ts index 0bec6c3..06351e6 100644 --- a/src/Shared/MorningstarURL.ts +++ b/src/Shared/MorningstarURL.ts @@ -59,7 +59,7 @@ export class MorningstarURL extends URL { * @return * The modified search parameters as reference. */ - public setDate( + public setDate ( name: ('endDate'|'startDate'), date: (number|string) ): void { @@ -83,7 +83,7 @@ export class MorningstarURL extends URL { * @return * The modified search parameters as reference. */ - public setLocalizationOptions( + public setLocalizationOptions ( options: LocalizationOptions ): void { @@ -107,7 +107,7 @@ export class MorningstarURL extends URL { * @return * The modified search parameters as reference. */ - public setSecuritiesOptions( + public setSecuritiesOptions ( securities: Array ): void { const searchParams = this.searchParams; diff --git a/src/Shared/PostmanEnvironment.ts b/src/Shared/PostmanEnvironment.ts index b409035..c92caa9 100644 --- a/src/Shared/PostmanEnvironment.ts +++ b/src/Shared/PostmanEnvironment.ts @@ -39,7 +39,7 @@ interface PostmanEnvironmentJSON { * */ -function isPostmanEnvironmentJSON( +function isPostmanEnvironmentJSON ( obj: unknown ): obj is PostmanEnvironmentJSON { return ( @@ -57,7 +57,7 @@ function isPostmanEnvironmentJSON( } -function isPostmanEnvironmentValue( +function isPostmanEnvironmentValue ( obj: unknown ): obj is PostmanEnvironment.Value { return ( @@ -95,18 +95,37 @@ export class PostmanEnvironment { /** - * Loads the Postman Environment from the given file content or posted file. + * Loads the Postman environment from the given file content or posted file. * * @param file * File content or posted file. * * @return - * Promise to succeed. + * Promise of PostmanEnvironment instance. */ - public static async fromFile( + public static async fromFile ( file: (string|File) ): Promise { - const json: unknown = JSON.parse(typeof file === 'string' ? file : await file.text()); + return PostmanEnvironment.fromJSON(JSON.parse( + typeof file === 'string' ? + file : + await file.text() + ) as unknown); + } + + + /** + * Loads the Postman Environment from the given Postman JSON. + * + * @param json + * Postman JSON. + * + * @return + * PostmanEnvironment instance. + */ + public static fromJSON ( + json: unknown + ): PostmanEnvironment { const values = []; let id = ''; @@ -136,9 +155,9 @@ export class PostmanEnvironment { * URL to load from. * * @return - * Promise to succeed. + * Promise of PostmanEnvironment instance. */ - public static async fromURL( + public static async fromURL ( url: string ): Promise { return await this.fromFile(await (await fetch(url)).text()); @@ -152,7 +171,7 @@ export class PostmanEnvironment { * */ - protected constructor( + protected constructor ( id: string, name: string, values: Array @@ -195,7 +214,7 @@ export class PostmanEnvironment { * @return * Postman Environment value or `undefined`. */ - public getAllValuesOf( + public getAllValuesOf ( key: (string|RegExp) ): Array { const values: Array = []; @@ -227,7 +246,7 @@ export class PostmanEnvironment { * @return * Postman Environment value or `undefined`. */ - public getLastValueOf( + public getLastValueOf ( key: (string|RegExp) ): (PostmanEnvironment.Value|undefined) { let lastValue: (PostmanEnvironment.Value|undefined); @@ -265,7 +284,7 @@ export class PostmanEnvironment { * @return * Postman Environment value or `undefined`. */ - public getValueOf( + public getValueOf ( key: (string|RegExp) ): (PostmanEnvironment.Value|undefined) { diff --git a/src/TimeSeries/Converters/CumulativeReturnSeriesConverter.ts b/src/TimeSeries/Converters/CumulativeReturnSeriesConverter.ts index 7c6972a..31c5709 100644 --- a/src/TimeSeries/Converters/CumulativeReturnSeriesConverter.ts +++ b/src/TimeSeries/Converters/CumulativeReturnSeriesConverter.ts @@ -58,7 +58,7 @@ export class CumulativeReturnSeriesConverter extends MorningstarConverter { * */ - public constructor( + public constructor ( options: CumulativeReturnSeriesOptions = { type: 'CumulativeReturn' } ) { super(options); @@ -84,7 +84,7 @@ export class CumulativeReturnSeriesConverter extends MorningstarConverter { * */ - public parse( + public parse ( options: CumulativeReturnSeriesOptions ): void { const table = this.table; diff --git a/src/TimeSeries/Converters/DividendSeriesConverter.ts b/src/TimeSeries/Converters/DividendSeriesConverter.ts index bb93548..a727c1e 100644 --- a/src/TimeSeries/Converters/DividendSeriesConverter.ts +++ b/src/TimeSeries/Converters/DividendSeriesConverter.ts @@ -59,7 +59,7 @@ export class DividendSeriesConverter extends MorningstarConverter { * */ - public constructor( + public constructor ( options: DividendSeriesOptions = { type: 'Dividend' } ) { super(options); @@ -85,7 +85,7 @@ export class DividendSeriesConverter extends MorningstarConverter { * */ - public parse( + public parse ( options: DividendSeriesOptions ): void { const table = this.table; diff --git a/src/TimeSeries/Converters/GrowthSeriesConverter.ts b/src/TimeSeries/Converters/GrowthSeriesConverter.ts index d1814f7..2b0d590 100644 --- a/src/TimeSeries/Converters/GrowthSeriesConverter.ts +++ b/src/TimeSeries/Converters/GrowthSeriesConverter.ts @@ -58,7 +58,7 @@ export class GrowthSeriesConverter extends MorningstarConverter { * */ - public constructor( + public constructor ( options: GrowthSeriesOptions = { type: 'Growth' } ) { super(options); @@ -84,7 +84,7 @@ export class GrowthSeriesConverter extends MorningstarConverter { * */ - public parse( + public parse ( options: GrowthSeriesOptions ): void { const table = this.table; diff --git a/src/TimeSeries/Converters/RatingSeriesConverter.ts b/src/TimeSeries/Converters/RatingSeriesConverter.ts index a2a8faf..66f7116 100644 --- a/src/TimeSeries/Converters/RatingSeriesConverter.ts +++ b/src/TimeSeries/Converters/RatingSeriesConverter.ts @@ -58,7 +58,7 @@ export class RatingSeriesConverter extends MorningstarConverter { * */ - public constructor( + public constructor ( options: RatingSeriesOptions = { type: 'Rating' } ) { super(options); @@ -84,7 +84,7 @@ export class RatingSeriesConverter extends MorningstarConverter { * */ - public parse( + public parse ( options: RatingSeriesOptions ): void { const table = this.table; diff --git a/src/TimeSeries/TimeSeriesConnector.ts b/src/TimeSeries/TimeSeriesConnector.ts index a4e1940..db59f97 100644 --- a/src/TimeSeries/TimeSeriesConnector.ts +++ b/src/TimeSeries/TimeSeriesConnector.ts @@ -51,7 +51,7 @@ export class TimeSeriesConnector extends MorningstarConnector { * */ - public constructor( + public constructor ( options: TimeSeriesOptions ) { super(options); @@ -123,7 +123,7 @@ export class TimeSeriesConnector extends MorningstarConnector { * */ - public override async load(): Promise { + public override async load (): Promise { const options = this.options; const currencyId = options.currencyId; const endDate = options.endDate; diff --git a/src/TimeSeries/TimeSeriesJSON.ts b/src/TimeSeries/TimeSeriesJSON.ts index 590bc9d..7e7b235 100644 --- a/src/TimeSeries/TimeSeriesJSON.ts +++ b/src/TimeSeries/TimeSeriesJSON.ts @@ -85,7 +85,7 @@ namespace TimeSeriesJSON { * */ - export function isHistory( + export function isHistory ( json?: unknown ): json is History { return ( @@ -101,7 +101,7 @@ namespace TimeSeriesJSON { } - function isHistoryDetail( + function isHistoryDetail ( json?: unknown ): json is HistoryDetail { return ( @@ -113,7 +113,7 @@ namespace TimeSeriesJSON { } - export function isResponse( + export function isResponse ( json?: unknown ): json is Response { return ( @@ -125,7 +125,7 @@ namespace TimeSeriesJSON { } - function isSecurity( + function isSecurity ( json?: unknown ): json is Security { return ( @@ -148,7 +148,7 @@ namespace TimeSeriesJSON { } - export function isTimeSeriesResponse( + export function isTimeSeriesResponse ( json?: unknown ): json is TimeSeriesResponse { return ( diff --git a/src/tsconfig.json b/src/tsconfig.json index 2af25df..352acce 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -16,6 +16,6 @@ ] }, "files": [ - "./index.ts" + "index.ts" ] } \ No newline at end of file diff --git a/test/unit-tests/Shared/MorningstarAPI.test.ts b/test/unit-tests/Shared/MorningstarAPI.test.ts index a9e2702..89e780c 100644 --- a/test/unit-tests/Shared/MorningstarAPI.test.ts +++ b/test/unit-tests/Shared/MorningstarAPI.test.ts @@ -1,7 +1,7 @@ import * as Assert from 'node:assert/strict'; import * as MC from '../../../code/morningstar-connectors.src'; -export async function apiAccess( +export async function apiAccess ( apiOptions: MC.Shared.MorningstarAPIOptions ) { const api = new MC.Shared.MorningstarAPI(apiOptions); diff --git a/test/unit-tests/TimeSeries/DividendConverter.test.ts b/test/unit-tests/TimeSeries/DividendConverter.test.ts index 0f19d0d..27dc596 100644 --- a/test/unit-tests/TimeSeries/DividendConverter.test.ts +++ b/test/unit-tests/TimeSeries/DividendConverter.test.ts @@ -1,7 +1,7 @@ import * as Assert from 'node:assert/strict'; import * as MorningstarConnectors from '../../../code/morningstar-connectors.src'; -export async function ratingLoad( +export async function ratingLoad ( api: MorningstarConnectors.Shared.MorningstarAPIOptions ) { const connector = new MorningstarConnectors.TimeSeriesConnector({ diff --git a/test/unit-tests/runtime.ts b/test/unit-tests/runtime.ts index 0abc5e3..ad270a5 100644 --- a/test/unit-tests/runtime.ts +++ b/test/unit-tests/runtime.ts @@ -12,6 +12,9 @@ * */ +/* eslint-disable no-console */ + + 'use strict'; @@ -36,7 +39,7 @@ import * as JSDOM from 'jsdom'; * */ -function getAPIOptions(): Shared.MorningstarAPIOptions { +function getAPIOptions (): Shared.MorningstarAPIOptions { const apiOptions: Shared.MorningstarAPIOptions = { url: 'https://www.emea-api.morningstar.com/ecint/v1/' }; @@ -59,7 +62,7 @@ function getAPIOptions(): Shared.MorningstarAPIOptions { } -async function logError( +async function logError ( error: unknown ): Promise { @@ -94,7 +97,7 @@ async function logError( } -function prepareGlobals() { +function prepareGlobals () { const jsdom = new JSDOM.JSDOM(); const window = jsdom.window; const originalDispatchEvent = window.dispatchEvent; @@ -118,7 +121,7 @@ function prepareGlobals() { } -async function runUnitTests() { +async function runUnitTests () { const failures: Array = []; const successes: Array = []; const stdout = process.stdout; @@ -197,7 +200,7 @@ async function runUnitTests() { } -function stdWrite( +function stdWrite ( ...text: Array ): void { process.stdout.write([ diff --git a/webpack.config.ts b/webpack.config.ts index cc4ab01..7f83e06 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -45,6 +45,12 @@ const externals = { commonjs: '@highcharts/dashboards/datagrid', commonjs2: '@highcharts/dashboards/datagrid', root: 'DataGrid' + }, + 'highcharts': { + amd: 'highcharts/highcharts', + commonjs: 'highcharts', + commonjs2: 'highcharts', + root: 'Highcharts' } };