File

libs/nx/src/executors/shared/df-editor-server/update-dynamic-form-config.ts

Index

Properties

Properties

baseConfigDirectory (Optional)
Type string
configDirectory
Type string
import type {
  BuildingBlockConfiguration,
  DynamicFormBBConfiguration,
  DynamicFormConfiguration,
  PageConfiguration
} from '@allianz/taly-core/schemas';
import type { PagesJsonSchema } from '@allianz/taly-sdk';
import {
  getJsonFilePathInDirectory,
  getJsonFilePathsInDirectory,
  SPLIT_JOURNEY_PATHS
} from '@allianz/taly-sdk/node';
import { parse, stringify } from 'comment-json';
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';

interface DfEditRequest {
  pageId: string;
  buildingBlockId: string;
  config: DynamicFormConfiguration;
}

interface ConfigDirectories {
  configDirectory: string;
  baseConfigDirectory?: string;
}

/**
 * Updates the dynamic form configuration for a building block identified by `id`
 * directly in the source configuration file(s) on disk.
 *
 * Supports classic journeys (pages.json), split journeys (pages/*.json),
 * and multi-tenant journeys (tenant pages + base pages fallback).
 *
 * Uses `comment-json` to preserve JSONC comments when writing back.
 */
export function updateDynamicFormConfig(
  { configDirectory, baseConfigDirectory }: ConfigDirectories,
  { pageId, buildingBlockId, config }: DfEditRequest
): void {
  // For multi-tenant: try tenant first, then fall back to base
  if (baseConfigDirectory) {
    const updatedInTenant = tryUpdateInDirectory(configDirectory, pageId, buildingBlockId, config);
    if (updatedInTenant) return;

    const updatedInBase = tryUpdateInDirectory(
      baseConfigDirectory,
      pageId,
      buildingBlockId,
      config
    );
    if (updatedInBase) return;

    throw new Error(
      `Building block "${buildingBlockId}" not found in page "${pageId}" in tenant config "${configDirectory}" or base config "${baseConfigDirectory}".`
    );
  }

  const updated = tryUpdateInDirectory(configDirectory, pageId, buildingBlockId, config);
  if (updated) return;

  throw new Error(
    `Building block "${buildingBlockId}" not found in page "${pageId}" in config directory "${configDirectory}".`
  );
}

/**
 * Tries to find and update the building block in all page config files within a directory.
 * Returns true if the building block was found and updated.
 */
function tryUpdateInDirectory(
  configDirectory: string,
  pageId: string,
  blockId: string,
  newConfig: DynamicFormConfiguration
): boolean {
  // Classic journey: single pages.json(c) at the root
  const pagesFilePath = getJsonFilePathInDirectory(configDirectory, 'pages');
  if (pagesFilePath) {
    return updateBlockInClassicPagesFile(pagesFilePath, pageId, blockId, newConfig);
  }

  // Split journey: individual page files in pages/ subdirectory
  const pagesDir = join(configDirectory, SPLIT_JOURNEY_PATHS.FOLDERS.PAGES);
  const pageFiles = getJsonFilePathsInDirectory(pagesDir);
  for (const pageFile of pageFiles) {
    if (updateBlockInSplitPageFile(pageFile, pageId, blockId, newConfig)) {
      return true;
    }
  }

  return false;
}

/**
 * Updates a dynamic form block in a classic pages.json(c) file.
 * The file has a top-level `pages` array, each with a `blocks` array.
 */
function updateBlockInClassicPagesFile(
  filePath: string,
  pageId: string,
  blockId: string,
  newConfig: DynamicFormConfiguration
): boolean {
  const content = readAndParse<PagesJsonSchema>(filePath);
  if (!content.pages) return false;

  const page = content.pages.find((p) => p.id === pageId);
  if (!page) return false;

  if (updateBlockInBlocksArray(page.blocks, blockId, newConfig)) {
    writeBack(filePath, content);
    return true;
  }

  return false;
}

/**
 * Updates a dynamic form block in a split page file.
 * Each file is a single page with a top-level `blocks` array.
 */
function updateBlockInSplitPageFile(
  filePath: string,
  pageId: string,
  blockId: string,
  newConfig: DynamicFormConfiguration
): boolean {
  const content = readAndParse<PageConfiguration>(filePath);
  if (!content.blocks || content.id !== pageId) return false;

  if (updateBlockInBlocksArray(content.blocks, blockId, newConfig)) {
    writeBack(filePath, content);
    return true;
  }

  return false;
}

/**
 * Searches for a block by id in the blocks array and updates its `form` property.
 * Only updates blocks that have a `form` property (dynamic form building blocks).
 * Returns true if a matching block was found and updated.
 */
function updateBlockInBlocksArray(
  blocks: (BuildingBlockConfiguration | DynamicFormBBConfiguration)[] | undefined,
  blockId: string,
  newConfig: DynamicFormConfiguration
): boolean {
  if (!blocks) return false;

  const block = blocks.find((b) => b.id === blockId);
  if (!block || !isDynamicFormBbConfig(block)) return false;

  block.form = newConfig;
  return true;
}

function isDynamicFormBbConfig(
  block: BuildingBlockConfiguration | DynamicFormBBConfiguration
): block is DynamicFormBBConfiguration {
  return Boolean((block as DynamicFormBBConfiguration).form);
}

function readAndParse<T>(filePath: string): T {
  const raw = readFileSync(filePath, 'utf8');
  return parse(raw) as T;
}

function writeBack(filePath: string, content: unknown): void {
  writeFileSync(filePath, stringify(content, null, 2) + '\n', 'utf8');
}

results matching ""

    No results matching ""