libs/ngx-pfe/pfe-service-activator/url-builder/url-builder.service.ts
Methods |
|
| Public getURL | ||||||||||||
getURL(path?: string, pathParams?: PathParams[])
|
||||||||||||
|
Builds a complete URL by combining the base endpoint with a path and substituting path parameters. This method handles placeholder extraction, parameter filtering, validation, and substitution.
Parameters :
Example :
Returns :
string
The complete URL with all placeholders replaced by their corresponding values |
import { inject, Injectable } from '@angular/core';
import { PathParams, PfeServiceActivatorParamsService } from '../params';
import { PfeModuleConfigurationService } from '../../services/pfe-module-configuration/pfe-module-configuration.service';
@Injectable()
export class PfeServiceActivatorUrlBuilderService {
private paramsService = inject(PfeServiceActivatorParamsService);
private pfeModuleConfigurationService = inject(PfeModuleConfigurationService);
private SERVICE_ACTIVATOR_PLACEHOLDER_REG_EX = new RegExp(`{.+?}`, 'g');
/**
* Builds a complete URL by combining the base endpoint with a path and substituting path parameters.
* This method handles placeholder extraction, parameter filtering, validation, and substitution.
*
* @param path - The API path to append to the base endpoint (e.g., 'contract/{contractId}/policy-holder')
* @param pathParams - Optional array of path parameter configurations to substitute in the URL
* @returns The complete URL with all placeholders replaced by their corresponding values
* @throws Error if path parameters are invalid (undefined or non-primitive) or if any placeholders remain unreplaced
*
* @example
* const pathParams = [{ id: 'contractId', value: '$.contract.id' }];
* const url = getURL('contract/{contractId}/policy-holder', pathParams);
* // Returns: 'https://api.example.com/contract/12345/policy-holder'
*/
public getURL(path?: string, pathParams?: PathParams[]): string {
// Validate the initial URL
this.validatePath(path);
// Get the url
const baseUrl = this.getBaseUrl(path);
// Get the path params
const params = this.paramsService.getPathParams(pathParams);
// Make the substitution from the placeholder with the params
const finalUrl = params ? this.substituteUrlPlaceholders(baseUrl, params) : baseUrl;
// Validate the URL to check if one of the params is not replaced, and throw an error
this.validateFinalUrl(finalUrl);
return finalUrl;
}
/**
* Replaces URL placeholders with their corresponding parameter values.
* Each placeholder {paramName} is replaced with the URL-encoded parameter value.
*
* @param url - URL containing placeholders in the format {paramName}
* @param params - Object containing parameter names and their values to substitute
* @returns URL with all available placeholders replaced by URL-encoded parameter values
* @private
*
* @example
* const url = '/api/contract/{contractId}/policy/{policyId}';
* const params = { contractId: 'CONTRACT_ID', policyId: 'POLICY_ID' };
* substituteUrlPlaceholders(url, params);
* // Returns: '/api/contract/CONTRACT_ID/policy/POLICY_ID'
*/
private substituteUrlPlaceholders(url: string, params: { [x: string]: unknown }): string {
let finalUrl = url;
Object.keys(params).forEach((paramName) => {
const placeholder = new RegExp(`\\{${paramName}\\}`, 'g');
const value = encodeURIComponent(params[paramName] as string | number | boolean);
finalUrl = finalUrl.replace(placeholder, value);
});
return finalUrl;
}
/**
* Validates the final URL to ensure all placeholders have been successfully replaced.
* This check prevents malformed URLs from being used in HTTP requests.
*
* @param url - The final URL after placeholder substitution
* @throws Error if any unreplaced placeholders in format {paramName} are found
* @private
*
* @example
* validateFinalUrl('/api/contract/CONTRACT_ID/policy/POLICY_ID'); // Valid - no error
* validateFinalUrl('/api/contract/{contractId}/policy/POLICY_ID'); // Throws error - unreplaced placeholder
*/
private validateFinalUrl(url: string): void {
const unreplacedPlaceholders = url.match(this.SERVICE_ACTIVATOR_PLACEHOLDER_REG_EX);
if (unreplacedPlaceholders && unreplacedPlaceholders.length > 0) {
throw new Error(
`URL contains unreplaced placeholders: ${JSON.stringify(unreplacedPlaceholders)}. ` +
`This indicates that some required path parameters were not properly substituted.`
);
}
}
/**
* Get the base URL.
* It will check if the provided URL is a full url (starting with http / https) if that is the case will return the path as provided
* Else will add the service activator endpoint and path.
*
* @param path The path as comes from the config
* @private
*/
private getBaseUrl(path: string): string {
const httpsRegExp = new RegExp(/^(http:|https:|\/\/)/, 'i');
return httpsRegExp.test(path) ? path : `${this.pfeModuleConfigurationService.pfeModuleConfig().serviceActivatorEndpoint}/${path}`;
}
/**
* Validates the provided path.
* Check if the URL is properly defined
*
* @param path - The path as comes from the config
* @private
*/
private validatePath(path?: string): asserts path is string {
// Check if the URL is present or falsy. Like an empty string
if (!path) {
throw new Error('The service activator path was not present');
}
}
}