Github user kxepal commented on the pull request:
https://github.com/apache/couchdb-couch-mrview/pull/28#issuecomment-141621119
@nickva
> I tried to refactor validate_* function to re-use as much as possible.
They ended up being per-type (validate_object, validate_array, validate_string).
Basically:
```
diff --git a/src/couch_mrview.erl b/src/couch_mrview.erl
index 819f580..0472f2e 100644
--- a/src/couch_mrview.erl
+++ b/src/couch_mrview.erl
@@ -46,16 +46,85 @@
}).
-validate(DbName, DDoc) ->
- {Fields} = DDoc#doc.body,
- case couch_util:get_value(<<"options">>, Fields, {[]}) of
- {_} -> ok;
- _ -> throw({invalid_design_doc, <<"`options` parameter must be an
object.">>})
- end,
- case couch_util:get_value(<<"views">>, Fields, {[]}) of
- {_} -> ok;
- _ -> throw({invalid_design_doc, <<"`views` parameter must be an
object.">>})
- end,
+
+validate_ddoc_fields(DDoc) ->
+ lists:foreach(fun(Path) ->
+ validate_ddoc_fields(DDoc, Path)
+ end, [
+ [{<<"filters">>, object}, {any, string}],
+ [{<<"filters">>, object}, {any, string}],
+ [{<<"filters">>, object}, {any, string}],
+ [{<<"language">>, string}],
+ [{<<"lists">>, object}, {any, string}],
+ [{<<"options">>, object}],
+ [{<<"rewrites">>, array}],
+ [{<<"shows">>, object}, {any, string}],
+ [{<<"updates">>, object}, {any, string}],
+ [{<<"validate_doc_update">>, string}],
+ [{<<"views">>, object}, {any, object}, {<<"map">>, string}],
+ [{<<"views">>, object}, {any, object}, {<<"reduce">>, string}],
+ [{<<"views">>, object}, {<<"lib">>, object}, {any, string}]
+ ]),
+ ok.
+
+validate_ddoc_field(undefined, Type) when is_atom(Type) ->
+ ok;
+validate_ddoc_field(_, any) ->
+ ok;
+validate_ddoc_field(Value, string) when is_binary(Value) ->
+ ok;
+validate_ddoc_field(Value, array) when is_list(Value) ->
+ ok;
+validate_ddoc_field({Value}, object) when is_list(Value) ->
+ ok;
+validate_ddoc_field({Props}, {any, Type}) ->
+ lists:foldl(fun
+ ({_, Value}, ok) ->
+ validate_ddoc_field(Value, Type);
+ ({Key, _}, error) ->
+ {error, Key}
+ end, ok, Props);
+validate_ddoc_field({Props}, {Key, Type}) ->
+ validate_ddoc_field(couch_util:get_value(Key, Props), Type);
+validate_ddoc_field(_, _) ->
+ error.
+
+validate_ddoc_fields(DDoc, Path) ->
+ case validate_ddoc_fields(DDoc, Path, []) of
+ ok -> ok;
+ {error, {FailedPath0, Type0}} ->
+ FailedPath = iolist_to_binary(lists:reverse(FailedPath0)),
+ Type = ?l2b(atom_to_list(Type0)),
+ throw({invalid_design_doc,
+ <<"`", FailedPath/binary, "` must be ", Type/binary>>})
+ end.
+
+validate_ddoc_fields(undefined, _, _) ->
+ ok;
+validate_ddoc_fields(_, [], _) ->
+ ok;
+validate_ddoc_fields({KVS}=Props, [{any, Type} | Rest], Acc) ->
+ lists:foldl(fun
+ ({Key, _}, ok) ->
+ validate_ddoc_fields(Props, [{Key, Type} | Rest], Acc);
+ ({_, _}, {error, _}=Error) ->
+ Error
+ end, ok, KVS);
+validate_ddoc_fields({KVS}=Props, [{Key, Type} | Rest], Acc) ->
+ case validate_ddoc_field(Props, {Key, Type}) of
+ ok ->
+ validate_ddoc_fields(couch_util:get_value(Key, KVS),
+ Rest,
+ [Key | Acc]);
+ error ->
+ {error, {[Key, <<".">> | Acc], Type}};
+ {error, Key1} ->
+ {error, {[Key1, <<".">> | Acc], Type}}
+ end.
+
+
+validate(DbName, DDoc) ->
+ ok = validate_ddoc_fields(DDoc#doc.body),
GetName = fun
(#mrview{map_names = [Name | _]}) -> Name;
(#mrview{reduce_funs = [{Name, _} | _]}) -> Name;
```
If you'll not match exact error message in test you'll see that all will
pass. Except requirement for map function - that's not true case since you may
have just `views.lib` commonjs module with no any map/reduce function around
due to legacy reasons or with intention to add views later and reuse the same
code.
---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at [email protected] or file a JIRA ticket
with INFRA.
---