Repository: novuhq/novu
File: apps/api/src/app/workflows-v2/util/template-parser/liquid-parser.ts (TypeScript)

Code before:
        const token = template?.token;
        invalidVariables.push({
          name: token?.input,
          filterMessage: filterIssues[0].message,
          output: rawOutput,
        });

        return;
      }

      const isValidVariable = result.valid && result.props.length > 0;
      if (isValidVariable) {
        const isAllowedVariable = isPropertyAllowed(variableSchema, variableName);
        if (isAllowedVariable) {
          validVariables.push({ name: variableName, output: rawOutput });
          if (filters.length > 0) {
            filters.forEach((filter) => {
              const { args } = filter;
              const firstArg = args[0];
              if (
                filter.name === 'toSentence' &&
                args.length > 0 &&
                'content' in firstArg &&
                typeof firstArg.content === 'string' &&
                firstArg.content.startsWith('payload.')
              ) {
                const isFirstArgValid = isPropertyAllowed(variableSchema, firstArg.content);
                if (isFirstArgValid) {
                  validVariables.push({
                    name: `${variableName}.${firstArg.content}`,
                    output: `{{${firstArg.content}}}`,
                  });
                }
              }
            });
          }
        } else {
          invalidVariables.push({
            name: variableName,
            message: 'is not supported',
            output: rawOutput,
          });
        }
      }

      if (!result.valid) {
        const token = template?.token;
        invalidVariables.push({
          name: token?.input,
          message: result.error,
          output: rawOutput,

Review comment (suggestion):
also only when the variable name matches the `steps.x.events`, otherwise we should not treat it as the variable

Code after:
      const isDigestEventsVariable = !!variableName.match(/^steps\..+\.events$/);
      const filters = extractFilters(template);
      const filterIssues = validateFilters(filters, isDigestEventsVariable);
      const hasValidFilters = filterIssues.length === 0;

      if (!hasValidFilters) {
        const token = template?.token;
        invalidVariables.push({
          name: token?.input,
          filterMessage: filterIssues[0].message,
          output: rawOutput,
        });

        return;
      }

      const isValidVariable = result.valid && result.props.length > 0;
      if (isValidVariable) {
        const isAllowedVariable = isPropertyAllowed(variableSchema, variableName);
        if (isAllowedVariable) {
          validVariables.push({ name: variableName, output: rawOutput });
          if (filters.length > 0) {
            filters.forEach((filter) => {
              const { args } = filter;
              const firstArg = args[0];
              if (
                filter.name === 'toSentence' &&
                args.length > 0 &&
                'content' in firstArg &&
                typeof firstArg.content === 'string'
              ) {
                /**
                 * Check if the parent variable with the first argument is allowed
                 * basically forcing it to check if additionalProperties is true by checking for final variable name
                 * and if the parent variable is a valid dynamic path as variableSchema can be undefined.
                 * OR
                 * Check if the variable is a digest events array variable
                 * and the first argument starts with payload.
                 */
                if (
                  (isValidDynamicPath(variableName) &&
                    isPropertyAllowed(variableSchema, `${variableName}.${firstArg.content}`)) ||
                  (firstArg.content.startsWith('payload.') && DIGEST_EVENTS_VARIABLE_PATTERN.test(variableName))
                ) {
                  const isFirstArgValid = isPropertyAllowed(variableSchema, firstArg.content);
                  if (isFirstArgValid) {
                    validVariables.push({
                      name: `${variableName}.${firstArg.content}`,
                      output: `{{${firstArg.content}}}`,
                    });
                  }