diff --git a/core/kazoo_data/src/kzs_doc.erl b/core/kazoo_data/src/kzs_doc.erl index 3944c396666..71922ef9445 100644 --- a/core/kazoo_data/src/kzs_doc.erl +++ b/core/kazoo_data/src/kzs_doc.erl @@ -202,7 +202,7 @@ prepare_publish(JObj) -> -spec maybe_tombstone(kz_json:object()) -> kz_json:object(). maybe_tombstone(JObj) -> - maybe_tombstone(JObj, kz_json:is_true(<<"_deleted">>, JObj, 'false')). + maybe_tombstone(JObj, kz_doc:is_deleted(JObj)). -spec maybe_tombstone(kz_json:object(), boolean()) -> kz_json:object(). maybe_tombstone(JObj, 'true') -> diff --git a/core/kazoo_number_manager/src/knm_locality.erl b/core/kazoo_number_manager/src/knm_locality.erl index 48e79d2d78a..db1767add57 100644 --- a/core/kazoo_number_manager/src/knm_locality.erl +++ b/core/kazoo_number_manager/src/knm_locality.erl @@ -20,7 +20,7 @@ fetch(Numbers) when is_list(Numbers)-> case knm_config:locality_url() of 'undefined' -> - lager:error("could not get number locality url"), + ?LOG_WARNING("could not get number locality url"), {'error', 'missing_url'}; Url -> Resp = fetch_req(Numbers, Url), diff --git a/core/kazoo_number_manager/src/knm_phone_number.erl b/core/kazoo_number_manager/src/knm_phone_number.erl index db76453edff..10da1f7a0f1 100644 --- a/core/kazoo_number_manager/src/knm_phone_number.erl +++ b/core/kazoo_number_manager/src/knm_phone_number.erl @@ -193,8 +193,11 @@ fetch(T0=#{todo := Nums, options := Options}) -> Pairs = group_by_db(lists:usort(knm_converters:normalize(Nums))), F = fun (NumberDb, NormalizedNums, T) -> case fetch_in(NumberDb, NormalizedNums, Options) of + {'error', 'not_found'=R} -> + ?LOG_INFO("bulk read failed to find nums ~s", [kz_binary:join(NormalizedNums)]), + knm_numbers:ko(NormalizedNums, R, T); {'error', R} -> - lager:error("bulk read failed (~p): ~p", [R, NormalizedNums]), + ?LOG_WARNING("bulk read failed (~p): ~p", [R, NormalizedNums]), knm_numbers:ko(NormalizedNums, R, T); {'ok', JObjs} when is_list(JObjs) -> bulk_fetch(T, JObjs); {'ok', JObj} -> do_handle_fetch(T, JObj) @@ -224,60 +227,79 @@ fetch_in(NumberDb, Nums, Options) -> -endif. bulk_fetch(T0, JObjs) -> - F = fun (JObj, T) -> - Num = kz_json:get_ne_value(<<"key">>, JObj), - case kz_json:get_ne_value(<<"doc">>, JObj) of - 'undefined' -> - R = kz_json:get_ne_value(<<"error">>, JObj), - lager:warning("failed reading ~s: ~p", [Num, R]), - knm_numbers:ko(Num, kz_term:to_atom(R, 'true'), T); - Doc -> - do_handle_fetch(T, Doc) - end - end, - lists:foldl(F, T0, JObjs). + lists:foldl(fun bulk_fetch_fold/2, T0, JObjs). + +bulk_fetch_fold(JObj, T) -> + Num = kz_json:get_ne_value(<<"key">>, JObj), + case kz_json:get_ne_json_value(<<"doc">>, JObj) of + 'undefined' -> + R = kz_json:get_ne_value(<<"error">>, JObj), + ?LOG_WARNING("failed reading ~s: ~p", [Num, R]), + knm_numbers:ko(Num, kz_term:to_atom(R, 'true'), T); + Doc -> + do_handle_fetch(T, Doc) + end. + +bulk_change_fold(JObj, T, Db, PNsMap, ErrorF) -> + Num = kz_json:get_ne_value(<<"id">>, JObj), + case kz_json:is_true(<<"ok">>, JObj) =:= 'true' + orelse kz_doc:revision(JObj) =/= 'undefined' + of + 'true' -> + ?LOG_DEBUG("successfully changed ~s in ~s", [Num, Db]), + knm_numbers:ok(maps:get(Num, PNsMap), T); + 'false' -> + %% Weirdest thing here is on conflict doc was actually properly saved! + R = kz_json:get_ne_value(<<"error">>, JObj), + ?LOG_WARNING("error changing ~s in ~s: ~s", [Num, Db, kz_json:encode(JObj)]), + ErrorF(Num, kz_term:to_atom(R, 'true'), T) + end. %% @doc Works the same with the output of save_docs and del_docs %% @end -handle_bulk_change(Db, JObjs, PNsMap, T0, ErrorF) +-spec handle_bulk_change(kz_term:ne_binary(), kz_json:objects(), list() | map(), map(), 'save' | 'delete', fun()) -> + map(). +handle_bulk_change(Db, JObjs, PNsMap, T0, SaveType, ErrorF) when is_map(PNsMap) -> - F = fun (JObj, T) -> - Num = kz_json:get_ne_value(<<"id">>, JObj), - case kz_json:get_ne_value(<<"ok">>, JObj) =:= 'true' - orelse kz_doc:revision(JObj) =/= 'undefined' - of - 'true' -> - lager:debug("successfully changed ~s in ~s", [Num, Db]), - knm_numbers:ok(maps:get(Num, PNsMap), T); - 'false' -> - %% Weirdest thing here is on conflict doc was actually properly saved! - R = kz_json:get_ne_value(<<"error">>, JObj), - lager:warning("error changing ~s in ~s: ~s", [Num, Db, kz_json:encode(JObj)]), - ErrorF(Num, kz_term:to_atom(R, 'true'), T) - end - end, - retry_conflicts(lists:foldl(F, T0, JObjs), Db, PNsMap, ErrorF); -handle_bulk_change(Db, JObjs, PNs, T, ErrorF) -> + F = fun(JObj, T) -> bulk_change_fold(JObj, T, Db, PNsMap, ErrorF) end, + retry_conflicts(lists:foldl(F, T0, JObjs) + ,Db + ,PNsMap + ,SaveType + ,ErrorF + ); +handle_bulk_change(Db, JObjs, PNs, T, SaveType, ErrorF) -> PNsMap = group_by_num(PNs), - handle_bulk_change(Db, JObjs, PNsMap, T, ErrorF). + handle_bulk_change(Db, JObjs, PNsMap, T, SaveType, ErrorF). -handle_bulk_change(Db, JObjs, PNs, T) -> +handle_bulk_change(Db, JObjs, PNs, T, SaveType) -> ErrorF = fun database_error/3, - handle_bulk_change(Db, JObjs, PNs, T, ErrorF). + handle_bulk_change(Db, JObjs, PNs, T, SaveType, ErrorF). %% On delete there won't be conflicts. -retry_conflicts(T0, Db, PNsMap, ErrorF) -> +-spec retry_conflicts(map(), kz_term:ne_binary(), map(), 'save' | 'delete', fun()) -> + map(). +retry_conflicts(T0, Db, PNsMap, SaveType, ErrorF) -> {Conflicts, BaseT} = take_conflits(T0), - F = fun (Num, T) -> - lager:error("~s conflicted, retrying", [Num]), - PN = maps:get(Num, PNsMap), - case kz_datamgr:ensure_saved(Db, to_json(PN)) of - {'ok', _} -> knm_numbers:ok(PN, T); - {'error', R} -> ErrorF(Num, R, T) - end - end, + + F = fun(Num, T) -> retry_conflicts_fold(Num, T, Db, PNsMap, SaveType, ErrorF) end, lists:foldl(F, BaseT, Conflicts). +retry_conflicts_fold(Num, T, Db, PNsMap, 'save', ErrorF) -> + ?LOG_ERROR("~s conflicted, retrying", [Num]), + PN = maps:get(Num, PNsMap), + PNJObj = to_json(PN), + case kz_datamgr:ensure_saved(Db, PNJObj) of + {'ok', _Saved} -> knm_numbers:ok(PN, T); + {'error', R} -> ErrorF(Num, R, T) + end; +retry_conflicts_fold(Num, T, Db, PNsMap, 'delete', ErrorF) -> + ?LOG_ERROR("~s conflicted, deleting from ~s again", [Num, Db]), + case kz_datamgr:del_doc(Db, Num) of + {'ok', _Deleted} -> knm_numbers:ok(maps:get(Num, PNsMap), T); + {'error', R} -> ErrorF(Num, R, T) + end. + take_conflits(T=#{ko := KOs}) -> F = fun ({_Num, R}) when is_atom(R) -> 'false'; ({_Num, R}) -> knm_errors:cause(R) =:= <<"conflict">> @@ -293,54 +315,62 @@ do_handle_fetch(T=#{options := Options}, Doc) -> end. group_by_db(Nums) -> - F = fun (Num, M) -> - Key = knm_converters:to_db(Num), - M#{Key => [Num | maps:get(Key, M, [])]} - end, - lists:foldl(F, #{}, Nums). + lists:foldl(fun group_by_db_fold/2, #{}, Nums). + +group_by_db_fold(Num, M) -> + Key = knm_converters:to_db(Num), + M#{Key => [Num | maps:get(Key, M, [])]}. group_by_num(PNs) -> - F = fun (PN, M) -> M#{number(PN) => PN} end, - lists:foldl(F, #{}, PNs). + lists:foldl(fun group_by_num_fold/2, #{}, PNs). + +group_by_num_fold(PN, M) -> M#{number(PN) => PN}. split_by_numberdb(PNs) -> - F = fun (PN, M) -> - Key = number_db(PN), - M#{Key => [PN | maps:get(Key, M, [])]} - end, - lists:foldl(F, #{}, PNs). + lists:foldl(fun group_by_number_db_fold/2, #{}, PNs). + +group_by_number_db_fold(PN, M) -> + Key = number_db(PN), + M#{Key => [PN | maps:get(Key, M, [])]}. +-spec split_by_assignedto([knm_phone_number()]) -> + #{kz_term:api_ne_binary() => [knm_phone_number()]}. split_by_assignedto(PNs) -> - F = fun (PN, M) -> - AssignedTo = assigned_to(PN), - Key = case kz_term:is_empty(AssignedTo) of - 'true' -> 'undefined'; - 'false' -> existing_db_key(kz_util:format_account_db(AssignedTo)) - end, - M#{Key => [PN | maps:get(Key, M, [])]} - end, - lists:foldl(F, #{}, PNs). + lists:foldl(fun split_by_assignedto_fold/2, #{}, PNs). + +-spec split_by_assignedto_fold(knm_phone_number(), #{kz_term:api_ne_binary() => [knm_phone_number()]}) -> + #{kz_term:api_ne_binary() => [knm_phone_number()]}. +split_by_assignedto_fold(#knm_phone_number{}=PN, MapAcc) -> + split_by_assignedto_fold(PN, MapAcc, assigned_to(PN)). + +split_by_assignedto_fold(PN, MapAcc, 'undefined') -> + MapAcc#{'undefined' => [PN | maps:get('undefined', MapAcc, [])]}; +split_by_assignedto_fold(PN, MapAcc, AssignedTo) -> + AccountDb = kz_util:format_account_db(AssignedTo), + case existing_db_key(AccountDb) of + 'undefined' -> + MapAcc#{'undefined' => [PN | maps:get('undefined', MapAcc, [])]}; + AccountDb -> + MapAcc#{AccountDb => [PN | maps:get(AccountDb, MapAcc, [])]} + end. split_by_prevassignedto(PNs) -> - F = fun (PN, M) -> - PrevAssignedTo = prev_assigned_to(PN), - PrevIsCurrent = assigned_to(PN) =:= PrevAssignedTo, - Key = case PrevIsCurrent - orelse kz_term:is_empty(PrevAssignedTo) - of - 'true' when PrevIsCurrent -> - lager:debug("~s prev_assigned_to is same as assigned_to," - " not unassign-ing from prev", [number(PN)] - ), - 'undefined'; - 'true' -> - lager:debug("prev_assigned_to is empty for ~s, ignoring", [number(PN)]), - 'undefined'; - 'false' -> existing_db_key(kz_util:format_account_db(PrevAssignedTo)) - end, - M#{Key => [PN | maps:get(Key, M, [])]} - end, - lists:foldl(F, #{}, PNs). + lists:foldl(fun split_by_prev_assigned_to_fold/2, #{}, PNs). + +split_by_prev_assigned_to_fold(PN, M) -> + split_by_prev_assigned_to_fold(PN, M, prev_assigned_to(PN), assigned_to(PN)). + +split_by_prev_assigned_to_fold(PN, M, 'undefined', 'undefined') -> + ?LOG_DEBUG("prev_assigned_to is empty for ~s, ignoring", [number(PN)]), + M#{'undefined' => [PN | maps:get('undefined', M, [])]}; +split_by_prev_assigned_to_fold(PN, M, PrevAssignedTo, PrevAssignedTo) -> + ?LOG_DEBUG("~s prev_assigned_to is same as assigned_to," + " not unassign-ing from prev", [number(PN)] + ), + M#{'undefined' => [PN | maps:get('undefined', M, [])]}; +split_by_prev_assigned_to_fold(PN, M, PrevAssignedTo, _AssignedTo) -> + Key = existing_db_key(kz_util:format_account_db(PrevAssignedTo)), + M#{Key => [PN | maps:get(Key, M, [])]}. -spec existing_db_key(kz_term:ne_binary()) -> kz_term:api_ne_binary(). -ifdef(TEST). @@ -454,7 +484,7 @@ handle_fetch(JObj, Options) -> end. is_mdn_for_mdn_run(#knm_phone_number{auth_by = ?KNM_DEFAULT_AUTH_BY}, _) -> - lager:debug("mdn check disabled by auth_by"), + ?LOG_DEBUG("mdn check disabled by auth_by"), 'true'; is_mdn_for_mdn_run(PN, IsMDNRun) -> IsMDN = ?CARRIER_MDN =:= module_name(PN), @@ -496,7 +526,7 @@ save(T0) -> take_not_to_save(T0=#{todo := PNs, options := Options}) -> case knm_number_options:dry_run(Options) of 'true' -> - lager:debug("dry_run-ing btw"), + ?LOG_DEBUG("dry_run-ing btw"), T = T0#{todo => [], ok => []}, {T, PNs}; 'false' -> @@ -516,9 +546,9 @@ take_not_to_save_fold(PN, {T, NotToSave}) -> end. log_why_not_to_save(true, _Num) -> - lager:debug("not dirty, skip saving ~s", [_Num]); + ?LOG_DEBUG("not dirty, skip saving ~s", [_Num]); log_why_not_to_save(false, _Num) -> - lager:debug("deleted, skip saving ~s", [_Num]). + ?LOG_DEBUG("deleted, skip saving ~s", [_Num]). %%------------------------------------------------------------------------------ %% @doc To call only from knm_numbers:delete/2 (only for sysadmins). @@ -528,7 +558,7 @@ log_why_not_to_save(false, _Num) -> delete(T=#{todo := PNs, options := Options}) -> case knm_number_options:dry_run(Options) of 'true' -> - lager:debug("dry_run-ing btw, not deleting anything"), + ?LOG_DEBUG("dry_run-ing btw, not deleting anything"), knm_numbers:ok(PNs, T); 'false' -> knm_numbers:pipe(T, [fun log_permanent_deletion/1 @@ -588,7 +618,7 @@ to_public_json(PN) -> %% @end %%------------------------------------------------------------------------------ -spec to_json(knm_phone_number()) -> kz_json:object(). -to_json(PN=#knm_phone_number{doc=JObj}) -> +to_json(#knm_phone_number{doc=JObj}=PN) -> kz_json:from_list( [{<<"_id">>, number(PN)} ,{?PVT_DB_NAME, number_db(PN)} @@ -982,10 +1012,10 @@ used_by(#knm_phone_number{used_by=UsedBy}) -> UsedBy. -spec set_used_by(knm_phone_number(), kz_term:api_ne_binary()) -> knm_phone_number(). set_used_by(PN=#knm_phone_number{used_by = V}, V) -> PN; set_used_by(PN, UsedBy='undefined') -> - lager:debug("unassigning ~s from ~s", [number(PN), PN#knm_phone_number.used_by]), + ?LOG_DEBUG("unassigning ~s from ~s", [number(PN), PN#knm_phone_number.used_by]), ?DIRTY(PN#knm_phone_number{used_by = UsedBy}); set_used_by(PN, UsedBy=?NE_BINARY) -> - lager:debug("assigning ~s to ~s from ~s", [number(PN), UsedBy, PN#knm_phone_number.used_by]), + ?LOG_DEBUG("assigning ~s to ~s from ~s", [number(PN), UsedBy, PN#knm_phone_number.used_by]), ?DIRTY(PN#knm_phone_number{used_by = UsedBy}). %%------------------------------------------------------------------------------ @@ -1026,7 +1056,7 @@ set_feature(PN0, Feature=?NE_BINARY, Data) -> end, PN = set_features(PN0, kz_json:set_value(Feature, Data, Features)), PN#knm_phone_number.is_dirty - andalso lager:debug("setting ~s feature ~s: ~s", [number(PN), Feature, kz_json:encode(Data)]), + andalso ?LOG_DEBUG("setting ~s feature ~s: ~s", [number(PN), Feature, kz_json:encode(Data)]), PN. -spec reset_features(knm_phone_number()) -> knm_phone_number(). @@ -1110,7 +1140,7 @@ set_state(PN=#knm_phone_number{state = 'undefined'}, State) -> PN#knm_phone_number{state = State}; set_state(PN, State) -> 'true' = is_state(State), - lager:debug("updating state from ~s to ~s", [PN#knm_phone_number.state, State]), + ?LOG_DEBUG("updating state from ~s to ~s", [PN#knm_phone_number.state, State]), ?DIRTY(PN#knm_phone_number{state = State}). -spec is_state(any()) -> boolean(). @@ -1205,7 +1235,7 @@ set_ported_in(PN=#knm_phone_number{ported_in = 'undefined'}, Ported) when is_boolean(Ported) -> PN#knm_phone_number{ported_in = Ported}; set_ported_in(PN, Ported) when is_boolean(Ported) -> - lager:debug("updating ported_in from ~s to ~s", [PN#knm_phone_number.ported_in, Ported]), + ?LOG_DEBUG("updating ported_in from ~s to ~s", [PN#knm_phone_number.ported_in, Ported]), ?DIRTY(PN#knm_phone_number{ported_in = Ported}). %%------------------------------------------------------------------------------ @@ -1248,7 +1278,7 @@ set_module_name(PN0=#knm_phone_number{module_name = 'undefined', features = Feat set_features(PN, NewFeatures); set_module_name(PN0, Name=?NE_BINARY) -> - lager:debug("updating module_name from ~p to ~p", [PN0#knm_phone_number.module_name, Name]), + ?LOG_DEBUG("updating module_name from ~p to ~p", [PN0#knm_phone_number.module_name, Name]), PN = ?DIRTY(PN0#knm_phone_number{module_name = Name}), Features = kz_json:delete_key(?FEATURE_LOCAL, features(PN)), set_features(PN, Features). @@ -1258,7 +1288,7 @@ set_module_name_local(PN0=#knm_phone_number{module_name = 'undefined'}, Name) -> PN = set_feature(PN0, ?FEATURE_LOCAL, local_feature(PN0)), PN#knm_phone_number{module_name = Name}; set_module_name_local(PN0, Name) -> - lager:debug("updating module_name from ~p to ~p", [PN0#knm_phone_number.module_name, Name]), + ?LOG_DEBUG("updating module_name from ~p to ~p", [PN0#knm_phone_number.module_name, Name]), PN = set_feature(PN0, ?FEATURE_LOCAL, local_feature(PN0)), ?DIRTY(PN#knm_phone_number{module_name = Name}). @@ -1311,10 +1341,10 @@ set_region(PN=#knm_phone_number{region = V}, V) -> PN; set_region(PN=#knm_phone_number{region = 'undefined'}, Region=?NE_BINARY) -> PN#knm_phone_number{region = Region}; set_region(PN, Region='undefined') -> - lager:debug("updating region from ~s to ~s", [PN#knm_phone_number.region, Region]), + ?LOG_DEBUG("updating region from ~s to ~s", [PN#knm_phone_number.region, Region]), ?DIRTY(PN#knm_phone_number{region = Region}); set_region(PN, Region=?NE_BINARY) -> - lager:debug("updating region from ~s to ~s", [PN#knm_phone_number.region, Region]), + ?LOG_DEBUG("updating region from ~s to ~s", [PN#knm_phone_number.region, Region]), ?DIRTY(PN#knm_phone_number{region = Region}). %%------------------------------------------------------------------------------ @@ -1345,7 +1375,7 @@ is_admin(_) -> 'false'. -else. is_admin(#knm_phone_number{auth_by=AuthBy}) -> is_admin(AuthBy); is_admin(?KNM_DEFAULT_AUTH_BY) -> - lager:info("bypassing auth"), + ?LOG_INFO("bypassing auth"), 'true'; is_admin(AuthBy) -> kzd_accounts:is_superduper_admin(AuthBy). @@ -1519,9 +1549,11 @@ remove_denied_features(PN) -> RemoveFromPvt = lists:usort(lists:flatmap(fun remove_in_private/1, DeniedFeatures)), RemoveFromPub = lists:usort(lists:flatmap(fun remove_in_public/1, DeniedFeatures)), ?LOG_WARNING("removing out of sync pvt features: ~s" - ,[kz_util:iolist_join($,, lists:usort([ToRm || [ToRm|_] <- RemoveFromPvt]))]), + ,[kz_util:iolist_join($,, lists:usort([ToRm || [ToRm|_] <- RemoveFromPvt]))] + ), ?LOG_WARNING("removing out of sync pub features: ~s" - ,[kz_util:iolist_join($,, lists:usort([ToRm || [ToRm|_] <- RemoveFromPub]))]), + ,[kz_util:iolist_join($,, lists:usort([ToRm || [ToRm|_] <- RemoveFromPub]))] + ), NewPvt = kz_json:prune_keys(RemoveFromPvt, features(PN)), NewPub = kz_json:prune_keys(RemoveFromPub, doc(PN)), Updates = [{fun set_features/2, NewPvt} @@ -1615,7 +1647,7 @@ is_authorized(#knm_phone_number{assigned_to = 'undefined' ,assign_to = 'undefined' ,auth_by = AuthBy }) -> - lager:debug("assigns all 'undefined', checking if auth is super duper"), + ?LOG_DEBUG("assigns all 'undefined', checking if auth is super duper"), is_admin(AuthBy); is_authorized(#knm_phone_number{assigned_to = 'undefined' ,assign_to = AssignTo @@ -1739,23 +1771,24 @@ try_delete_from(SplitBy, T0) -> -spec try_delete_from(fun(), knm_numbers:collection(), boolean()) -> knm_numbers:collection(). try_delete_from(SplitBy, T0, IgnoreDbNotFound) -> - F = fun ('undefined', PNs, T) -> + F = fun ('undefined', PNs, TAcc) -> ?LOG_DEBUG("skipping: no db for ~s", [[[number(PN),$\s] || PN <- PNs]]), - knm_numbers:add_oks(PNs, T); - (Db, PNs, T) -> - ?LOG_DEBUG("deleting from ~s", [Db]), - Nums = [to_json(PN) || PN <- PNs], + knm_numbers:add_oks(PNs, TAcc); + (Db, PNs, TAcc) -> + Nums = [number(PN) || PN <- PNs], case delete_docs(Db, Nums) of - {'ok', JObjs} -> handle_bulk_change(Db, JObjs, PNs, T); + {'ok', JObjs} -> + handle_bulk_change(Db, JObjs, PNs, TAcc, 'delete'); {'error', 'not_found'} when IgnoreDbNotFound -> - lager:debug("db ~s does not exist, ignoring", [Db]), - knm_numbers:add_oks(PNs, T); + ?LOG_DEBUG("db ~s does not exist, ignoring", [Db]), + knm_numbers:add_oks(PNs, TAcc); {'error', E} -> - lager:error("failed to delete from ~s (~p): ~p", [Db, E, Nums]), - database_error(Nums, E, T) + ?LOG_ERROR("failed to delete from ~s (~p): ~p", [Db, E, Nums]), + database_error(Nums, E, TAcc) end end, - maps:fold(F, T0, SplitBy(knm_numbers:todo(T0))). + SplitByResults = SplitBy(knm_numbers:todo(T0)), + maps:fold(F, T0, SplitByResults). save_to(SplitBy, ErrorF, T0) -> F = fun FF ('undefined', PNs, T) -> @@ -1768,16 +1801,16 @@ save_to(SplitBy, ErrorF, T0) -> IsNumberDb = 'numbers' =:= kz_datamgr:db_classification(Db), case save_docs(Db, Docs) of {'ok', JObjs} -> - handle_bulk_change(Db, JObjs, PNs, T, ErrorF); + handle_bulk_change(Db, JObjs, PNs, T, 'save', ErrorF); {'error', 'not_found'} when IsNumberDb -> Nums = [kz_doc:id(Doc) || Doc <- Docs], - lager:debug("creating new number db '~s' for numbers ~p", [Db, Nums]), + ?LOG_DEBUG("creating new number db '~s' for numbers ~p", [Db, Nums]), 'true' = kz_datamgr:db_create(Db), _ = kapps_maintenance:refresh(Db), FF(Db, PNs, T); {'error', E} -> Nums = [kz_doc:id(Doc) || Doc <- Docs], - lager:error("failed to assign numbers to ~s (~p): ~p", [Db, E, Nums]), + ?LOG_ERROR("failed to assign numbers to ~s (~p): ~p", [Db, E, Nums]), database_error(Nums, E, T) end end, diff --git a/core/kazoo_number_manager/test/knm_create_new_number_tests.erl b/core/kazoo_number_manager/test/knm_create_new_number_tests.erl index 705532e2c85..8da650354a7 100644 --- a/core/kazoo_number_manager/test/knm_create_new_number_tests.erl +++ b/core/kazoo_number_manager/test/knm_create_new_number_tests.erl @@ -18,8 +18,10 @@ create_new_number_test_() -> } ], {'ok', N} = knm_number:create(?TEST_CREATE_NUM, Props), + JObj = knm_number:to_public_json(N), PN = knm_number:phone_number(N), + [?_assert(knm_phone_number:is_dirty(PN)) ,{"Verify phone number is assigned to reseller account" ,?_assertEqual(?RESELLER_ACCOUNT_ID, knm_phone_number:assigned_to(PN)) @@ -40,10 +42,10 @@ create_new_number_test_() -> ,?_assertEqual(?CARRIER_LOCAL, knm_phone_number:module_name(PN)) } ,{"Verify getting created field returns a number" - ,?_assertEqual(true, is_integer(knm_phone_number:created(PN))) + ,?_assert(is_integer(knm_phone_number:created(PN))) } ,{"Verify the created field is stored as a number" - ,?_assertEqual(true, is_integer(kz_json:get_value([<<"_read_only">>, <<"created">>], JObj))) + ,?_assert(is_integer(kz_json:get_value([<<"_read_only">>, <<"created">>], JObj))) } ]. diff --git a/core/kazoo_proper/src/pqc_cb_accounts.erl b/core/kazoo_proper/src/pqc_cb_accounts.erl index 603447434f9..fa038c4f5de 100644 --- a/core/kazoo_proper/src/pqc_cb_accounts.erl +++ b/core/kazoo_proper/src/pqc_cb_accounts.erl @@ -186,7 +186,7 @@ seq_44832() -> ,lists:seq(1, 4) ), - cleanup(API), + _ = cleanup(API), ?INFO("finished double-POST check"). -spec enable_and_delete_topup() -> 'ok'. @@ -215,7 +215,7 @@ enable_and_delete_topup() -> 'undefined' = kz_json:get_ne_value(<<"topup">>, kz_json:decode(Resp1)), - cleanup(API), + _ = cleanup(API), ?INFO("FINISHED ENABLE AND DISABLE TOPUP CHECKS"). -spec cleanup(pqc_cb_api:state()) -> any(). @@ -236,4 +236,5 @@ topup_request(API, AccountId, RequestEnvelope) -> -spec cleanup() -> 'ok'. cleanup() -> API = pqc_cb_api:init_api(['crossbar'], ['cb_accounts']), - cleanup(API). + _ = cleanup(API), + 'ok'. diff --git a/core/kazoo_proper/src/pqc_cb_phone_numbers.erl b/core/kazoo_proper/src/pqc_cb_phone_numbers.erl index 69bd53aaa12..e060382bb81 100644 --- a/core/kazoo_proper/src/pqc_cb_phone_numbers.erl +++ b/core/kazoo_proper/src/pqc_cb_phone_numbers.erl @@ -8,7 +8,8 @@ -behaviour(proper_statem). %% Crossbar API test functions --export([list_number/3 +-export([summary/2 + ,list_number/3 ,cleanup_numbers/2 ,add_number/3 ,activate_number/3 @@ -25,6 +26,10 @@ ,correct_parallel/0 ]). +-export([seq/0 + ,cleanup/0 + ]). + -include_lib("proper/include/proper.hrl"). -include("kazoo_proper.hrl"). @@ -36,6 +41,13 @@ cleanup_numbers(_API, Numbers) -> _ = knm_numbers:delete(Numbers, [{'auth_by', <<"system">>}]), 'ok'. +-spec summary(pqc_cb_api:state(), kz_term:ne_binary()) -> pqc_cb_api:response(). +summary(API, AccountId) -> + URL = numbers_url(AccountId), + RequestHeaders = pqc_cb_api:request_headers(API), + + pqc_cb_api:make_request([200], fun kz_http:get/2, URL, RequestHeaders). + -spec list_number(pqc_cb_api:state(), kz_term:api_ne_binary(), kz_term:ne_binary()) -> pqc_cb_api:response(). list_number(_API, 'undefined', _Number) -> ?FAILED_RESPONSE; list_number(API, AccountId, Number) -> @@ -44,12 +56,18 @@ list_number(API, AccountId, Number) -> pqc_cb_api:make_request([200, 404], fun kz_http:get/2, URL, RequestHeaders). --spec add_number(pqc_cb_api:state(), kz_term:api_ne_binary(), kz_term:ne_binary()) -> pqc_cb_api:response(). +-spec add_number(pqc_cb_api:state(), kz_term:api_ne_binary(), kz_term:ne_binary()) -> + pqc_cb_api:response(). add_number(_API, 'undefined', _Number) -> ?FAILED_RESPONSE; add_number(API, AccountId, Number) -> + add_number(API, AccountId, Number, kz_json:new()). + +-spec add_number(pqc_cb_api:state(), kz_term:api_ne_binary(), kz_term:ne_binary(), kz_json:object()) -> + pqc_cb_api:response(). +add_number(API, AccountId, Number, RequestData) -> URL = number_url(AccountId, Number), RequestHeaders = pqc_cb_api:request_headers(API), - RequestEnvelope = pqc_cb_api:create_envelope(kz_json:new() + RequestEnvelope = pqc_cb_api:create_envelope(RequestData ,kz_json:from_list([{<<"accept_charges">>, 'true'}]) ), pqc_cb_api:make_request([201, 404, 409] @@ -91,6 +109,12 @@ number_url(AccountId, Number) -> ,"/" ). +-spec numbers_url(kz_term:ne_binary()) -> string(). +numbers_url(AccountId) -> + string:join([pqc_cb_accounts:account_url(AccountId), "phone_numbers"] + ,"/" + ). + -spec number_url(kz_term:ne_binary(), kz_term:ne_binary(), string()) -> string(). number_url(AccountId, Number, PathToken) -> string:join([pqc_cb_accounts:account_url(AccountId) @@ -292,3 +316,71 @@ postcondition(Model -spec precondition(pqc_kazoo_model:model(), any()) -> boolean(). precondition(_Model, _Call) -> 'true'. + +-spec seq() -> 'ok'. +seq() -> + _ = init(), + seq_kzoo_41(). + +seq_kzoo_41() -> + Model = initial_state(), + API = pqc_kazoo_model:api(Model), + + AccountResp = pqc_cb_accounts:create_account(API, hd(?ACCOUNT_NAMES)), + AccountId = kz_json:get_value([<<"data">>, <<"id">>], kz_json:decode(AccountResp)), + lager:info("created account ~s", [AccountId]), + + EmptySummaryResp = summary(API, AccountId), + lager:info("empty summary: ~s", [EmptySummaryResp]), + 'true' = kz_json:is_empty(kz_json:get_json_value([<<"data">>, <<"numbers">>], kz_json:decode(EmptySummaryResp))), + + PhoneNumber = hd(?PHONE_NUMBERS), + + CreateResp = add_number(API, AccountId, PhoneNumber, kz_json:from_list([{<<"carrier_name">>, <<"knm_other">>}])), + lager:info("create resp ~p", [CreateResp]), + NumberDoc = kz_json:get_json_value(<<"data">>, kz_json:decode(CreateResp)), + <> = kz_doc:id(NumberDoc), + + SummaryResp = summary(API, AccountId), + lager:info("summary resp: ~s", [SummaryResp]), + SummaryJObj = kz_json:get_json_value([<<"data">>, <<"numbers">>, PhoneNumber], kz_json:decode(SummaryResp)), + 'false' = kz_json:is_empty(SummaryJObj), + + RemoveResp = remove_number(API, AccountId, PhoneNumber), + lager:info("removed resp: ~s", [RemoveResp]), + + EmptyAgainResp = summary(API, AccountId), + lager:info("empty again: ~s", [EmptyAgainResp]), + EmptyAgainJObj = kz_json:decode(EmptyAgainResp), + lager:info("payload: ~p", [EmptyAgainJObj]), + EmptyData = kz_json:get_json_value([<<"data">>, <<"numbers">>], EmptyAgainJObj), + lager:info("empty again data: ~p", [EmptyData]), + 'true' = kz_json:is_empty(EmptyData), + + cleanup(API). + +init() -> + _ = kz_data_tracing:clear_all_traces(), + _ = [kapps_controller:start_app(App) || + App <- ['crossbar'] + ], + _ = [crossbar_maintenance:start_module(Mod) || + Mod <- ['cb_phone_numbers_v2'] + ], + lager:info("INIT FINISHED"). + +-spec cleanup() -> any(). +cleanup() -> + lager:info("CLEANUP ALL THE THINGS"), + kz_data_tracing:clear_all_traces(), + cleanup(pqc_cb_api:authenticate()). + +-spec cleanup(pqc_cb_api:state()) -> any(). +cleanup(API) -> + lager:info("CLEANUP TIME, EVERYBODY HELPS"), + lager:info("~p", [API]), + _ = pqc_cb_accounts:cleanup_accounts(API, ?ACCOUNT_NAMES), + _D = knm_numbers:delete(?PHONE_NUMBERS, [{'auth_by', pqc_cb_api:auth_account_id(API)}]), + lager:info("deleted numbers ~p", [_D]), + pqc_cb_api:cleanup(API), + 'ok'.