mirror of
https://github.com/valitydev/woody_erlang_user_identity.git
synced 2024-11-06 00:45:24 +00:00
MSPF-206: Add user identity extension initial attempt (#1)
* MSPF-206 Add user identity extension initial attempt
This commit is contained in:
parent
89d8d0ec3e
commit
ef4bd49144
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
.rebar3
|
||||
_*
|
||||
.eunit
|
||||
*.o
|
||||
*.beam
|
||||
*.plt
|
||||
*.swp
|
||||
*.swo
|
||||
.erlang.cookie
|
||||
ebin
|
||||
log
|
||||
erl_crash.dump
|
||||
.rebar
|
||||
logs
|
||||
_build
|
||||
erlang.sublime-*
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "build_utils"]
|
||||
path = build_utils
|
||||
url = git@github.com:rbkmoney/build_utils.git
|
43
Jenkinsfile
vendored
Normal file
43
Jenkinsfile
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
#!groovy
|
||||
|
||||
def finalHook = {
|
||||
runStage('store CT logs') {
|
||||
archive '_build/test/logs/'
|
||||
}
|
||||
}
|
||||
|
||||
build('woody_user_identity', 'docker-host', finalHook) {
|
||||
checkoutRepo()
|
||||
loadBuildUtils()
|
||||
|
||||
def pipeDefault
|
||||
def withWsCache
|
||||
runStage('load pipeline') {
|
||||
env.JENKINS_LIB = "build_utils/jenkins_lib"
|
||||
pipeDefault = load("${env.JENKINS_LIB}/pipeDefault.groovy")
|
||||
withWsCache = load("${env.JENKINS_LIB}/withWsCache.groovy")
|
||||
}
|
||||
|
||||
pipeDefault() {
|
||||
runStage('compile') {
|
||||
withGithubPrivkey {
|
||||
sh 'make wc_compile'
|
||||
}
|
||||
}
|
||||
runStage('lint') {
|
||||
sh 'make wc_lint'
|
||||
}
|
||||
runStage('xref') {
|
||||
sh 'make wc_xref'
|
||||
}
|
||||
runStage('dialyze') {
|
||||
withWsCache("_build/default/rebar3_19.1_plt") {
|
||||
sh 'make wc_dialyze'
|
||||
}
|
||||
}
|
||||
runStage('test') {
|
||||
sh "make wc_test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
58
Makefile
Normal file
58
Makefile
Normal file
@ -0,0 +1,58 @@
|
||||
REBAR := $(shell which rebar3 2>/dev/null || which ./rebar3)
|
||||
SUBMODULES = build_utils
|
||||
SUBTARGETS = $(patsubst %,%/.git,$(SUBMODULES))
|
||||
|
||||
UTILS_PATH := build_utils
|
||||
TEMPLATES_PATH := .
|
||||
|
||||
BUILD_IMAGE_TAG := 55e987e74e9457191a5b4a7c5dc9e3838ae82d2b
|
||||
|
||||
CALL_ANYWHERE := all submodules rebar-update compile xref lint dialyze test start devrel release clean distclean
|
||||
|
||||
SERVICE_NAME := woody_user_identity
|
||||
|
||||
# Hint: 'test' might be a candidate for CALL_W_CONTAINER-only target
|
||||
CALL_W_CONTAINER := $(CALL_ANYWHERE)
|
||||
|
||||
.PHONY: $(CALL_W_CONTAINER) generate
|
||||
|
||||
all: compile
|
||||
|
||||
-include $(UTILS_PATH)/make_lib/utils_container.mk
|
||||
|
||||
$(SUBTARGETS): %/.git: %
|
||||
git submodule update --init $<
|
||||
touch $@
|
||||
|
||||
submodules: $(SUBTARGETS)
|
||||
|
||||
rebar-update:
|
||||
$(REBAR) update
|
||||
|
||||
compile: submodules rebar-update
|
||||
$(REBAR) compile
|
||||
|
||||
devrel: submodules
|
||||
$(REBAR) release
|
||||
|
||||
test: submodules
|
||||
$(REBAR) do eunit, ct
|
||||
|
||||
xref: submodules
|
||||
$(REBAR) xref
|
||||
|
||||
clean:
|
||||
$(REBAR) clean
|
||||
|
||||
distclean: clean
|
||||
rm -rf _build
|
||||
|
||||
dialyze:
|
||||
$(REBAR) dialyzer
|
||||
|
||||
release:
|
||||
$(REBAR) as prod release
|
||||
|
||||
lint:
|
||||
elvis rock
|
||||
|
11
README.md
11
README.md
@ -1,2 +1,9 @@
|
||||
# woody_erlang_user_identity
|
||||
Woody Erlang extension for user identity
|
||||
Woody User Identity
|
||||
=====
|
||||
|
||||
A simple library that allows to manipulate user identity through context
|
||||
|
||||
Build
|
||||
-----
|
||||
|
||||
$ rebar3 compile
|
||||
|
1
build_utils
Submodule
1
build_utils
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 877ae1c8299675fff8c4d445657b60064176b077
|
48
elvis.config
Normal file
48
elvis.config
Normal file
@ -0,0 +1,48 @@
|
||||
[
|
||||
{elvis, [
|
||||
{config, [
|
||||
#{
|
||||
dirs => ["src"],
|
||||
filter => "*.erl",
|
||||
rules => [
|
||||
{elvis_style, line_length, #{limit => 120, skip_comments => false}},
|
||||
{elvis_style, no_tabs},
|
||||
{elvis_style, no_trailing_whitespace},
|
||||
{elvis_style, macro_module_names},
|
||||
{elvis_style, operator_spaces, #{rules => [{right, ","}, {right, "++"}, {left, "++"}]}},
|
||||
{elvis_style, nesting_level, #{level => 4}},
|
||||
{elvis_style, god_modules, #{limit => 25}},
|
||||
{elvis_style, no_if_expression},
|
||||
{elvis_style, invalid_dynamic_call, #{ignore => [elvis]}},
|
||||
{elvis_style, used_ignored_variable},
|
||||
{elvis_style, no_behavior_info},
|
||||
{elvis_style, module_naming_convention, #{regex => "^([a-z][a-z0-9]*_?)*(_SUITE)?$"}},
|
||||
{elvis_style, function_naming_convention, #{regex => "^([a-z][a-z0-9]*_?)*$"}},
|
||||
{elvis_style, state_record_and_type},
|
||||
{elvis_style, no_spec_with_records},
|
||||
{elvis_style, dont_repeat_yourself, #{min_complexity => 10}},
|
||||
{elvis_style, no_debug_call, #{ignore => [elvis, elvis_utils]}}
|
||||
]
|
||||
},
|
||||
#{
|
||||
dirs => ["."],
|
||||
filter => "Makefile",
|
||||
ruleset => makefiles
|
||||
},
|
||||
#{
|
||||
dirs => ["."],
|
||||
filter => "elvis.config",
|
||||
ruleset => elvis_config
|
||||
},,
|
||||
#{
|
||||
dirs => ["."],
|
||||
filter => "rebar.config",
|
||||
rules => [
|
||||
{elvis_style, line_length, #{limit => 120, skip_comments => false}},
|
||||
{elvis_style, no_tabs},
|
||||
{elvis_style, no_trailing_whitespace}
|
||||
]
|
||||
}
|
||||
]}
|
||||
]}
|
||||
].
|
44
rebar.config
Normal file
44
rebar.config
Normal file
@ -0,0 +1,44 @@
|
||||
% Common project erlang options.
|
||||
{erl_opts, [
|
||||
% mandatory
|
||||
debug_info,
|
||||
warnings_as_errors,
|
||||
warn_export_all,
|
||||
warn_missing_spec,
|
||||
warn_untyped_record,
|
||||
warn_export_vars,
|
||||
|
||||
% by default
|
||||
warn_unused_record,
|
||||
warn_bif_clash,
|
||||
warn_obsolete_guard,
|
||||
warn_unused_vars,
|
||||
warn_shadow_vars,
|
||||
warn_unused_import,
|
||||
warn_unused_function,
|
||||
warn_deprecated_function
|
||||
]}.
|
||||
|
||||
% Common project dependencies.
|
||||
{deps, [
|
||||
{woody, ".*", {git,"git@github.com:rbkmoney/woody_erlang.git",{branch, "master"}}},
|
||||
{genlib, {git, "https://github.com/rbkmoney/genlib.git", {branch, "master"}}}
|
||||
]}.
|
||||
|
||||
{xref_checks, [
|
||||
undefined_function_calls,
|
||||
undefined_functions,
|
||||
deprecated_functions_calls,
|
||||
deprecated_functions,
|
||||
locals_not_used
|
||||
]}.
|
||||
|
||||
{dialyzer, [
|
||||
{warnings, [
|
||||
unmatched_returns,
|
||||
error_handling,
|
||||
race_conditions,
|
||||
unknown
|
||||
]},
|
||||
{plt_apps, all_deps}
|
||||
]}.
|
38
rebar.lock
Normal file
38
rebar.lock
Normal file
@ -0,0 +1,38 @@
|
||||
{"1.1.0",
|
||||
[{<<"certifi">>,{pkg,<<"certifi">>,<<"0.7.0">>},2},
|
||||
{<<"cowboy">>,{pkg,<<"cowboy">>,<<"1.0.4">>},1},
|
||||
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},2},
|
||||
{<<"genlib">>,
|
||||
{git,"https://github.com/rbkmoney/genlib.git",
|
||||
{ref,"82ff16f4314fc406dd90752467a08fe401b009ef"}},
|
||||
1},
|
||||
{<<"hackney">>,{pkg,<<"hackney">>,<<"1.6.2">>},1},
|
||||
{<<"idna">>,{pkg,<<"idna">>,<<"1.2.0">>},2},
|
||||
{<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},2},
|
||||
{<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.0.2">>},2},
|
||||
{<<"ranch">>,{pkg,<<"ranch">>,<<"1.3.2">>},2},
|
||||
{<<"snowflake">>,
|
||||
{git,"https://github.com/rbkmoney/snowflake.git",
|
||||
{ref,"0a598108f6582affe3b4ae550fc5b9f2062e318a"}},
|
||||
1},
|
||||
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},2},
|
||||
{<<"thrift">>,
|
||||
{git,"https://github.com/rbkmoney/thrift_erlang.git",
|
||||
{ref,"240bbc842f6e9b90d01bd07838778cf48752b510"}},
|
||||
1},
|
||||
{<<"woody">>,
|
||||
{git,"git@github.com:rbkmoney/woody_erlang.git",
|
||||
{ref,"8f1f42e209a272570c5355599f908339cd097b5b"}},
|
||||
0}]}.
|
||||
[
|
||||
{pkg_hash,[
|
||||
{<<"certifi">>, <<"861A57F3808F7EB0C2D1802AFEAAE0FA5DE813B0DF0979153CBAFCD853ABABAF">>},
|
||||
{<<"cowboy">>, <<"A324A8DF9F2316C833A470D918AAF73AE894278B8AA6226CE7A9BF699388F878">>},
|
||||
{<<"cowlib">>, <<"9D769A1D062C9C3AC753096F868CA121E2730B9A377DE23DEC0F7E08B1DF84EE">>},
|
||||
{<<"hackney">>, <<"96A0A5E7E65B7ACAD8031D231965718CC70A9B4131A8B033B7543BBD673B8210">>},
|
||||
{<<"idna">>, <<"AC62EE99DA068F43C50DC69ACF700E03A62A348360126260E87F2B54ECED86B2">>},
|
||||
{<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>},
|
||||
{<<"mimerl">>, <<"993F9B0E084083405ED8252B99460C4F0563E41729AB42D9074FD5E52439BE88">>},
|
||||
{<<"ranch">>, <<"E4965A144DC9FBE70E5C077C65E73C57165416A901BD02EA899CFD95AA890986">>},
|
||||
{<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]}
|
||||
].
|
17
src/woody_user_identity.app.src
Normal file
17
src/woody_user_identity.app.src
Normal file
@ -0,0 +1,17 @@
|
||||
{application, woody_user_identity,[
|
||||
{description, "A library to manipulate user identity through woody context"},
|
||||
{vsn, "0.1.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[
|
||||
kernel,
|
||||
stdlib,
|
||||
woody
|
||||
]},
|
||||
{env,[]},
|
||||
{modules, []},
|
||||
|
||||
{maintainers, []},
|
||||
{licenses, []},
|
||||
{links, []}
|
||||
]}.
|
105
src/woody_user_identity.erl
Normal file
105
src/woody_user_identity.erl
Normal file
@ -0,0 +1,105 @@
|
||||
-module(woody_user_identity).
|
||||
|
||||
%% API exports
|
||||
-export([put/2]).
|
||||
-export([get/1]).
|
||||
|
||||
-type id() :: binary().
|
||||
-type email() :: binary().
|
||||
-type username() :: binary().
|
||||
|
||||
-type user_identity() :: #{
|
||||
id := id(),
|
||||
email => email(),
|
||||
username => username()
|
||||
}.
|
||||
|
||||
-export_type([id/0]).
|
||||
-export_type([email/0]).
|
||||
-export_type([username/0]).
|
||||
-export_type([user_identity/0]).
|
||||
|
||||
-define(PREFIX, <<"user-identity.">>).
|
||||
|
||||
-type key() :: atom().
|
||||
-type rules() :: [{required, boolean()}].
|
||||
|
||||
%%====================================================================
|
||||
%% API functions
|
||||
%%====================================================================
|
||||
|
||||
-spec put(user_identity(), woody_context:ctx()) -> woody_context:ctx() | no_return().
|
||||
put(Identity, Context) ->
|
||||
Meta = prepare_meta(Identity),
|
||||
woody_context:add_meta(Context, Meta).
|
||||
|
||||
-spec get(woody_context:ctx()) -> user_identity() | no_return().
|
||||
get(Context) ->
|
||||
get_user_identity(Context).
|
||||
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
|
||||
-spec prepare_meta(map()) -> map() | no_return().
|
||||
prepare_meta(Identity) ->
|
||||
lists:foldl(
|
||||
fun({Key, Rules}, Acc) ->
|
||||
Required = is_required(Rules),
|
||||
case maps:get(Key, Identity, undefined) of
|
||||
undefined when Required =:= false->
|
||||
Acc;
|
||||
undefined ->
|
||||
missing_required_error(Key);
|
||||
Value ->
|
||||
MetaKey = encode_key(Key),
|
||||
Acc#{MetaKey => Value}
|
||||
end
|
||||
end,
|
||||
#{},
|
||||
get_keys_info()
|
||||
).
|
||||
|
||||
-spec get_user_identity(woody_context:ctx()) -> user_identity() | no_return().
|
||||
get_user_identity(Context) ->
|
||||
lists:foldl(
|
||||
fun({Key, Rules}, Acc) ->
|
||||
MetaKey = encode_key(Key),
|
||||
Required = is_required(Rules),
|
||||
case woody_context:get_meta(MetaKey, Context) of
|
||||
undefined when Required =:= false->
|
||||
Acc;
|
||||
undefined ->
|
||||
missing_required_error(Key);
|
||||
Value ->
|
||||
Acc#{Key => Value}
|
||||
end
|
||||
end,
|
||||
#{},
|
||||
get_keys_info()
|
||||
).
|
||||
|
||||
-spec get_keys_info() -> [{key(), rules()}].
|
||||
get_keys_info() ->
|
||||
[
|
||||
{id, [{required, true}]},
|
||||
{email, []},
|
||||
{username, []}
|
||||
].
|
||||
|
||||
-spec is_required(rules()) -> boolean().
|
||||
is_required(Rules) ->
|
||||
case proplists:get_value(required, Rules, false) of
|
||||
true ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
-spec encode_key(key()) -> binary().
|
||||
encode_key(Key) when is_atom(Key) ->
|
||||
<<(?PREFIX)/binary, (genlib:to_binary(Key))/binary>>.
|
||||
|
||||
-spec missing_required_error(key()) -> no_return().
|
||||
missing_required_error(Key) ->
|
||||
throw({missing_required, Key}).
|
89
test/woody_user_identity_SUITE.erl
Normal file
89
test/woody_user_identity_SUITE.erl
Normal file
@ -0,0 +1,89 @@
|
||||
-module(woody_user_identity_SUITE).
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
|
||||
%% common test API
|
||||
-export([
|
||||
all/0,
|
||||
init_per_suite/1,
|
||||
end_per_suite/1
|
||||
]).
|
||||
|
||||
-export([
|
||||
put_get_ok_test/1,
|
||||
put_get_incomplete_ok_test/1,
|
||||
put_missing_required_error_test/1,
|
||||
get_missing_required_error_test/1
|
||||
]).
|
||||
|
||||
%%
|
||||
%% tests descriptions
|
||||
%%
|
||||
|
||||
-spec all() -> _.
|
||||
all() ->
|
||||
[
|
||||
put_get_ok_test,
|
||||
put_get_incomplete_ok_test,
|
||||
put_missing_required_error_test,
|
||||
get_missing_required_error_test
|
||||
].
|
||||
|
||||
%%
|
||||
%% starting/stopping
|
||||
%%
|
||||
|
||||
-spec init_per_suite(_) -> _.
|
||||
init_per_suite(C) ->
|
||||
{ok, Apps} = application:ensure_all_started(woody),
|
||||
[{apps, Apps}|C].
|
||||
|
||||
-spec end_per_suite(_) -> _.
|
||||
end_per_suite(C) ->
|
||||
[application:stop(App) || App <- proplists:get_value(apps, C)].
|
||||
|
||||
%%
|
||||
%% tests
|
||||
%%
|
||||
|
||||
-spec put_get_ok_test(_) -> _.
|
||||
put_get_ok_test(_) ->
|
||||
Context0 = woody_context:new(),
|
||||
Identity = #{
|
||||
id => <<"UserID">>,
|
||||
email => <<"UserEmail">>,
|
||||
username => <<"UserName">>
|
||||
},
|
||||
Context = woody_user_identity:put(Identity, Context0),
|
||||
Identity = woody_user_identity:get(Context).
|
||||
|
||||
-spec put_get_incomplete_ok_test(_) -> _.
|
||||
put_get_incomplete_ok_test(_) ->
|
||||
Context0 = woody_context:new(),
|
||||
Identity = #{id => <<"UserID">>},
|
||||
Context = woody_user_identity:put(Identity, Context0),
|
||||
Identity = woody_user_identity:get(Context).
|
||||
|
||||
-spec put_missing_required_error_test(_) -> _.
|
||||
put_missing_required_error_test(_) ->
|
||||
Context0 = woody_context:new(),
|
||||
Identity = #{email => <<"test@test.com">>},
|
||||
ok = try
|
||||
woody_user_identity:put(Identity, Context0),
|
||||
error
|
||||
catch
|
||||
throw:{missing_required, id} ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec get_missing_required_error_test(_) -> _.
|
||||
get_missing_required_error_test(_) ->
|
||||
Context = woody_context:new(),
|
||||
ok = try
|
||||
woody_user_identity:get(Context),
|
||||
error
|
||||
catch
|
||||
throw:{missing_required, id} ->
|
||||
ok
|
||||
end.
|
Loading…
Reference in New Issue
Block a user