diff options
Diffstat (limited to 'src/nouveau/src/nouveau_api.erl')
-rw-r--r-- | src/nouveau/src/nouveau_api.erl | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/nouveau/src/nouveau_api.erl b/src/nouveau/src/nouveau_api.erl new file mode 100644 index 000000000..5bf6b1731 --- /dev/null +++ b/src/nouveau/src/nouveau_api.erl @@ -0,0 +1,216 @@ +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- + +-module(nouveau_api). + +-include("nouveau.hrl"). + +-export([ + analyze/2, + index_info/1, + create_index/2, + delete_path/1, + delete_path/2, + delete_doc/3, + update_doc/4, + search/2 +]). + +-define(JSON_CONTENT_TYPE, {"Content-Type", "application/json"}). + +analyze(Text, Analyzer) when + is_binary(Text), is_binary(Analyzer) +-> + ReqBody = {[{<<"text">>, Text}, {<<"analyzer">>, Analyzer}]}, + Resp = send_if_enabled( + nouveau_util:nouveau_url() ++ "/analyze", + [?JSON_CONTENT_TYPE], + post, + jiffy:encode(ReqBody) + ), + case Resp of + {ok, "200", _, RespBody} -> + Json = jiffy:decode(RespBody, [return_maps]), + {ok, maps:get(<<"tokens">>, Json)}; + {ok, StatusCode, _, RespBody} -> + {error, jaxrs_error(StatusCode, RespBody)}; + {error, Reason} -> + send_error(Reason) + end; +analyze(_, _) -> + {error, {bad_request, <<"'text' and 'analyzer' fields must be non-empty strings">>}}. + +index_info(#index{} = Index) -> + Resp = send_if_enabled(index_url(Index), [], get), + case Resp of + {ok, "200", _, RespBody} -> + {ok, jiffy:decode(RespBody, [return_maps])}; + {ok, StatusCode, _, RespBody} -> + {error, jaxrs_error(StatusCode, RespBody)}; + {error, Reason} -> + send_error(Reason) + end. + +create_index(#index{} = Index, IndexDefinition) -> + Resp = send_if_enabled( + index_url(Index), [?JSON_CONTENT_TYPE], put, jiffy:encode(IndexDefinition) + ), + case Resp of + {ok, "204", _, _} -> + ok; + {ok, StatusCode, _, RespBody} -> + {error, jaxrs_error(StatusCode, RespBody)}; + {error, Reason} -> + send_error(Reason) + end. + +delete_path(Path) -> + delete_path(Path, []). + +delete_path(Path, Exclusions) when + is_binary(Path), is_list(Exclusions) +-> + Resp = send_if_enabled( + index_path(Path), [?JSON_CONTENT_TYPE], delete, jiffy:encode(Exclusions) + ), + case Resp of + {ok, "204", _, _} -> + ok; + {ok, StatusCode, _, RespBody} -> + {error, jaxrs_error(StatusCode, RespBody)}; + {error, Reason} -> + send_error(Reason) + end. + +delete_doc(#index{} = Index, DocId, UpdateSeq) when + is_binary(DocId), is_integer(UpdateSeq) +-> + ReqBody = {[{<<"seq">>, UpdateSeq}]}, + Resp = send_if_enabled( + doc_url(Index, DocId), [?JSON_CONTENT_TYPE], delete, jiffy:encode(ReqBody) + ), + case Resp of + {ok, "204", _, _} -> + ok; + {ok, StatusCode, _, RespBody} -> + {error, jaxrs_error(StatusCode, RespBody)}; + {error, Reason} -> + send_error(Reason) + end. + +update_doc(#index{} = Index, DocId, UpdateSeq, Fields) when + is_binary(DocId), is_integer(UpdateSeq), is_list(Fields) +-> + ReqBody = {[{<<"seq">>, UpdateSeq}, {<<"fields">>, Fields}]}, + Resp = send_if_enabled( + doc_url(Index, DocId), [?JSON_CONTENT_TYPE], put, jiffy:encode(ReqBody) + ), + case Resp of + {ok, "204", _, _} -> + ok; + {ok, StatusCode, _, RespBody} -> + {error, jaxrs_error(StatusCode, RespBody)}; + {error, Reason} -> + send_error(Reason) + end. + +search(#index{} = Index, QueryArgs) -> + Resp = send_if_enabled( + search_url(Index), [?JSON_CONTENT_TYPE], post, jiffy:encode(QueryArgs) + ), + case Resp of + {ok, "200", _, RespBody} -> + {ok, jiffy:decode(RespBody, [return_maps])}; + {ok, StatusCode, _, RespBody} -> + {error, jaxrs_error(StatusCode, RespBody)}; + {error, Reason} -> + send_error(Reason) + end. + +%% private functions + +index_path(Path) -> + lists:flatten( + io_lib:format( + "~s/index/~s", + [ + nouveau_util:nouveau_url(), + couch_util:url_encode(Path) + ] + ) + ). + +index_url(#index{} = Index) -> + lists:flatten( + io_lib:format( + "~s/index/~s", + [ + nouveau_util:nouveau_url(), + couch_util:url_encode(nouveau_util:index_name(Index)) + ] + ) + ). + +doc_url(#index{} = Index, DocId) -> + lists:flatten( + io_lib:format( + "~s/index/~s/doc/~s", + [ + nouveau_util:nouveau_url(), + couch_util:url_encode(nouveau_util:index_name(Index)), + couch_util:url_encode(DocId) + ] + ) + ). + +search_url(IndexName) -> + index_url(IndexName) ++ "/search". + +jaxrs_error("400", Body) -> + {bad_request, message(Body)}; +jaxrs_error("404", Body) -> + {not_found, message(Body)}; +jaxrs_error("405", Body) -> + {method_not_allowed, message(Body)}; +jaxrs_error("417", Body) -> + {expectation_failed, message(Body)}; +jaxrs_error("422", Body) -> + {bad_request, lists:join(" and ", errors(Body))}; +jaxrs_error("500", Body) -> + {internal_server_error, message(Body)}. + +send_error({conn_failed, _}) -> + {error, {service_unavailable, <<"Search service unavailable.">>}}; +send_error(Reason) -> + {error, Reason}. + +message(Body) -> + Json = jiffy:decode(Body, [return_maps]), + maps:get(<<"message">>, Json). + +errors(Body) -> + Json = jiffy:decode(Body, [return_maps]), + maps:get(<<"errors">>, Json). + +send_if_enabled(Url, Header, Method) -> + send_if_enabled(Url, Header, Method, []). + +send_if_enabled(Url, Header, Method, Body) -> + case nouveau:enabled() of + true -> + ibrowse:send_req(Url, Header, Method, Body); + false -> + {error, nouveau_not_enabled} + end. |