public static void mergeOutputDataParams(
      Map<String, Parameter> allParams, Map<String, Parameter> params) {
    params.forEach(
        (name, param) -> {
          if (!allParams.containsKey(name)) {
            throw new MaestroValidationException(
                "Invalid output parameter [%s], not defined in params", name);
          }
          MergeContext context = MergeContext.stepCreate(ParamSource.OUTPUT_PARAMETER);
          if (param.getType() == ParamType.MAP && param.isLiteral()) {
            ParamDefinition baseDef = allParams.get(name).toDefinition();
            Map<String, ParamDefinition> baseMap = baseDef.asMapParamDef().getValue();
            ParamDefinition toMergeDef = param.toDefinition();
            Map<String, ParamDefinition> toMergeMap = toMergeDef.asMapParamDef().getValue();

            mergeParams(baseMap, toMergeMap, context);
            Parameter mergedParam =
                buildMergedParamDefinition(name, toMergeDef, baseDef, context, baseMap)
                    .toParameter();
            populateEvaluatedResultAndTime(mergedParam, param.getEvaluatedTime());
            allParams.put(name, mergedParam);
          } else if (param.getType() == ParamType.STRING_MAP && param.isLiteral()) {
            ParamDefinition baseDef = allParams.get(name).toDefinition();
            Map<String, String> baseMap = baseDef.asStringMapParamDef().getValue();
            ParamDefinition toMergeDef = param.toDefinition();
            Map<String, String> toMergeMap = toMergeDef.asStringMapParamDef().getValue();

            baseMap.putAll(toMergeMap);
            Parameter mergedParam =
                buildMergedParamDefinition(name, toMergeDef, baseDef, context, baseMap)
                    .toParameter();
            populateEvaluatedResultAndTime(mergedParam, param.getEvaluatedTime());
            allParams.put(name, mergedParam);
          } else {
            ParamDefinition paramDefinition =
                ParamsMergeHelper.buildMergedParamDefinition(
                    name,
                    param.toDefinition(),
                    allParams.get(name).toDefinition(),
                    MergeContext.stepCreate(ParamSource.OUTPUT_PARAMETER),
                    param.getValue());
            Parameter parameter = paramDefinition.toParameter();
            parameter.setEvaluatedResult(param.getEvaluatedResult());
            parameter.setEvaluatedTime(param.getEvaluatedTime());
            allParams.put(name, parameter);
          }
        });
  }
@Test
  public void testMergeOutputDataParamsMapParam() throws JsonProcessingException {
    Map<String, Parameter> allParams =
        parseParamMap(
            "{\"map_param\":{\"value\":{\"string_array_param\":{\"value\":[\"p1\",\"p2\"],\"type\":\"STRING_ARRAY\"},\"long_param\":{\"value\":123,\"type\":\"LONG\"},\"nested_map_param\":{\"value\":{\"nested_1\":{\"value\":\"val1\",\"type\":\"STRING\"}},\"type\":\"MAP\"}},\"type\":\"MAP\",\"evaluated_result\":{\"string_array_param\":[\"p1\",\"p2\"],\"long_param\":123,\"nested_map_param\":{\"nested_1\":\"val1\"}},\"evaluated_time\":1626893775979}}");
    Map<String, Parameter> paramsToMerge =
        parseParamMap(
            "{\"map_param\":{\"value\":{\"string_array_param\":{\"value\":[\"p3\",\"p4\"],\"type\":\"STRING_ARRAY\"},\"nested_map_param\":{\"value\":{\"nested_2\":{\"value\":\"val2\",\"type\":\"STRING\"}},\"type\":\"MAP\"}},\"type\":\"MAP\",\"evaluated_result\":{\"string_array_param\":[\"p3\",\"p4\"],\"nested_map_param\":{\"nested_2\":\"val2\"}},\"evaluated_time\":1626893775979}}");
    ParamsMergeHelper.mergeOutputDataParams(allParams, paramsToMerge);

    Map<String, ParamDefinition> mergedMapParamDef = allParams.get("map_param").getValue();
    Map<String, Object> mergedMapEvaluated = allParams.get("map_param").getEvaluatedResult();
    // verify string array is replaced in both def and evaluated result.
    assertArrayEquals(
        new String[] {"p3", "p4"},
        mergedMapParamDef.get("string_array_param").asStringArrayParamDef().getValue());
    assertArrayEquals(
        new String[] {"p3", "p4"}, (String[]) mergedMapEvaluated.get("string_array_param"));
    // verify the long param is retained in both def and evaluated result
    assertEquals(123, (long) mergedMapParamDef.get("long_param").asLongParamDef().getValue());
    assertEquals(123, (long) mergedMapEvaluated.get("long_param"));
    // verify the nested map param is merged in both def and evaluated result
    Map<String, ParamDefinition> nestedMap =
        mergedMapParamDef.get("nested_map_param").asMapParamDef().getValue();
    assertEquals(2, nestedMap.size());
    assertTrue(nestedMap.containsKey("nested_1"));
    assertTrue(nestedMap.containsKey("nested_2"));
    Map<String, Object> nestedEvaluated =
        (Map<String, Object>) mergedMapEvaluated.get("nested_map_param");
    assertEquals("val1", nestedEvaluated.get("nested_1"));
    assertEquals("val2", nestedEvaluated.get("nested_2"));
  }