File

libs/ngx-pfe/pfe-service-activator/url-builder/url-builder.service.ts

Index

Methods

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.

const pathParams = [{ id: 'contractId', value: '$.contract.id' }];
const url = getURL('contract/{contractId}/policy-holder', pathParams);
// Returns: 'https://api.example.com/contract/12345/policy-holder'
Parameters :
Name Type Optional Description
path string Yes
  • The API path to append to the base endpoint (e.g., 'contract/{contractId}/policy-holder')
pathParams PathParams[] Yes
  • Optional array of path parameter configurations to substitute in the URL
Example :
const pathParams = [{ id: 'contractId', value: '$.contract.id' }];
const url = getURL('contract/{contractId}/policy-holder', pathParams);
// Returns: &#39;<a href="https://api.example.com/contract/12345/policy-holder">https://api.example.com/contract/12345/policy-holder</a>&#39;
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');
    }
  }
}

results matching ""

    No results matching ""