/__w/shurbej/shurbej/_build/test/cover/aggregate/shurbej_validate.html

1 -module(shurbej_validate).
2
3 -export([item/1, collection/1, search/1, setting/2, key_format/1, item_types/0]).
4
5 item_types() ->
6 79 shurbej_schema_data:item_types().
7
8 %% Validate an item map. Returns ok | {error, Reason}.
9 item(Item) when is_map(Item) ->
10 76 checks([
11 76 fun() -> require_field(<<"itemType">>, Item) end,
12 75 fun() -> validate_item_type(Item) end,
13 73 fun() -> validate_key_if_present(Item) end,
14 72 fun() -> validate_tags(Item) end,
15 71 fun() -> validate_creators(Item) end,
16 70 fun() -> validate_collections_field(Item) end
17 ]);
18 item(_) ->
19
:-(
{error, <<"Item must be a JSON object">>}.
20
21 %% Validate a collection map.
22 collection(Coll) when is_map(Coll) ->
23 9 checks([
24 9 fun() -> require_field(<<"name">>, Coll) end,
25 8 fun() -> validate_key_if_present(Coll) end,
26 8 fun() -> validate_string_if_present(<<"name">>, Coll) end
27 ]);
28 collection(_) ->
29
:-(
{error, <<"Collection must be a JSON object">>}.
30
31 %% Validate a search map.
32 search(Search) when is_map(Search) ->
33 9 checks([
34 9 fun() -> require_field(<<"name">>, Search) end,
35 8 fun() -> validate_key_if_present(Search) end
36 ]);
37 search(_) ->
38
:-(
{error, <<"Search must be a JSON object">>}.
39
40 %% Validate a setting key and value.
41 setting(Key, Value) when is_binary(Key) ->
42 6 case byte_size(Key) of
43 1 0 -> {error, <<"Setting key must not be empty">>};
44
:-(
_ when is_map(Value) -> ok;
45 1 _ when is_list(Value) -> ok;
46 2 _ when is_binary(Value) -> ok;
47 2 _ when is_number(Value) -> ok;
48
:-(
_ when is_boolean(Value) -> ok;
49
:-(
_ -> {error, <<"Invalid setting value">>}
50 end;
51 setting(_, _) ->
52
:-(
{error, <<"Setting key must be a string">>}.
53
54 %% Validate key format: 8 chars from Zotero charset.
55 key_format(Key) when is_binary(Key) ->
56 89 Valid = <<"23456789ABCDEFGHIJKLMNPQRSTUVWXYZ">>,
57 89 case byte_size(Key) of
58 8 ->
59 88 case lists:all(fun(C) -> binary:match(Valid, <<C>>) =/= nomatch end,
60 binary_to_list(Key)) of
61 88 true -> ok;
62
:-(
false -> {error, <<"Key contains invalid characters">>}
63 end;
64 _ ->
65 1 {error, <<"Key must be exactly 8 characters">>}
66 end;
67 key_format(_) ->
68
:-(
{error, <<"Key must be a string">>}.
69
70 %% Internal
71
72 85 checks([]) -> ok;
73 checks([Check | Rest]) ->
74 479 case Check() of
75 470 ok -> checks(Rest);
76 9 {error, _} = Err -> Err
77 end.
78
79 require_field(Field, Map) ->
80 94 case maps:get(Field, Map, undefined) of
81 1 undefined -> {error, <<"Missing required field: ", Field/binary>>};
82 2 <<>> -> {error, <<"Field must not be empty: ", Field/binary>>};
83 91 _ -> ok
84 end.
85
86 validate_item_type(Item) ->
87 75 Type = maps:get(<<"itemType">>, Item),
88 75 case lists:member(Type, item_types()) of
89 73 true -> ok;
90 2 false -> {error, <<"Unknown item type: ", Type/binary>>}
91 end.
92
93 validate_key_if_present(Map) ->
94 89 case maps:get(<<"key">>, Map, undefined) of
95
:-(
undefined -> ok;
96 89 Key -> key_format(Key)
97 end.
98
99 validate_tags(Item) ->
100 72 case maps:get(<<"tags">>, Item, []) of
101 Tags when is_list(Tags) ->
102 72 case lists:all(fun is_valid_tag/1, Tags) of
103 71 true -> ok;
104 1 false -> {error, <<"Each tag must be an object with a 'tag' string field">>}
105 end;
106 _ ->
107
:-(
{error, <<"'tags' must be an array">>}
108 end.
109
110 is_valid_tag(Tag) when is_map(Tag) ->
111 6 case maps:get(<<"tag">>, Tag, undefined) of
112 6 T when is_binary(T) -> true;
113
:-(
_ -> false
114 end;
115 1 is_valid_tag(_) -> false.
116
117 validate_creators(Item) ->
118 71 case maps:get(<<"creators">>, Item, []) of
119 Creators when is_list(Creators) ->
120 71 case lists:all(fun is_valid_creator/1, Creators) of
121 70 true -> ok;
122 1 false -> {error, <<"Each creator must have 'creatorType' and either 'name' or 'firstName'/'lastName'">>}
123 end;
124 _ ->
125
:-(
{error, <<"'creators' must be an array">>}
126 end.
127
128 is_valid_creator(C) when is_map(C) ->
129 1 HasType = is_binary(maps:get(<<"creatorType">>, C, undefined)),
130 1 HasName = is_binary(maps:get(<<"name">>, C, undefined)),
131 1 HasFirst = is_binary(maps:get(<<"firstName">>, C, undefined)),
132 1 HasLast = is_binary(maps:get(<<"lastName">>, C, undefined)),
133 1 HasType andalso (HasName orelse (HasFirst andalso HasLast));
134
:-(
is_valid_creator(_) -> false.
135
136 validate_collections_field(Item) ->
137 70 case maps:get(<<"collections">>, Item, []) of
138 Colls when is_list(Colls) ->
139 69 case lists:all(fun is_binary/1, Colls) of
140 69 true -> ok;
141
:-(
false -> {error, <<"'collections' must be an array of key strings">>}
142 end;
143 _ ->
144 1 {error, <<"'collections' must be an array">>}
145 end.
146
147 validate_string_if_present(Field, Map) ->
148 8 case maps:get(Field, Map, undefined) of
149
:-(
undefined -> ok;
150 8 V when is_binary(V) -> ok;
151
:-(
_ -> {error, <<"Field must be a string: ", Field/binary>>}
152 end.
Line Hits Source