public static void mergeParams(
      Map<String, ParamDefinition> params,
      Map<String, ParamDefinition> paramsToMerge,
      MergeContext context) {
    if (paramsToMerge == null) {
      return;
    }
    Stream.concat(params.keySet().stream(), paramsToMerge.keySet().stream())
        .forEach(
            name -> {
              ParamDefinition paramToMerge = paramsToMerge.get(name);
              if (paramToMerge == null) {
                return;
              }

              if (paramToMerge.getType() == ParamType.MAP && paramToMerge.isLiteral()) {
                Map<String, ParamDefinition> baseMap = mapValueOrEmpty(params, name);
                Map<String, ParamDefinition> toMergeMap = mapValueOrEmpty(paramsToMerge, name);
                mergeParams(
                    baseMap,
                    toMergeMap,
                    MergeContext.copyWithParentMode(
                        context, params.getOrDefault(name, paramToMerge).getMode()));
                params.put(
                    name,
                    buildMergedParamDefinition(
                        name, paramToMerge, params.get(name), context, baseMap));
              } else if (paramToMerge.getType() == ParamType.STRING_MAP
                  && paramToMerge.isLiteral()) {
                Map<String, String> baseMap = stringMapValueOrEmpty(params, name);
                Map<String, String> toMergeMap = stringMapValueOrEmpty(paramsToMerge, name);
                baseMap.putAll(toMergeMap);
                params.put(
                    name,
                    buildMergedParamDefinition(
                        name, paramToMerge, params.get(name), context, baseMap));
              } else {
                params.put(
                    name,
                    buildMergedParamDefinition(
                        name, paramToMerge, params.get(name), context, paramToMerge.getValue()));
              }
            });
  }
@Test
  public void testMergeForeachRestartWithMutableOnStart() throws IOException {
    DefaultParamManager defaultParamManager =
        new DefaultParamManager(JsonHelper.objectMapperWithYaml());
    defaultParamManager.init();
    Map<String, ParamDefinition> allParams =
        defaultParamManager.getDefaultParamsForType(StepType.FOREACH).get();

    Map<String, ParamDefinition> paramsToMerge =
        parseParamDefMap(
            "{'loop_params': {'value': {'i' : { 'value': [1, 2, 3], 'type': 'long_array', "
                + "'validator': 'param!=null && param.size() > 2', 'mode': 'mutable'}, "
                + "'j' : {'expression': 'param1', 'type': 'STRING_ARRAY', "
                + "'validator': 'param!=null && param.size() > 2'}}, 'type': 'MAP'}}");

    AssertHelper.assertThrows(
        "throws exception when a foreach source restarts and tries to mutate params with MUTABLE_ON_START mode",
        MaestroValidationException.class,
        "Cannot modify param with mode [MUTABLE_ON_START] for parameter [loop_params]",
        () -> ParamsMergeHelper.mergeParams(allParams, paramsToMerge, foreachRestartMergeContext));
  }