Skip to content

Commit

Permalink
Merge pull request DiversityTemplating#2 from DiversityTemplating/com…
Browse files Browse the repository at this point in the history
…ponents

Components
  • Loading branch information
jonas hadin committed Sep 18, 2014
2 parents f4ef181 + 479e719 commit 2ba951a
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 36 deletions.
94 changes: 83 additions & 11 deletions src/component_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,96 @@
-record(state, {
}).

-define(JSON_HEADER, [{<<"content-type">>, <<"application/json">>}]).

init(_, Req, _Opts) ->
{ok, Req, #state{}}.

handle(Req, State=#state{}) ->
{Method, Req2} = cowboy_req:method(Req),
{ok, Req3} =
case Method of
<<"POST">> -> handle_post(Req2, State);
<<"GET">> -> handle_get(Req2, State)
end,
{ok, Req3, State}.

handle_get(Req, _State=#state{}) ->
{PathInfo, Req2} = cowboy_req:path_info(Req),
{ComponentName, Req3} = cowboy_req:binding(component, Req2),
Tags = git_utils:tags(ComponentName),
{ok, Req4} =
case PathInfo of
[] ->
cowboy_req:reply(200, ?JSON_HEADER, jiffy:encode(Tags), Req3);
[<<"*">>] ->
Tag = find_latest_tag(Tags),
Data = git_utils:get_diversity_json(ComponentName, Tag),
cowboy_req:reply(200, ?JSON_HEADER, Data, Req3);
[Tag] ->
Data = case lists:member(Tag, Tags) of
true ->
git_utils:get_diversity_json(ComponentName, Tag);
false ->
PatchNr = find_latest_patch(Tag, Tags),
TagWithPatch = <<Tag/binary, ".", PatchNr/binary>>,
%% Ensure that it's an valid tag
true = lists:member(TagWithPatch, Tags),
git_utils:get_diversity_json(ComponentName, TagWithPatch)
end,
cowboy_req:reply(200, ?JSON_HEADER, Data, Req3);
[Tag, Settings]
when Settings =:= <<"settings">>;
Settings =:= <<"settingsForm">> ->
Json = git_utils:get_diversity_json(ComponentName, Tag),
{DiversityData} = jiffy:decode(Json),
SettingsBinary = proplists:get_value(Settings, DiversityData, {[]}),
SettingsJson = jiffy:encode(SettingsBinary),
cowboy_req:reply(200, ?JSON_HEADER, SettingsJson, Req3);
_ ->
cowboy_req:reply(404, Req3)
end,
{ok, Req4}.

handle_post(Req, _State=#state{}) ->
{ComponentName, Req2} = cowboy_req:binding(component, Req),
{PathInfo, Req3} = cowboy_req:path_info(Req2),
case PathInfo of
[<<"update">>] ->
git_utils:git_refresh_repo(ComponentName),
cowboy_req:reply(200, ?JSON_HEADER, <<"Updated">>, Req3);
_ ->
cowboy_req:reply(404, Req3)
end,
{ok, Req3}.


find_latest_tag([]) ->
<<"">>;
find_latest_tag(Tags) ->
NumericTags = lists:filter(fun(X) ->
case re:run(binary_to_list(X), "^\\d\\.\\d\\.\\d$") of
nomatch -> false; _ -> true
end
end, Tags),
case NumericTags of
[] -> <<"HEAD">>;
_ -> lists:last(lists:sort(NumericTags))
end.

{ok, Req4} = case PathInfo of
[] ->
Tags = git_utils:tags(ComponentName),
cowboy_req:reply(200,
[{<<"content-type">>, <<"application/json">>}],
jiffy:encode(Tags),
Req3);
_ -> cowboy_req:reply(404, Req3)
end,

{ok, Req4, State}.
find_latest_patch(PrefixTag, Tags) ->
lists:foldl(fun(Tag, LatestPatch) ->
case binary:longest_common_prefix([Tag, PrefixTag])of
3 ->
%% <<"1.2.3">>, the patch nr is char 4
PatchNr = binary:part(Tag, 4, 1),
case LatestPatch =< PatchNr of
true -> PatchNr;
false -> LatestPatch
end;
_ -> LatestPatch
end
end, <<"0">>, Tags).

terminate(_Reason, _Req, _State) ->
ok.
48 changes: 41 additions & 7 deletions src/component_list_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,55 @@
-record(state, {
}).

-define(INFORMATION_FIELDS, [<<"name">>,<<"title">>,<<"description">>,<<"version">>,<<"grouping">>]).

init(_, Req, _Opts) ->
{ok, Req, #state{}}.

handle(Req, State=#state{}) ->
{Path, Req2} = cowboy_req:path(Req),
{ok, Req3} = case Path of
{ok, Req4} = case Path of
<<"/components/">> ->
Projects = gitlab_utils:get_public_projects(),
{QueryVal, Req3} = cowboy_req:qs_val(<<"grouping">>, Req2),
Projects = case QueryVal of
undefined ->
PublicProjects = gitlab_utils:get_public_projects(),
get_components_information(PublicProjects);
Group when is_binary(Group) ->
PublicProjects = gitlab_utils:get_public_projects(),
filter_projects_by_grouping(PublicProjects, Group)
end,
cowboy_req:reply(200,
[{<<"content-type">>, <<"application/json">>}],
jiffy:encode(maps:keys(Projects)),
Req2);
_ -> cowboy_req:reply(404, Req2)
[{<<"content-type">>, <<"application/json">>}],
jiffy:encode(Projects),
Req3);
_ ->
cowboy_req:reply(404, Req2)
end,
{ok, Req3, State}.
{ok, Req4, State}.

%% @doc Returns a list with new maps only containing the keys in information_fields
-spec get_components_information([map()]) -> [map()].
get_components_information(Components) ->
maps:fold(fun(ComponentName, ComponentUrl, Acc) ->
Json = git_utils:get_diversity_json(ComponentName, ComponentUrl, <<"HEAD">>),
DiversityMap = jiffy:decode(Json, [return_maps]),
Without = lists:filter(fun(K) -> not lists:member(K, ?INFORMATION_FIELDS) end, maps:keys(DiversityMap)),
[maps:without(Without, DiversityMap) | Acc]
end, [], Components).

%% @doc Returns a list of component names filtered by given grouping
-spec filter_projects_by_grouping([map()], binary()) -> [binary()].
filter_projects_by_grouping(Components, Grouping) ->
maps:fold(fun(ComponentName, ComponentUrl, Acc) ->
Json = git_utils:get_diversity_json(ComponentName, ComponentUrl, <<"HEAD">>),
DiversityMap = jiffy:decode(Json, [return_maps]),
Groups = maps:get(<<"grouping">>, DiversityMap, []),
case lists:member(Grouping, Groups) of
true -> [ComponentName | Acc];
false -> Acc
end
end, [], Components).

terminate(_Reason, _Req, _State) ->
ok.
65 changes: 51 additions & 14 deletions src/git_utils.erl
Original file line number Diff line number Diff line change
@@ -1,28 +1,65 @@
-module(git_utils).

%% API
-export([clone_bare/1, tags/1]).
-export([tags/1, get_diversity_json/2, get_diversity_json/3, git_refresh_repo/1]).


%% @doc Clones a git repository with the bare flag
clone_bare(RepoUrl) ->
{ok, RepoDir} = application:get_env(divapi, repo_dir),
filelib:ensure_dir(RepoDir),
c:cd(RepoDir),
os:cmd("git clone --bare " ++ RepoUrl).

%% @doc Returns a list with all tags in a git repository
-spec tags(binary()) -> [binary()].
tags(RepoName) ->
Cmd = "tag",
ResultString = get_git_result(RepoName, Cmd),
[list_to_binary(Tag) || Tag <- string:tokens(ResultString, "\n")].

%% @doc Returns the diversity.json for a tag in a repo
-spec get_diversity_json(binary(), binary()) -> binary().
get_diversity_json(RepoName, Tag) ->
Cmd = "show " ++ binary_to_list(Tag) ++ ":diversity.json",
list_to_binary(get_git_result(RepoName, Cmd)).
-spec get_diversity_json(binary(), binary(), binary()) -> binary().
get_diversity_json(RepoName, RepoUrl, Tag) ->
Cmd = "show " ++ binary_to_list(Tag) ++ ":diversity.json",
list_to_binary(get_git_result(RepoName, RepoUrl, Cmd)).

%% @doc Fetches the latest tags for a repo
-spec git_refresh_repo(binary()) -> any().
git_refresh_repo(RepoName) ->
{ok, RepoDir} = application:get_env(divapi, repo_dir),
GitRepoName = binary_to_list(RepoName) ++ ".git",
GitRepoName = RepoDir ++ "/" ++ binary_to_list(RepoName) ++ ".git",
file:set_cwd(GitRepoName),
Cmd = "fetch origin master:master",
git_cmd(Cmd).

%% ----------------------------------------------------------------------------
%% Internal stuff
%% ----------------------------------------------------------------------------
get_git_result(RepoName, Cmd) ->
get_git_result(RepoName, undefined, Cmd).
get_git_result(RepoName, RepoUrl, Cmd) ->
{ok, RepoDir} = application:get_env(divapi, repo_dir),
GitRepoName = RepoDir ++ "/" ++ binary_to_list(RepoName) ++ ".git",
%% Clone git repo if non-existing in configured dir
case filelib:is_dir(GitRepoName) of
false ->
RepoUrl = gitlab_utils:get_public_project_url(RepoName),
clone_bare(binary_to_list(RepoUrl))
RepoUrl2 = get_repo_url(RepoName, RepoUrl),
clone_bare(binary_to_list(RepoUrl2));
true ->
%% Checkout not needed
ok
end,
file:set_cwd(GitRepoName),
git_cmd(Cmd).

clone_bare(RepoUrl) ->
{ok, RepoDir} = application:get_env(divapi, repo_dir),
filelib:ensure_dir(RepoDir),
file:set_cwd(RepoDir),
Cmd = "clone --bare " ++ RepoUrl,
git_cmd(Cmd).

get_repo_url(RepoName, undefined) -> gitlab_utils:get_public_project_url(RepoName);
get_repo_url(_, RepoUrl) -> RepoUrl.

c:cd(RepoDir ++ "/" ++ GitRepoName),
Cmd = "git tag",
ResultString = os:cmd(Cmd),
[list_to_binary(Tag) || Tag <- string:tokens(ResultString, "\n")].
git_cmd(Cmd) ->
os:cmd("git " ++ Cmd).
2 changes: 1 addition & 1 deletion test/api_handler_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ basic_handle(_Config) ->
meck:expect(cowboy_req, reply, fun(200, Headers, Body, Req) -> {ok, Req} end),

Response = diversity_api_handler:handle(SimplifiedReq, State),
Response =:= {ok, SimplifiedReq, State},
Response == {ok, SimplifiedReq, State},
meck:unload(cowboy_req).
98 changes: 98 additions & 0 deletions test/component_handler_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
-module(component_handler_SUITE).
-include_lib("common_test/include/ct.hrl").

-export([all/0]).
%% API
-export([
handle_component_tags/1,
handle_component_tag/1,
handle_component_part_tag/1,
handle_component_wildcard/1
]).

-record(state, {}).

all() -> [handle_component_tags, handle_component_tag, handle_component_part_tag, handle_component_wildcard].

handle_component_tags(_Config) ->
SimplifiedReq = {http_req,<<"GET">>,'HTTP/1.1'},
State = #state{},
application:set_env(divapi, repo_dir, "gitdir"),
meck:new(cowboy_req),
meck:expect(cowboy_req, reply, fun(200, _Headers, Body, _Req) -> {ok, Body} end),
meck:expect(cowboy_req, path_info, fun(Req) -> {[],Req} end),
meck:expect(cowboy_req, binding, fun(component, Req) -> {<<"Component">>, Req} end),
meck:expect(cowboy_req, method, fun(Req) -> {<<"GET">>, Req} end),

meck:new(git_utils),
meck:expect(git_utils, tags, fun(_) -> [<<"0.1.0">>, <<"0.2.0">>] end),

Expected = jiffy:encode([<<"0.1.0">>, <<"0.2.0">>]),
Response = component_handler:handle(SimplifiedReq, State),

Response = {ok, Expected, State},
meck:unload(cowboy_req),
meck:unload(git_utils).

handle_component_tag(_Config) ->
SimplifiedReq = {http_req,<<"GET">>,'HTTP/1.1'},
State = #state{},
application:set_env(divapi, repo_dir, "gitdir"),
meck:new(cowboy_req),
meck:expect(cowboy_req, reply, fun(200, _Headers, Body, _Req) -> {ok, Body} end),
meck:expect(cowboy_req, path_info, fun(Req) -> {[<<"0.1.1">>],Req} end),
meck:expect(cowboy_req, binding, fun(component, Req) -> {<<"Component">>, Req} end),
meck:expect(cowboy_req, method, fun(Req) -> {<<"GET">>, Req} end),

meck:new(git_utils),
meck:expect(git_utils, tags, fun(_) -> [<<"0.1.1">>, <<"0.2.0">>] end),
meck:expect(git_utils, get_diversity_json, fun(<<"Component">>,_) -> <<"{}">> end),

Expected = <<"{}">>,
Response = component_handler:handle(SimplifiedReq, State),

Response = {ok, Expected, State},
meck:unload(cowboy_req),
meck:unload(git_utils).

handle_component_part_tag(_Config) ->
SimplifiedReq = {http_req,<<"GET">>,'HTTP/1.1'},
State = #state{},
application:set_env(divapi, repo_dir, "gitdir"),
meck:new(cowboy_req),
meck:expect(cowboy_req, reply, fun(200, _Headers, Body, _Req) -> {ok, Body} end),
meck:expect(cowboy_req, path_info, fun(Req) -> {[<<"0.1">>],Req} end),
meck:expect(cowboy_req, binding, fun(component, Req) -> {<<"Component">>, Req} end),
meck:expect(cowboy_req, method, fun(Req) -> {<<"GET">>, Req} end),

meck:new(git_utils),
meck:expect(git_utils, tags, fun(_) -> [<<"0.1.1">>, <<"0.2.0">>] end),
meck:expect(git_utils, get_diversity_json, fun(<<"Component">>,_) -> <<"{}">> end),

Expected = <<"{}">>,
Response = component_handler:handle(SimplifiedReq, State),

Response = {ok, Expected, State},
meck:unload(cowboy_req),
meck:unload(git_utils).

handle_component_wildcard(_Config) ->
SimplifiedReq = {http_req,<<"GET">>,'HTTP/1.1'},
State = #state{},
application:set_env(divapi, repo_dir, "gitdir"),
meck:new(cowboy_req),
meck:expect(cowboy_req, reply, fun(200, _Headers, Body, _Req) -> {ok, Body} end),
meck:expect(cowboy_req, path_info, fun(Req) -> {[<<"*">>],Req} end),
meck:expect(cowboy_req, binding, fun(component, Req) -> {<<"Component">>, Req} end),
meck:expect(cowboy_req, method, fun(Req) -> {<<"GET">>, Req} end),

meck:new(git_utils),
meck:expect(git_utils, tags, fun(_) -> [<<"0.1.1">>, <<"0.2.0">>] end),
meck:expect(git_utils, get_diversity_json, fun(<<"Component">>,_) -> <<"{\"test\":\"stuff\"}">> end),

Expected = <<"{\"test\":\"stuff\"}">>,
Response = component_handler:handle(SimplifiedReq, State),

Response = {ok, Expected, State},
meck:unload(cowboy_req),
meck:unload(git_utils).
4 changes: 1 addition & 3 deletions test/gitlab_utils_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,5 @@
all() -> [get_public_projects].

get_public_projects(_Config) ->
inets:start(),
Res = gitlab_utils:get_public_projects(),
ct:pal("~p",[Res]).
ok.

0 comments on commit 2ba951a

Please sign in to comment.