1
#include "source/common/formatter/coalesce_formatter.h"
2

            
3
#include "source/common/common/fmt.h"
4
#include "source/common/json/json_loader.h"
5

            
6
namespace Envoy {
7
namespace Formatter {
8

            
9
absl::StatusOr<FormatterProviderPtr> CoalesceFormatter::create(absl::string_view json_config,
10
25
                                                               absl::optional<size_t> max_length) {
11
25
  if (json_config.empty()) {
12
    return absl::InvalidArgumentError("COALESCE requires a JSON configuration parameter");
13
  }
14

            
15
25
  auto json_or_error = Json::Factory::loadFromString(std::string(json_config));
16
25
  if (!json_or_error.ok()) {
17
1
    return absl::InvalidArgumentError(fmt::format(
18
1
        "COALESCE: failed to parse JSON configuration: {}", json_or_error.status().message()));
19
1
  }
20

            
21
24
  const auto& json = *json_or_error.value();
22

            
23
24
  if (!json.hasObject("operators")) {
24
1
    return absl::InvalidArgumentError(
25
1
        "COALESCE: JSON configuration must contain 'operators' array");
26
1
  }
27

            
28
23
  auto operators_or_error = json.getObjectArray("operators");
29
23
  if (!operators_or_error.ok()) {
30
    return absl::InvalidArgumentError(fmt::format("COALESCE: 'operators' must be an array: {}",
31
                                                  operators_or_error.status().message()));
32
  }
33

            
34
23
  const auto& operators = operators_or_error.value();
35
23
  if (operators.empty()) {
36
1
    return absl::InvalidArgumentError("COALESCE: 'operators' array must not be empty");
37
1
  }
38

            
39
22
  std::vector<FormatterProviderPtr> formatters;
40
22
  formatters.reserve(operators.size());
41

            
42
56
  for (size_t i = 0; i < operators.size(); ++i) {
43
38
    const auto& entry = operators[i];
44
38
    auto formatter_or_error = parseOperatorEntry(*entry);
45
38
    if (!formatter_or_error.ok()) {
46
4
      return absl::InvalidArgumentError(
47
4
          fmt::format("COALESCE: failed to parse operator at index {}: {}", i,
48
4
                      formatter_or_error.status().message()));
49
4
    }
50
34
    formatters.push_back(std::move(formatter_or_error.value()));
51
34
  }
52

            
53
18
  return std::make_unique<CoalesceFormatter>(std::move(formatters), max_length);
54
22
}
55

            
56
absl::StatusOr<FormatterProviderPtr>
57
38
CoalesceFormatter::parseOperatorEntry(const Json::Object& entry) {
58
  // Check if this is a simple string command with command-only and no parameters.
59
38
  auto string_value = entry.asString();
60
38
  if (string_value.ok()) {
61
13
    return createFormatterForCommand(string_value.value(), "", absl::nullopt);
62
13
  }
63

            
64
  // Otherwise, it should be an object with "command" field.
65
25
  if (!entry.isObject()) {
66
1
    return absl::InvalidArgumentError(
67
1
        "operator entry must be either a string (command name) or an object with 'command' field");
68
1
  }
69

            
70
24
  auto command_or_error = entry.getString("command");
71
24
  if (!command_or_error.ok()) {
72
1
    return absl::InvalidArgumentError(fmt::format("operator object must have 'command' field: {}",
73
1
                                                  command_or_error.status().message()));
74
1
  }
75

            
76
23
  std::string param;
77
23
  if (entry.hasObject("param")) {
78
22
    auto param_or_error = entry.getString("param");
79
22
    if (!param_or_error.ok()) {
80
      return absl::InvalidArgumentError(
81
          fmt::format("'param' field must be a string: {}", param_or_error.status().message()));
82
    }
83
22
    param = param_or_error.value();
84
22
  }
85

            
86
23
  absl::optional<size_t> entry_max_length;
87
23
  if (entry.hasObject("max_length")) {
88
2
    auto max_length_or_error = entry.getInteger("max_length");
89
2
    if (!max_length_or_error.ok()) {
90
      return absl::InvalidArgumentError(fmt::format("'max_length' field must be an integer: {}",
91
                                                    max_length_or_error.status().message()));
92
    }
93
2
    if (max_length_or_error.value() <= 0) {
94
1
      return absl::InvalidArgumentError("'max_length' must be a positive integer");
95
1
    }
96
1
    entry_max_length = static_cast<size_t>(max_length_or_error.value());
97
1
  }
98

            
99
22
  return createFormatterForCommand(command_or_error.value(), param, entry_max_length);
100
23
}
101

            
102
absl::StatusOr<FormatterProviderPtr>
103
CoalesceFormatter::createFormatterForCommand(absl::string_view command, absl::string_view param,
104
35
                                             absl::optional<size_t> max_length) {
105
  // Try built-in command parsers to create the formatter.
106
58
  for (const auto& parser : BuiltInCommandParserFactoryHelper::commandParsers()) {
107
58
    auto formatter = parser->parse(command, param, max_length);
108
58
    if (formatter != nullptr) {
109
34
      return formatter;
110
34
    }
111
58
  }
112

            
113
1
  return absl::InvalidArgumentError(fmt::format("unknown command: '{}'", command));
114
35
}
115

            
116
absl::optional<std::string>
117
16
CoalesceFormatter::format(const Context& context, const StreamInfo::StreamInfo& stream_info) const {
118
24
  for (const auto& formatter : formatters_) {
119
24
    auto result = formatter->format(context, stream_info);
120
24
    if (result.has_value() && !result.value().empty()) {
121
14
      if (max_length_.has_value()) {
122
2
        SubstitutionFormatUtils::truncate(result.value(), max_length_.value());
123
2
      }
124
14
      return result;
125
14
    }
126
24
  }
127
2
  return absl::nullopt;
128
16
}
129

            
130
Protobuf::Value CoalesceFormatter::formatValue(const Context& context,
131
2
                                               const StreamInfo::StreamInfo& stream_info) const {
132
3
  for (const auto& formatter : formatters_) {
133
3
    auto result = formatter->formatValue(context, stream_info);
134
    // Check if this is a valid non-null value.
135
3
    if (result.kind_case() != Protobuf::Value::KIND_NOT_SET &&
136
3
        result.kind_case() != Protobuf::Value::kNullValue) {
137
      // For string values, also check if empty.
138
1
      if (result.kind_case() == Protobuf::Value::kStringValue) {
139
1
        if (!result.string_value().empty()) {
140
1
          if (max_length_.has_value() && result.string_value().size() > max_length_.value()) {
141
            result.set_string_value(result.string_value().substr(0, max_length_.value()));
142
          }
143
1
          return result;
144
1
        }
145
1
      } else {
146
        return result;
147
      }
148
1
    }
149
3
  }
150
1
  return SubstitutionFormatUtils::unspecifiedValue();
151
2
}
152

            
153
} // namespace Formatter
154
} // namespace Envoy