Quick Start

How to add a new formula

In this section, we will take the example of adding a new formula - IS_ODD, and learn how to quickly add a custom formula.

The main function of the IS_ODD formula is to identify whether the specified number is an odd number.

Add IS_ODD formula class

Since the formula IS_ODD returns a Boolean type, you can inherit directly from the base class LogicalFunc and implement the basic methods of the formula, as follows:

// FilePath: "apitable/packages/core/src/formula_parser/functions/logical.ts"
export class IsOdd extends LogicalFunc {
  static override acceptValueType = new Set([BasicValueType.Number]);

  static override validateParams(params: AstNode[]) {
    if (params.length < 1) {
      throw new ParamsCountError(ParamsErrorType.AtLeastCount, 'IS_ODD', 1);
    }
  }

  static override getReturnType(params?: AstNode[]) {
    params && this.validateParams(params);
    return BasicValueType.Boolean;
  }

  static override func(params: [IFormulaParam<number>]): boolean | null {
    const [{ value: num }] = params;
    if (num == null || typeof num !== 'number') {
      return null;
    }
    return num % 2 !== 0;
  }
}

Add IS_ODD formula definition

The list of formulas on the front and back ends will be read according to the following list:

// FilePath: "apitable/packages/core/src/formula_parser/functions/index.ts"
export const Functions = new Map<string, IFunction>([
  // ...
  ['IS_ODD', {
    name: 'IS_ODD',
    func: logical.IsOdd,
    definition: 'IS_ODD(number)',
    summary: t(Strings.function_isodd_summary), // 新增 function_isodd_summary key
    example: t(Strings.function_isodd_example), // 新增 function_isodd_example key
  }],
])

Add unit test for IS_ODD formula

To ensure that formula-related changes do not affect the results of the formula runs, unit tests need to be written for each formula, as follows:

// FilePath: "apitable/packages/core/src/formula_parser/__tests__/logical.test.ts"
it('IS_ODD', () => {
    expect(evaluate(
      'IS_ODD({a})',
      mergeContext({ a: 1, b: 2, c: 1591414562369, d: ['opt1'] }),
    )).toEqual(true);
    
    expect(evaluate(
      'IS_ODD({b})',
      mergeContext({ a: 1, b: 2, c: 1591414562369, d: ['opt1'] }),
    )).toEqual(false);
    
    expect(evaluate(
      'IS_ODD({a})',
      mergeContext({ a: 'abc', b: 1, c: 1591414562369, d: ['opt1'] }),
    )).toEqual(null);
    
    expect(evaluate(
      'IS_ODD({a}, {b})',
      mergeContext({ a: -2, b: 1, c: 1591414562369, d: ['opt1'] }),
    )).toEqual(true);

    expect(() => evaluate(
      'IS_ODD()',
      mergeContext({ a: 1, b: 2, c: 1591414562369, d: ['opt1'] }),
    )).toThrow(ParamsCountError);
});