src/app/routes/utils/constructPageFetchUrl/index.ts

Summary

Maintainability
B
6 hrs
Test Coverage
B
84%
import Url from 'url-parse';
import pipe from 'ramda/src/pipe';
import getEnvironment from '#app/routes/utils/getEnvironment';
import { getMostReadEndpoint } from '#app/lib/utilities/getUrlHelpers/getMostReadUrls';
import { getUrlPath } from '#lib/utilities/urlParser';
import {
  Services,
  Variants,
  Environments,
  PageTypes,
} from '#models/types/global';
import handleError from '../handleError';
import HOME_PAGE_CONFIG from '../../homePage/getInitialData/page-config';
import {
  TOPIC_PAGE_CONFIG,
  TopicPagePaths,
} from '../../topic/getInitialData/page-config';
import {
  ARTICLE_PAGE,
  AV_EMBEDS,
  CPS_ASSET,
  HOME_PAGE,
  LIVE_PAGE,
  MOST_READ_PAGE,
  TOPIC_PAGE,
  UGC_PAGE,
} from '../pageTypes';
import parseAvRoute from '../parseAvRoute';

const removeLeadingSlash = (path: string) => path?.replace(/^\/+/g, '');
const removeAmp = (path: string) => path.split('.')[0];
const getArticleId = (path: string) => path.match(/(c[a-zA-Z0-9]{10,}o)/)?.[1];
const getCpsId = (path: string) => removeLeadingSlash(path);
const getFrontPageId = (path: string) =>
  `${removeLeadingSlash(path)}/front_page`;
const getTipoId = (path: string) => path.match(/(c[a-zA-Z0-9]{10,}t)/)?.[1];
const getUgcId = (path: string) => path.match(/(u[a-zA-Z0-9]{8,})/)?.[1];
const isOptimoIdCheck = (path: string) =>
  /\/(articles|sgeulachdan|erthyglau)\/(c[a-zA-Z0-9]{10,}o)/.test(path);
const isCpsIdCheck = (path: string) =>
  /([0-9]{5,9}|[a-z0-9\-_]+-[0-9]{5,9})$/.test(path);
const isTipoIdCheck = (path: string) => /(c[a-zA-Z0-9]{10,}t)/.test(path);

const isFrontPage = ({
  path,
  service,
  variant,
}: {
  path: string;
  service: Services;
  variant?: Variants;
}) => (variant ? path === `/${service}/${variant}` : path === `/${service}`);

interface GetIdProps {
  pageType: PageTypes;
  service?: Services;
  variant?: Variants;
  env: Environments;
}

const getId = ({ pageType, service, variant, env }: GetIdProps) => {
  let getIdFunction;

  switch (pageType) {
    case ARTICLE_PAGE:
      getIdFunction = (path: string) => {
        const isOptimoId = isOptimoIdCheck(path);
        const isCpsId = isCpsIdCheck(path);

        if (isOptimoId) return getArticleId(path);
        if (isCpsId) return getCpsId(path);

        return removeLeadingSlash(path);
      };
      break;
    case CPS_ASSET:
      getIdFunction = (path: string) => {
        /**
         * Legacy Front Pages are curated in CPS and fetched from the BFF using the CPS_ASSET page type
         * This functionality will be removed once all front pages migrated to the new HomePage
         *  */
        return env !== 'local' &&
          service &&
          isFrontPage({ path, service, variant })
          ? getFrontPageId(path)
          : getCpsId(path);
      };
      break;
    case HOME_PAGE:
      getIdFunction = () => {
        return env !== 'local' && service
          ? HOME_PAGE_CONFIG?.[service]?.[env]
          : 'tipohome';
      };
      break;
    case MOST_READ_PAGE:
      getIdFunction = () => pageType;
      break;
    case LIVE_PAGE:
      getIdFunction = (path: string) => {
        if (isTipoIdCheck(path)) {
          return getTipoId(path);
        }
        if (isCpsIdCheck(path)) {
          return `/${service}${variant ? `/${variant}` : ''}/live/${getCpsId(path)}`;
        }
        return null;
      };
      break;

    case TOPIC_PAGE:
      getIdFunction = (path: string) => {
        return (
          TOPIC_PAGE_CONFIG?.[path as TopicPagePaths]?.[env] || getTipoId(path)
        );
      };
      break;
    case UGC_PAGE:
      getIdFunction = getUgcId;
      break;
    case AV_EMBEDS:
      getIdFunction = (path: string) => {
        const parsedRoute = parseAvRoute(path);

        const isShortCpsId = parsedRoute?.assetId?.length === 8;

        const withServiceAndVariant = !isShortCpsId
          ? `${parsedRoute.service ?? ''}${parsedRoute.variant ? `/${parsedRoute.variant}` : ''}`
          : '';

        const id = `${withServiceAndVariant}/${parsedRoute.assetId}`;

        return id;
      };
      break;
    default:
      getIdFunction = () => null;
      break;
  }
  return pipe(getUrlPath, removeAmp, getIdFunction);
};

