./ct_report/coverage/mongoose_component.COVER.html

1 -module(mongoose_component).
2 %% API
3 -export([has_component/1,
4 dirty_get_all_components/1,
5 register_components/4,
6 unregister_components/1,
7 lookup_component/1,
8 lookup_component/2]).
9
10 -export([start/0, stop/0]).
11 -export([node_cleanup/3]).
12
13 -include("mongoose.hrl").
14 -include("jlib.hrl").
15 -include("external_component.hrl").
16
17 -type domain() :: jid:server().
18
19 -type external_component() :: #external_component{domain :: domain(),
20 handler :: mongoose_packet_handler:t(),
21 node :: node(),
22 is_hidden :: boolean()}.
23
24 -export_type([external_component/0]).
25
26 % Not simple boolean() because is probably going to support third value in the future: only_hidden.
27 % Besides, it increases readability.
28 -type return_hidden() :: only_public | all.
29
30 -export_type([return_hidden/0]).
31
32 %%====================================================================
33 %% API
34 %%====================================================================
35
36 start() ->
37 104 Backend = mongoose_config:get_opt(component_backend),
38 104 mongoose_component_backend:init(#{backend => Backend}),
39 104 gen_hook:add_handlers(hooks()).
40
41 stop() ->
42
:-(
gen_hook:delete_handlers(hooks()).
43
44 -spec hooks() -> [gen_hook:hook_tuple()].
45 hooks() ->
46 104 [{node_cleanup, global, fun ?MODULE:node_cleanup/3, #{}, 90}].
47
48 -spec register_components(Domain :: [domain()],
49 Node :: node(),
50 Handler :: mongoose_packet_handler:t(),
51 AreHidden :: boolean()) -> {ok, [external_component()]} | {error, any()}.
52 register_components(Domains, Node, Handler, AreHidden) ->
53 31 try
54 31 register_components_unsafe(Domains, Node, Handler, AreHidden)
55 catch Class:Reason:Stacktrace ->
56 3 ?LOG_ERROR(#{what => component_register_failed,
57
:-(
class => Class, reason => Reason, stacktrace => Stacktrace}),
58 3 {error, Reason}
59 end.
60
61 register_components_unsafe(Domains, Node, Handler, AreHidden) ->
62 31 LDomains = prepare_ldomains(Domains),
63 31 Components = make_components(LDomains, Node, Handler, AreHidden),
64 31 assert_can_register_components(Components),
65 28 register_components(Components),
66 %% We do it outside of Mnesia transaction
67 28 lists:foreach(fun run_register_hook/1, Components),
68 28 {ok, Components}.
69
70 register_components(Components) ->
71 28 mongoose_component_backend:register_components(Components).
72
73 make_components(LDomains, Node, Handler, AreHidden) ->
74 31 [make_record_component(LDomain, Handler, Node, AreHidden) || LDomain <- LDomains].
75
76 make_record_component(LDomain, Handler, Node, IsHidden) ->
77 32 #external_component{domain = LDomain, handler = Handler,
78 node = Node, is_hidden = IsHidden}.
79
80 run_register_hook(#external_component{domain = LDomain, is_hidden = IsHidden}) ->
81 29 mongoose_hooks:register_subhost(LDomain, IsHidden),
82 29 ok.
83
84 run_unregister_hook(#external_component{domain = LDomain}) ->
85 29 mongoose_hooks:unregister_subhost(LDomain),
86 29 ok.
87
88 -spec unregister_components(Components :: [external_component()]) -> ok.
89 unregister_components(Components) ->
90 28 lists:foreach(fun run_unregister_hook/1, Components),
91 28 mongoose_component_backend:unregister_components(Components).
92
93 assert_can_register_components(Components) ->
94 31 ConflictComponents = lists:filter(fun is_already_registered/1, Components),
95 31 ConflictDomains = records_to_domains(ConflictComponents),
96 31 case ConflictDomains of
97 [] ->
98 28 ok;
99 _ ->
100 3 error({routes_already_exist, ConflictDomains})
101 end.
102
103 records_to_domains(Components) ->
104 31 [LDomain || #external_component{domain = LDomain} <- Components].
105
106 %% Returns true if any component route is registered for the domain.
107 -spec has_component(jid:lserver()) -> boolean().
108 has_component(Domain) ->
109
:-(
[] =/= lookup_component(Domain).
110
111 %% @doc Check if the component/route is already registered somewhere.
112 -spec is_already_registered(external_component()) -> boolean().
113 is_already_registered(#external_component{domain = LDomain, node = Node}) ->
114 32 has_dynamic_domains(LDomain)
115 31 orelse has_domain_route(LDomain)
116 31 orelse has_component_registered(LDomain, Node).
117
118 has_dynamic_domains(LDomain) ->
119 32 {error, not_found} =/= mongoose_domain_api:get_host_type(LDomain).
120
121 %% check that route for this domain is not already registered
122 has_domain_route(LDomain) ->
123 31 no_route =/= mongoose_router:lookup_route(LDomain).
124
125 %% check that there is no component registered globally for this node
126 has_component_registered(LDomain, Node) ->
127 31 no_route =/= get_component(LDomain, Node).
128
129 %% Find a component registered globally for this node (internal use)
130 get_component(LDomain, Node) ->
131 31 filter_component(lookup_component(LDomain), Node).
132
133 filter_component([], _) ->
134 29 no_route;
135 filter_component([Comp|Tail], Node) ->
136 3 case Comp of
137 #external_component{node = Node} ->
138 2 Comp;
139 _ ->
140 1 filter_component(Tail, Node)
141 end.
142
143 %% @doc Returns a list of components registered for this domain by any node,
144 %% the choice is yours.
145 -spec lookup_component(Domain :: jid:lserver()) -> [external_component()].
146 lookup_component(Domain) ->
147 395 mongoose_component_backend:lookup_component(Domain).
148
149 %% @doc Returns a list of components registered for this domain at the given node.
150 %% (must be only one, or nothing)
151 -spec lookup_component(Domain :: jid:lserver(), Node :: node()) -> [external_component()].
152 lookup_component(Domain, Node) ->
153 359 mongoose_component_backend:lookup_component(Domain, Node).
154
155 -spec dirty_get_all_components(return_hidden()) -> [jid:lserver()].
156 dirty_get_all_components(ReturnHidden) ->
157 86 mongoose_component_backend:get_all_components(ReturnHidden).
158
159 -spec node_cleanup(map(), map(), map()) -> {ok, map()}.
160 node_cleanup(Acc, #{node := Node}, _) ->
161 8 mongoose_component_backend:node_cleanup(Node),
162 8 {ok, maps:put(?MODULE, ok, Acc)}.
163
164 prepare_ldomains(Domains) ->
165 31 LDomains = [jid:nameprep(Domain) || Domain <- Domains],
166 31 Zip = lists:zip(Domains, LDomains),
167 31 InvalidDomains = [Domain || {Domain, error} <- Zip],
168 31 case InvalidDomains of
169 [] ->
170 31 LDomains;
171 _ ->
172
:-(
error({invalid_domains, InvalidDomains})
173 end.
Line Hits Source