anchor.diff:  remove_rdf(Node, FileGuid, Config) ->
       SessId = ?USER_IN_BOTH_SPACES_SESS_ID(Node, Config),
       lfm_proxy:remove_metadata(Node, SessId, {guid, FileGuid}, rdf).
  +
  +
  +%%%===================================================================
  +%%% Set json metadata functions
  +%%%===================================================================
  +
  +
  +set_file_json_metadata_test(Config) ->
  +    set_json_metadata_test_base(<<"file">>, Config).
  +
  +
  +set_dir_json_metadata_test(Config) ->
  +    set_json_metadata_test_base(<<"dir">>, Config).
  +
  +
  +%% @private
  +set_json_metadata_test_base(FileType, Config) ->
  +    [P2, P1] = Providers = ?config(op_worker_nodes, Config),
  +    SessIdP1 = ?USER_IN_BOTH_SPACES_SESS_ID(P1, Config),
  +    SessIdP2 = ?USER_IN_BOTH_SPACES_SESS_ID(P2, Config),
  +
  +    FilePath = filename:join(["/", ?SPACE_2, ?RANDOM_FILE_NAME]),
  +    {ok, FileGuid} = create_file(FileType, P1, SessIdP1, FilePath),
  +
  +    % Wait for metadata sync between providers
  +    ?assertMatch({ok, _}, lfm_proxy:stat(P2, SessIdP2, {guid, FileGuid}), ?ATTEMPTS),
  +
  +    ExampleJson = #{<<"attr1">> => [0, 1, <<"val">>]},
  +
  +    DataSpec = #data_spec{
  +        required = [<<"metadata">>],
  +        optional = QsParams = [<<"filter_type">>, <<"filter">>],
  +        correct_values = #{
  +            <<"metadata">> => [ExampleJson],
  +            <<"filter_type">> => [<<"keypath">>],
  +            <<"filter">> => [
  +                <<"attr1.[1]">>,        % Test setting attr in existing array
  +                <<"attr1.[2].attr22">>, % Test error when trying to set subjson to binary (<<"val">> in ExampleJson)
  +                <<"attr1.[5]">>,        % Test setting attr beyond existing array
  +                <<"attr2.[2]">>         % Test setting attr in nonexistent array
  +            ]
  +        },
  +        bad_values = [
  +            % invalid json error can be returned only for rest (invalid json is send as
  +            % body without modification) and not gs (#{<<"metadata">> => some_binary} is send,
  +            % so no matter what that some_binary is it will be treated as string)
  +            {<<"metadata">>, <<"aaa">>, {rest_handler, ?ERROR_BAD_VALUE_JSON(<<"metadata">>)}},
  +            {<<"metadata">>, <<"{">>, {rest_handler, ?ERROR_BAD_VALUE_JSON(<<"metadata">>)}},
  +            {<<"metadata">>, <<"{\"aaa\": aaa}">>, {rest_handler, ?ERROR_BAD_VALUE_JSON(<<"metadata">>)}},
  +
  +            {<<"filter_type">>, <<"dummy">>, ?ERROR_BAD_VALUE_NOT_ALLOWED(<<"filter_type">>, [<<"keypath">>])},
  +
  +            % Below differences between error returned by rest and gs are results of sending
  +            % parameters via qs in REST, so they lost their original type and are cast to binary
  +            {<<"filter_type">>, 100, {rest, ?ERROR_BAD_VALUE_NOT_ALLOWED(<<"filter_type">>, [<<"keypath">>])}},
  +            {<<"filter_type">>, 100, {gs, ?ERROR_BAD_VALUE_BINARY(<<"filter_type">>)}},
  +            {<<"filter">>, 100, {gs, ?ERROR_BAD_VALUE_BINARY(<<"filter">>)}}
  +        ]
  +    },
  +    GetRequestFilterArg = fun(#api_test_ctx{data = Data}) ->
  +        FilterType = maps:get(<<"filter_type">>, Data, undefined),
  +        Filter = maps:get(<<"filter">>, Data, undefined),
  +
  +        case {FilterType, Filter} of
  +            {undefined, _} ->
  +                {ok, []};
  +            {<<"keypath">>, undefined} ->
  +                ?ERROR_MISSING_REQUIRED_VALUE(<<"filter">>);
  +            {<<"keypath">>, _} ->
  +                case binary:split(Filter, <<".">>, [global]) of
  +                    [<<"attr1">>, <<"[2]">>, <<"attr22">>] ->
  +                        ?ERROR_POSIX(?ENODATA);
  +                    ExistingPath ->
  +                        {ok, ExistingPath}
  +                end
  +        end
  +    end,
  +    GetExpCallResultFun = fun(TestCtx) ->
  +        case GetRequestFilterArg(TestCtx) of
  +            {ok, _Filters} -> ok;
  +            {error, _} = Error -> Error
  +        end
  +    end,
  +    CheckJsonFun = fun(#api_test_ctx{node = TestNode} = TestCtx) ->
  +        FilterOrError = GetRequestFilterArg(TestCtx),
  +        lists:foreach(fun(Node) ->
  +            % Below expected metadata depends on the tested parameters combination order.
  +            % First only required params will be tested, then with only one optional params,
  +            % next with 2 and so on. If optional param has multiple values then those later
  +            % will be also tested later.
  +            ExpJson = case FilterOrError of
  +                {ok, []} ->
  +                    ExampleJson;
  +                ?ERROR_MISSING_REQUIRED_VALUE(_) ->
  +                    % Test failed to override previously set json because of specifying
  +                    % filter_type without specifying filter
  +                    ExampleJson;
  +                {ok, [<<"attr1">>, <<"[1]">>]} ->
  +                    #{<<"attr1">> => [0, ExampleJson, <<"val">>]};
  +                ?ERROR_POSIX(?ENODATA) ->
  +                    % Operation failed and nothing should be changed -
  +                    % it should match the same json as above
  +                    #{<<"attr1">> => [0, ExampleJson, <<"val">>]};
  +                {ok, [<<"attr1">>, <<"[5]">>]} ->
  +                    #{<<"attr1">> => [0, ExampleJson, <<"val">>, null, null, ExampleJson]};
  +                {ok, [<<"attr2">>, <<"[2]">>]} ->
  +                    #{
  +                        <<"attr1">> => [0, ExampleJson, <<"val">>, null, null, ExampleJson],
  +                        <<"attr2">> => [null, null, ExampleJson]
  +                    }
  +            end,
  +            ?assertMatch({ok, ExpJson}, get_json(Node, FileGuid, Config), ?ATTEMPTS)
  +        end, Providers),
  +
  +        case FilterOrError of
  +            {ok, [<<"attr2">>, <<"[2]">>]} ->
  +                % Remove metadata after last successful parameters combination tested so that
  +                % next tests can start from setting rather then updating metadata
  +                ?assertMatch(ok, remove_json(TestNode, FileGuid, Config)),
  +                % Wait for changes to be synced between providers. Otherwise it can possible
  +                % interfere with tests on other node (e.g. information about deletion that
  +                % comes after setting ExampleJson and before setting using filter results in
  +                % json metadata removal. In such case next test using 'filter' parameter should expect
  +                % ExpMetadata = #{<<"attr1">> => [null, null, null, null, null, ExampleJson]}
  +                % rather than above one as that will be the result of setting ExampleJson
  +                % with attr1.[5] filter and no prior json set)
  +                lists:foreach(fun(Node) ->
  +                    ?assertMatch({error, ?ENODATA}, get_json(Node, FileGuid, Config), ?ATTEMPTS)
  +                end, Providers);
  +            _ ->
  +                ok
  +        end
  +    end,
  +
  +    set_metadata_test_base(
  +        <<"json">>,
  +        FileType, FilePath, FileGuid, undefined,
  +        create_validate_set_metadata_rest_call_fun(GetExpCallResultFun),
  +        create_validate_set_metadata_gs_call_fun(GetExpCallResultFun),
  +        create_verify_env_fun_for_set_json_test(FileGuid, undefined, CheckJsonFun, Config),
  +        Providers,
  +        ?CLIENT_SPEC_FOR_SPACE_2_SCENARIOS(Config),
  +        DataSpec,
  +        QsParams,
  +        Config
  +    ).
  +
  +
  +set_file_primitive_json_metadata_test(Config) ->
  +    set_primitive_json_metadata_test_base(<<"file">>, Config).
  +
  +
  +set_dir_primitive_json_metadata_test(Config) ->
  +    set_primitive_json_metadata_test_base(<<"dir">>, Config).
  +
  +
  +%% @private
  +set_primitive_json_metadata_test_base(FileType, Config) ->
  +    [P2, P1] = Providers = ?config(op_worker_nodes, Config),
  +    SessIdP1 = ?USER_IN_BOTH_SPACES_SESS_ID(P1, Config),
  +    SessIdP2 = ?USER_IN_BOTH_SPACES_SESS_ID(P2, Config),
  +
  +    FilePath = filename:join(["/", ?SPACE_2, ?RANDOM_FILE_NAME]),
  +    {ok, FileGuid} = create_file(FileType, P1, SessIdP1, FilePath),
  +
  +    % Wait for metadata sync between providers
  +    ?assertMatch({ok, _}, lfm_proxy:stat(P2, SessIdP2, {guid, FileGuid}), ?ATTEMPTS),
  +
  +    DataSpec = #data_spec{
  +        required = [<<"metadata">>],
  +        correct_values = #{<<"metadata">> => [
  +            <<"{}">>, <<"[]">>, <<"true">>, <<"0">>, <<"0.1">>,
  +            <<"null">>, <<"\"string\"">>
  +        ]}
  +    },
  +    GetExpCallResultFun = fun(_TestCtx) -> ok end,
  +    CheckJsonFun = fun(#api_test_ctx{node = TestNode, data = #{<<"metadata">> := Metadata}}) ->
  +        ExpMetadata = json_utils:decode(Metadata),
  +        lists:foreach(fun(Node) ->
  +            ?assertMatch({ok, ExpMetadata}, get_json(Node, FileGuid, Config), ?ATTEMPTS)
  +        end, Providers),
  +
  +        case Metadata of
  +            <<"\"string\"">> ->
  +                % Remove metadata after last successful parameters combination tested so that
  +                % next tests can start from setting rather then updating metadata
  +                ?assertMatch(ok, remove_json(TestNode, FileGuid, Config)),
  +                lists:foreach(fun(Node) ->
  +                    ?assertMatch({error, ?ENODATA}, get_json(Node, FileGuid, Config), ?ATTEMPTS)
  +                end, Providers);
  +            _ ->
  +                ok
  +        end
  +    end,
  +
  +    set_metadata_test_base(
  +        <<"json">>,
  +        FileType, FilePath, FileGuid, undefined,
  +        create_validate_set_metadata_rest_call_fun(GetExpCallResultFun),
  +        create_validate_set_metadata_gs_call_fun(GetExpCallResultFun),
  +        create_verify_env_fun_for_set_json_test(FileGuid, undefined, CheckJsonFun, Config),
  +        Providers,
  +        ?CLIENT_SPEC_FOR_SPACE_2_SCENARIOS(Config),
  +        DataSpec,
  +        _QsParams = [],
  +        Config
  +    ).
  +
  +
  +set_shared_file_json_metadata_test(Config) ->
  +    set_json_metadata_for_shared_file_test_base(<<"file">>, Config).
  +
  +
  +set_shared_dir_json_metadata_test(Config) ->
  +    set_json_metadata_for_shared_file_test_base(<<"dir">>, Config).
  +
  +
  +%% @private
  +set_json_metadata_for_shared_file_test_base(FileType, Config) ->
  +    [P2, P1] = Providers = ?config(op_worker_nodes, Config),
  +    SessIdP1 = ?USER_IN_BOTH_SPACES_SESS_ID(P1, Config),
  +    SessIdP2 = ?USER_IN_BOTH_SPACES_SESS_ID(P2, Config),
  +
  +    FilePath = filename:join(["/", ?SPACE_2, ?RANDOM_FILE_NAME]),
  +    {ok, FileGuid} = create_file(FileType, P1, SessIdP1, FilePath),
  +    {ok, ShareId} = lfm_proxy:create_share(P1, SessIdP1, {guid, FileGuid}, <<"share">>),
  +
  +    % Wait for metadata sync between providers
  +    ?assertMatch({ok, #file_attr{shares = [ShareId]}}, lfm_proxy:stat(P2, SessIdP2, {guid, FileGuid}), ?ATTEMPTS),
  +
  +    GetExpCallResultFun = fun(_TestCtx) -> ?ERROR_NOT_SUPPORTED end,
  +    VerifyEnvFun = fun(_, #api_test_ctx{node = TestNode}) ->
  +        ?assertMatch({error, ?ENODATA}, get_json(TestNode, FileGuid, Config), ?ATTEMPTS),
  +        true
  +    end,
  +    DataSpec = #data_spec{
  +        required = [<<"metadata">>],
  +        correct_values = #{<<"metadata">> => [?JSON_METADATA_1]}
  +    },
  +
  +    set_metadata_test_base(
  +        <<"json">>,
  +        FileType, FilePath, FileGuid, ShareId,
  +        create_validate_set_metadata_rest_call_fun(GetExpCallResultFun),
  +        create_validate_set_metadata_gs_call_fun(GetExpCallResultFun),
  +        VerifyEnvFun,
  +        Providers,
  +        ?CLIENT_SPEC_FOR_SHARE_SCENARIOS(Config),
  +        DataSpec,
  +        _QsParams = [],
  +        Config
  +    ).
  +
  +
  +set_file_json_metadata_on_provider_not_supporting_space_test(Config) ->
  +    set_json_metadata_on_provider_not_supporting_space_test_base(<<"file">>, Config).
  +
  +
  +set_dir_json_metadata_on_provider_not_supporting_space_test(Config) ->
  +    set_json_metadata_on_provider_not_supporting_space_test_base(<<"dir">>, Config).
  +
  +
  +%% @private
  +set_json_metadata_on_provider_not_supporting_space_test_base(FileType, Config) ->
  +    [P2, P1] = Providers = ?config(op_worker_nodes, Config),
  +    SessIdP1 = ?USER_IN_BOTH_SPACES_SESS_ID(P1, Config),
  +
  +    FilePath = filename:join(["/", ?SPACE_1, ?RANDOM_FILE_NAME]),
  +    {ok, FileGuid} = create_file(FileType, P1, SessIdP1, FilePath),
  +
  +    GetExpCallResultFun = fun(_TestCtx) -> ok end,
  +
  +    DataSpec = #data_spec{
  +        required = [<<"metadata">>],
  +        correct_values = #{<<"metadata">> => [?JSON_METADATA_4, ?JSON_METADATA_5]}
  +    },
  +    CheckJsonFun = fun
  +        (#api_test_ctx{node = TestNode, data = #{<<"metadata">> := Meta}}) when Meta == ?JSON_METADATA_4 ->
  +            ?assertMatch({ok, Meta}, get_json(TestNode, FileGuid, Config), ?ATTEMPTS);
  +        (#api_test_ctx{node = TestNode, data = #{<<"metadata">> := Meta}}) when Meta == ?JSON_METADATA_5 ->
  +            ?assertMatch({ok, Meta}, get_json(TestNode, FileGuid, Config), ?ATTEMPTS),
  +            % Remove ?RDF_METADATA_2 to test setting ?RDF_METADATA_1 in other scenario on clean state
  +            ?assertMatch(ok, remove_json(TestNode, FileGuid, Config)),
  +            ?assertMatch({error, ?ENODATA}, get_json(TestNode, FileGuid, Config), ?ATTEMPTS)
  +    end,
  +
  +    set_metadata_test_base(
  +        <<"json">>,
  +        FileType, FilePath, FileGuid, undefined,
  +        create_validate_set_metadata_rest_call_fun(GetExpCallResultFun, P2),
  +        create_validate_set_metadata_gs_call_fun(GetExpCallResultFun, P2),
  +        create_verify_env_fun_for_set_json_test(FileGuid, P2, CheckJsonFun, Config),
  +        Providers,
  +        ?CLIENT_SPEC_FOR_SPACE_1_SCENARIOS(Config),
  +        DataSpec,
  +        _QsParams = [],
  +        Config
  +    ).
  +
  +
  +%% @private
  +create_verify_env_fun_for_set_json_test(FileGuid, ProviderNotSupportingSpace, CheckJsonFun, Config) ->
  +    fun
  +        (false, #api_test_ctx{node = TestNode}) ->
  +            ?assertMatch({error, ?ENODATA}, get_json(TestNode, FileGuid, Config), ?ATTEMPTS),
  +            true;
  +        (true, #api_test_ctx{node = TestNode}) when TestNode == ProviderNotSupportingSpace ->
  +            ?assertMatch({error, ?ENODATA}, get_json(TestNode, FileGuid, Config), ?ATTEMPTS),
  +            true;
  +        (true, TestCtx) ->
  +            CheckJsonFun(TestCtx),
  +            true
  +    end.
  +
  +
  +%% @private
  +get_json(Node, FileGuid, Config) ->
  +    SessId = ?USER_IN_BOTH_SPACES_SESS_ID(Node, Config),
  +    lfm_proxy:get_metadata(Node, SessId, {guid, FileGuid}, json, [], false).
  +
  +
  +%% @private
  +remove_json(Node, FileGuid, Config) ->
  +    SessId = ?USER_IN_BOTH_SPACES_SESS_ID(Node, Config),
  +    lfm_proxy:remove_metadata(Node, SessId, {guid, FileGuid}, json).
   
   
   %%%===================================================================
  
positive.code: 
  
  %% @private
  encode_metadata(Metadata) when is_binary(Metadata) ->
      Metadata;
  encode_metadata(Metadata) when is_map(Metadata) ->
      json_utils:encode(Metadata).
  
  
  %% @private
  maybe_decode_json(MaybeEncodedJson) ->
      try
          json_utils:decode(MaybeEncodedJson)
  
negative.code:     end.
  
  
  %%%===================================================================
  %%% SetUp and TearDown functions
  %%%===================================================================
  