export interface UrlConstructParams {
  pathname: string;
  pageType: PageTypes;
  service?: Services;
  variant?: Variants;
  page?: string;
  isAmp?: boolean;
  mediaId?: string | null;
  lang?: string | null;
}

const constructPageFetchUrl = ({
  pathname,
  pageType,
  service,
  variant,
  page,
  isAmp,
  mediaId,
  lang,
}: UrlConstructParams) => {
  const env = getEnvironment(pathname);
  const isLocal = !env || env === 'local';
  const id = getId({ pageType, service, env, variant })(pathname);
  const capitalisedPageType =
    pageType.charAt(0).toUpperCase() + pageType.slice(1);

  if (!id) throw handleError(`${capitalisedPageType} ID is invalid`, 500);

  const queryParameters = {
    id,
    ...(service && {
      service,
    }),
    pageType,
    ...(variant && {
      variant,
    }),
    ...(page && {
      page,
    }),
    ...(isAmp && {
      isAmp,
    }),
    // MediaId can be supplied by av-embeds routes to determine which media asset to return
    ...(mediaId && {
      mediaId,
    }),
    ...(lang && {
      lang,
    }),
    ...(env && { serviceEnv: env }),
  };

  let fetchUrl = Url(process.env.BFF_PATH as string).set(
    'query',
    queryParameters,
  );

  if (isLocal) {
    switch (pageType) {
      case ARTICLE_PAGE: {
        const isOptimoId = isOptimoIdCheck(`/articles/${id}`);

        fetchUrl = Url(
          isOptimoId
            ? `/${service}/articles/${id}${variant ? `/${variant}` : ''}`
            : `/${id}`,
        );

        break;
      }
      case CPS_ASSET:
        fetchUrl = Url(`/${id}`);
        break;
      case HOME_PAGE:
        fetchUrl = Url(`/${service}/${id}`);
        break;
      case MOST_READ_PAGE:
        fetchUrl = Url(getMostReadEndpoint({ service, variant }).split('.')[0]);
        break;
      case TOPIC_PAGE: {
        const variantPath = variant ? `/${variant}` : '';
        fetchUrl = Url(`/${service}${variantPath}/topics/${id}`);
        break;
      }
      case LIVE_PAGE: {
        const variantPath = variant ? `/${variant}` : '';
        const host = `http://${process.env.HOSTNAME || 'localhost'}`;
        const port = process.env.PORT ? `:${process.env.PORT}` : '';
        // pathname is the ID of the Live page without /service/live/, and supports both Tipo & CPS IDs
        fetchUrl = Url(
          `${host}${port}/api/local/${service}/live/${pathname}${variantPath}`,
        );
        break;
      }
      case UGC_PAGE: {
        const host = `http://${process.env.HOSTNAME || 'localhost'}`;
        const port = process.env.PORT ? `:${process.env.PORT}` : '';
        fetchUrl = Url(`${host}${port}/api/local/${service}/send/${id}`);
        break;
      }
      case AV_EMBEDS: {
        const parsedRoute = parseAvRoute(pathname);

        const host = `http://${process.env.HOSTNAME || 'localhost'}`;
        const port = process.env.PORT ? `:${process.env.PORT}` : '';

        if (parsedRoute.isWsRoute) {
          // handle /ws/av-embeds route
        } else {
          fetchUrl = Url(
            `${host}${port}/api/local/${parsedRoute.service}/av-embeds/${parsedRoute.variant ? `${parsedRoute?.variant}/` : ''}${parsedRoute.assetId}${parsedRoute.mediaId ? `/${parsedRoute.mediaDelimiter}/${parsedRoute.mediaId}` : ''} ${parsedRoute.lang ? `/${parsedRoute.lang}` : ''}`,
          );
        }

        break;
      }
      default:
        return fetchUrl;
    }
  }

  return fetchUrl;
};

export default constructPageFetchUrl;