diff --git a/README.md b/README.md index 809de75..e46d88a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ npm install @cityssm/faster-url-builder import { FasterUrlBuilder } from '@cityssm/faster-url-builder' console.log(new FasterUrlBuilder('test-tenant').loginUrl) -// => https://test-tenant.fasterwebcloud.com/FASTER +// => https://test-tenant.fasterwebcloud.com/FASTER/Login ``` ## Included URLs @@ -40,8 +40,8 @@ console.log(new FasterUrlBuilder('test-tenant').loginUrl) ## Related Projects -[FASTER Web Report Exporter](https://github.com/cityssm/node-faster-report-exporter)
+[**FASTER Web Report Exporter**](https://github.com/cityssm/node-faster-report-exporter)
On demand exports of selected reports from the FASTER Web Fleet Management System. -[FASTER Web Helper](https://github.com/cityssm/faster-web-helper)
+[**FASTER Web Helper**](https://github.com/cityssm/faster-web-helper)
A service to support integrations with the FASTER Web fleet management system. diff --git a/eslint.config.d.ts b/eslint.config.d.ts index 29528c9..a1905cc 100644 --- a/eslint.config.d.ts +++ b/eslint.config.d.ts @@ -1 +1,3 @@ -export { default } from 'eslint-config-cityssm'; +import { type Config } from 'eslint-config-cityssm'; +declare const config: Config; +export default config; diff --git a/eslint.config.js b/eslint.config.js index 29528c9..fa3adae 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1 +1,14 @@ -export { default } from 'eslint-config-cityssm'; +import eslintConfigCityssm, { cspellWords, tseslint } from 'eslint-config-cityssm'; +const config = tseslint.config(...eslintConfigCityssm, { + rules: { + '@cspell/spellchecker': [ + 'warn', + { + cspell: { + words: [...cspellWords, 'fasterwebcloud'] + } + } + ] + } +}); +export default config; diff --git a/eslint.config.ts b/eslint.config.ts index a6257c1..24890e2 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -1 +1,20 @@ -export { default } from 'eslint-config-cityssm' +import eslintConfigCityssm, { + type Config, + cspellWords, + tseslint +} from 'eslint-config-cityssm' + +const config = tseslint.config(...eslintConfigCityssm, { + rules: { + '@cspell/spellchecker': [ + 'warn', + { + cspell: { + words: [...cspellWords, 'fasterwebcloud'] + } + } + ] + } +}) as Config + +export default config diff --git a/index.d.ts b/index.d.ts index 03583e8..d515f65 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,4 +1,10 @@ -type FasterBaseUrl = `https://${string}.fasterwebcloud.com/FASTER`; +export type FasterBaseUrl = `https://${string}.fasterwebcloud.com/FASTER`; +/** + * Tests if a base URL is valid. + * @param fasterBaseUrl - A possible FASTER Web base URL. + * @returns `true` if the base URL is valid. + */ +export declare function isValidBaseUrl(fasterBaseUrl: string): fasterBaseUrl is FasterBaseUrl; export declare class FasterUrlBuilder { #private; /** Base URL */ @@ -9,9 +15,10 @@ export declare class FasterUrlBuilder { readonly reportViewerUrl: `${FasterBaseUrl}/Domains/Reports/ReportViewer.aspx`; /** * Initializes the FasterUrlBuilder - * @param fasterTenant - The subdomain of the FASTER Web URL before ".fasterwebcloud.com" + * @param fasterTenantOrBaseUrl - The subdomain of the FASTER Web URL before ".fasterwebcloud.com" + * or the full domain and path including "/FASTER" */ - constructor(fasterTenant: string); + constructor(fasterTenantOrBaseUrl: string); /** * Builds a URL for the inventory search. * @param searchString - Item number search string diff --git a/index.js b/index.js index 97c855f..8b813f7 100644 --- a/index.js +++ b/index.js @@ -1,23 +1,39 @@ +/** + * Tests if a base URL is valid. + * @param fasterBaseUrl - A possible FASTER Web base URL. + * @returns `true` if the base URL is valid. + */ +export function isValidBaseUrl(fasterBaseUrl) { + return (fasterBaseUrl.startsWith('https://') && fasterBaseUrl.endsWith('/FASTER')); +} export class FasterUrlBuilder { /** Base URL */ baseUrl; /** Login URL */ loginUrl; #inventorySearchUrl; + // eslint-disable-next-line no-secrets/no-secrets #workOrderSearchUrl; #workOrderUrl; /** Report Viewer URL - Parameters required */ reportViewerUrl; /** * Initializes the FasterUrlBuilder - * @param fasterTenant - The subdomain of the FASTER Web URL before ".fasterwebcloud.com" + * @param fasterTenantOrBaseUrl - The subdomain of the FASTER Web URL before ".fasterwebcloud.com" + * or the full domain and path including "/FASTER" */ - constructor(fasterTenant) { - this.baseUrl = `https://${fasterTenant}.fasterwebcloud.com/FASTER`; + constructor(fasterTenantOrBaseUrl) { + this.baseUrl = fasterTenantOrBaseUrl.startsWith('https') + ? fasterTenantOrBaseUrl + : `https://${fasterTenantOrBaseUrl}.fasterwebcloud.com/FASTER`; + if (!isValidBaseUrl(this.baseUrl)) { + throw new Error(`Invalid base URL: ${this.baseUrl}`); + } this.loginUrl = `${this.baseUrl}/Login`; /* Inventory */ this.#inventorySearchUrl = `${this.baseUrl}/Domains/Parts/Search/Default.aspx?xact=False&type=False&str=`; /* Maintenance */ + // eslint-disable-next-line no-secrets/no-secrets this.#workOrderSearchUrl = `${this.baseUrl}/Domains/Maintenance/WorkOrder/Search/Default.aspx?xact=False&type=False&str=`; this.#workOrderUrl = `${this.baseUrl}/Domains/Maintenance/WorkOrder/WorkOrderMaster.aspx?workOrderID=`; /* Reports */ diff --git a/index.ts b/index.ts index c0c33e0..bb8f220 100644 --- a/index.ts +++ b/index.ts @@ -1,4 +1,17 @@ -type FasterBaseUrl = `https://${string}.fasterwebcloud.com/FASTER` +export type FasterBaseUrl = `https://${string}.fasterwebcloud.com/FASTER` + +/** + * Tests if a base URL is valid. + * @param fasterBaseUrl - A possible FASTER Web base URL. + * @returns `true` if the base URL is valid. + */ +export function isValidBaseUrl( + fasterBaseUrl: string +): fasterBaseUrl is FasterBaseUrl { + return ( + fasterBaseUrl.startsWith('https://') && fasterBaseUrl.endsWith('/FASTER') + ) +} export class FasterUrlBuilder { /** Base URL */ @@ -9,6 +22,7 @@ export class FasterUrlBuilder { readonly #inventorySearchUrl: `${FasterBaseUrl}/Domains/Parts/Search/Default.aspx?xact=False&type=False&str=` + // eslint-disable-next-line no-secrets/no-secrets readonly #workOrderSearchUrl: `${FasterBaseUrl}/Domains/Maintenance/WorkOrder/Search/Default.aspx?xact=False&type=False&str=` readonly #workOrderUrl: `${FasterBaseUrl}/Domains/Maintenance/WorkOrder/WorkOrderMaster.aspx?workOrderID=` @@ -17,16 +31,25 @@ export class FasterUrlBuilder { /** * Initializes the FasterUrlBuilder - * @param fasterTenant - The subdomain of the FASTER Web URL before ".fasterwebcloud.com" + * @param fasterTenantOrBaseUrl - The subdomain of the FASTER Web URL before ".fasterwebcloud.com" + * or the full domain and path including "/FASTER" */ - constructor(fasterTenant: string) { - this.baseUrl = `https://${fasterTenant}.fasterwebcloud.com/FASTER` + constructor(fasterTenantOrBaseUrl: string) { + this.baseUrl = fasterTenantOrBaseUrl.startsWith('https') + ? (fasterTenantOrBaseUrl as FasterBaseUrl) + : `https://${fasterTenantOrBaseUrl}.fasterwebcloud.com/FASTER` + + if (!isValidBaseUrl(this.baseUrl)) { + throw new Error(`Invalid base URL: ${this.baseUrl as string}`) + } + this.loginUrl = `${this.baseUrl}/Login` /* Inventory */ this.#inventorySearchUrl = `${this.baseUrl}/Domains/Parts/Search/Default.aspx?xact=False&type=False&str=` /* Maintenance */ + // eslint-disable-next-line no-secrets/no-secrets this.#workOrderSearchUrl = `${this.baseUrl}/Domains/Maintenance/WorkOrder/Search/Default.aspx?xact=False&type=False&str=` this.#workOrderUrl = `${this.baseUrl}/Domains/Maintenance/WorkOrder/WorkOrderMaster.aspx?workOrderID=` diff --git a/package-lock.json b/package-lock.json index f76c543..43c3246 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@cityssm/faster-url-builder", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cityssm/faster-url-builder", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "devDependencies": { - "@types/node": "^22.9.0", + "@types/node": "^22.9.1", "eslint-config-cityssm": "^15.2.0", "prettier-config-cityssm": "^1.0.0" } @@ -3092,9 +3092,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", - "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "version": "22.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.1.tgz", + "integrity": "sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index f8ea9e8..22b479d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@cityssm/faster-url-builder", "type": "module", - "version": "0.1.0", + "version": "0.2.0", "description": "Builds URLs for the FASTER Web Fleet Management System", "exports": "./index.js", "scripts": { @@ -22,7 +22,7 @@ }, "homepage": "https://github.com/cityssm/node-faster-url-builder#readme", "devDependencies": { - "@types/node": "^22.9.0", + "@types/node": "^22.9.1", "eslint-config-cityssm": "^15.2.0", "prettier-config-cityssm": "^1.0.0" } diff --git a/test/test.js b/test/test.js index 9f97bd6..1d67b5e 100644 --- a/test/test.js +++ b/test/test.js @@ -1,10 +1,15 @@ import assert from 'node:assert'; import { describe, it } from 'node:test'; -import { FasterUrlBuilder } from '../index.js'; +import { FasterUrlBuilder, isValidBaseUrl } from '../index.js'; await describe('faster-url-builder', async () => { const tenant = 'faster-tenant'; const fasterUrlBuilder = new FasterUrlBuilder(tenant); - await it('Constructs a proper base URL', () => { + await it('Initializes with a full base URL', () => { + const testBaseUrl = 'https://test.example.com/FASTER'; + const fasterUrlBuilderFromUrl = new FasterUrlBuilder(testBaseUrl); + assert.strictEqual(fasterUrlBuilderFromUrl.baseUrl, testBaseUrl); + }); + await it('Constructs a proper base URL from a tenant', () => { assert(fasterUrlBuilder.baseUrl.includes(tenant)); }); await it('Constructs a proper login URL', () => { @@ -32,3 +37,22 @@ await describe('faster-url-builder', async () => { assert(workOrderUrl.endsWith(workOrderNumber.toString())); }); }); +await describe('faster-url-builder/errors', async () => { + await it('Rejects invalid base URLs', () => { + // http link + assert.strictEqual(isValidBaseUrl('http://test.example.com/FASTER'), false); + // missing "/FASTER" + assert.strictEqual(isValidBaseUrl('https://test.example.com'), false); + let declaredSuccessfully = false; + // eslint-disable-next-line @typescript-eslint/init-declarations + let builder; + try { + builder = new FasterUrlBuilder('https://test.example.com/FASTE'); + declaredSuccessfully = true; + } + catch { } + if (declaredSuccessfully) { + assert.fail(`URL builder declared successfully with invalid URL: ${builder.baseUrl}`); + } + }); +}); diff --git a/test/test.ts b/test/test.ts index bd6b7de..15371bf 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,13 +1,19 @@ import assert from 'node:assert' import { describe, it } from 'node:test' -import { FasterUrlBuilder } from '../index.js' +import { FasterUrlBuilder, isValidBaseUrl } from '../index.js' await describe('faster-url-builder', async () => { const tenant = 'faster-tenant' const fasterUrlBuilder = new FasterUrlBuilder(tenant) - await it('Constructs a proper base URL', () => { + await it('Initializes with a full base URL', () => { + const testBaseUrl = 'https://test.example.com/FASTER' + const fasterUrlBuilderFromUrl = new FasterUrlBuilder(testBaseUrl) + assert.strictEqual(fasterUrlBuilderFromUrl.baseUrl, testBaseUrl) + }) + + await it('Constructs a proper base URL from a tenant', () => { assert(fasterUrlBuilder.baseUrl.includes(tenant)) }) @@ -51,3 +57,28 @@ await describe('faster-url-builder', async () => { assert(workOrderUrl.endsWith(workOrderNumber.toString())) }) }) + +await describe('faster-url-builder/errors', async () => { + await it('Rejects invalid base URLs', () => { + // http link + assert.strictEqual(isValidBaseUrl('http://test.example.com/FASTER'), false) + + // missing "/FASTER" + assert.strictEqual(isValidBaseUrl('https://test.example.com'), false) + + let declaredSuccessfully = false + + // eslint-disable-next-line @typescript-eslint/init-declarations + let builder: FasterUrlBuilder | undefined + + try { + builder = new FasterUrlBuilder('https://test.example.com/FASTE') + declaredSuccessfully = true + } catch {} + if (declaredSuccessfully) { + assert.fail( + `URL builder declared successfully with invalid URL: ${(builder as FasterUrlBuilder).baseUrl}` + ) + } + }) +})