payments/src/lib/actions/publish.ts

import { Payment, PaymentProcessingState } from '../types';
import { tracer } from '@capchase/tracer';
import {
  publish,
  Channel,
  InternalPaymentAdded,
  InternalPaymentAddedBody,
} from '@capchase/messaging-system';
import { callLog } from '../call_log';

/**
 * Computes the name of the topic based on the contract name.
 *
 * @function
 * @memberOf module:payments/actions
 * @access private
 */
const channelNameForContract = (contractName: string): string => {
  if (contractName.length === 0) {
    throw new Error('Contract name should not be empty');
  }

  return `${Channel.INTERNAL_PAYMENT_ADDED}.contract.${contractName}`;
};

const internalPublishPaymentAdded = async (
  payment: Payment,
  actionBy: string
) => {
  await callLog({
    callName: 'publishPayment',
    actionBy,
    args: [payment, actionBy],
  });

  const span = tracer?.scope()?.active();

  span?.addTags({
    payment_id: payment.id,
    payment_key: payment.key,
    payment_processing_key: payment.processingKey,
    payment_processing_state: payment.processingState,
    action_by: actionBy,
  });

  // TODO(SHI-1454): Validate actual user permissions
  if (actionBy.length === 0) {
    throw new Error('Action by should be set');
  }

  // If the payment is already processed, do nothing
  if (payment.processingState === PaymentProcessingState.PROCESSED) {
    return;
  }

  const body: InternalPaymentAddedBody = {
    paymentKey: payment.key,
    processingKey: payment.processingKey,
    contractName: payment.contractName,
    status: payment.status,
  };

  // Send both to the general channel and the status specific channel
  await Promise.all([
    publish<InternalPaymentAdded>(Channel.INTERNAL_PAYMENT_ADDED, body),
    publish<InternalPaymentAdded>(
      channelNameForContract(payment.contractName),
      body
    ),
  ]);
};

/**
 * Publishes that a non processed payment has been added.
 *
 * @param {Payment} payment Payment ot be published
 * @param {string} actionBy Action executor
 * @return {Promise<void>}
 *
 * @async
 * @function
 * @memberOf module:payments/actions
 * @access public
 */
const publishPaymentAdded =
  tracer?.wrap('payments.publishPaymentAdded', internalPublishPaymentAdded) ||
  internalPublishPaymentAdded;

export { publishPaymentAdded, channelNameForContract };