diff --git a/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_api.erl b/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_api.erl index caf683455..b174fdcc0 100644 --- a/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_api.erl +++ b/apps/emqx_gateway/src/lwm2m/emqx_lwm2m_api.erl @@ -17,102 +17,113 @@ -module(emqx_lwm2m_api). -behaviour(minirest_api). +-include_lib("typerefl/include/types.hrl"). --export([api_spec/0]). +-export([api_spec/0, paths/0, schema/1, fields/1, namespace/0]). -export([lookup_cmd/2, observe/2, read/2, write/2]). --define(PREFIX, "/gateway/lwm2m/:clientid"). +-define(PATH(Suffix), "/gateway/lwm2m/:clientid"Suffix). +-define(DATA_TYPE, ['Integer', 'Float', 'Time', 'String', 'Boolean', 'Opaque', 'Objlnk']). --import(emqx_mgmt_util, [ object_schema/1 - , error_schema/2 - , properties/1 - , schema/1 - ]). +-import(hoconsc, [mk/2, ref/1, ref/2]). +-import(emqx_dashboard_swagger, [error_codes/2]). + +namespace() -> "lwm2m". api_spec() -> - {[ lookup_cmd_api(), observe_api(), read_api() - , write_api() - ], []}. + emqx_dashboard_swagger:spec(?MODULE). -base_paramters() -> - [ make_paramter(clientid, path, true, "string") - , make_paramter(path, query, true, "string") +paths() -> + [?PATH("/lookup_cmd"), ?PATH("/observe"), ?PATH("/read"), ?PATH("/write")]. + +schema(?PATH("/lookup_cmd")) -> + #{ + 'operationId' => lookup_cmd, + get => #{ + tags => [<<"lwm2m">>], + description => <<"Look up resource">>, + parameters => [ + {clientid, mk(binary(), #{in => path, example => "urn:oma:lwm2m:oma:2"})}, + {path, mk(binary(), #{in => query, required => true, example => "/3/0/7"})}, + {action, mk(binary(), #{in => query, required => true, example => "discover"})} + ], + 'requestBody' => [], + responses => #{ + 200 => [ + {clientid, mk(binary(), #{example => "urn:oma:lwm2m:oma:2"})}, + {path, mk(binary(), #{example => "/3/0/7"})}, + {action, mk(binary(), #{example => "discover"})}, + {'codeMsg', mk(binary(), #{example => "reply_not_received"})}, + {content, mk(hoconsc:array(ref(resource)), #{})} + ], + 404 => error_codes(['CLIENT_NOT_FOUND'], <<"Client not found">>) + } + } + }; +schema(?PATH("/observe")) -> + #{ + 'operationId' => observe, + post => #{ + tags => [<<"lwm2m">>], + description => <<"(cancel) observe resource">>, + parameters => [ + {clientid, mk(binary(), #{in => path, example => "urn:oma:lwm2m:oma:2"})}, + {path, mk(binary(), #{in => query, required => true, example => "/3/0/7"})}, + {enable, mk(boolean(), #{in => query, required => true, example => true})} + ], + 'requestBody' => [], + responses => #{ + 200 => <<"No Content">>, + 404 => error_codes(['CLIENT_NOT_FOUND'], <<"Clientid not found">>) + } + } + }; +schema(?PATH("/read")) -> + #{ + 'operationId' => read, + post => #{ + tags => [<<"lwm2m">>], + description => <<"Send a read command to resource">>, + parameters => [ + {clientid, mk(binary(), #{in => path, example => "urn:oma:lwm2m:oma:2"})}, + {path, mk(binary(), #{in => query, required => true, example => "/3/0/7"})} + ], + responses => #{ + 200 => <<"No Content">>, + 404 => error_codes(['CLIENT_NOT_FOUND'], <<"clientid not found">>) + } + } + }; +schema(?PATH("/write")) -> + #{ + 'operationId' => write, + post => #{ + description => <<"Send a write command to resource">>, + tags => [<<"lwm2m">>], + parameters => [ + {clientid, mk(binary(), #{in => path, example => "urn:oma:lwm2m:oma:2"})}, + {path, mk(binary(), #{in => query, required => true, example => "/3/0/7"})}, + {type, mk(hoconsc:enum(?DATA_TYPE), + #{in => query, required => true, example => 'Integer'})}, + {value, mk(binary(), #{in => query, required => true, example => 123})} + ], + responses => #{ + 200 => <<"No Content">>, + 404 => error_codes(['CLIENT_NOT_FOUND'], <<"Clientid not found">>) + } + } + }. + +fields(resource) -> + [ + {operations, mk(binary(), #{desc => <<"Resource Operations">>, example => "E"})}, + {'dataType', mk(hoconsc:enum(?DATA_TYPE), #{desc => <<"Data Type">>, + example => 'Integer'})}, + {path, mk(binary(), #{desc => <<"Resource Path">>, example => "urn:oma:lwm2m:oma:2"})}, + {name, mk(binary(), #{desc => <<"Resource Name">>, example => "lwm2m-test"})} ]. -lookup_cmd_paramters() -> - base_paramters() ++ [make_paramter(action, query, true, "string")]. - -lookup_cmd_properties() -> - properties([ {clientid, string} - , {path, string} - , {action, string} - , {code, string} - , {codeMsg, string} - , {content, {array, object}, lookup_cmd_content_props()}]). - -lookup_cmd_content_props() -> - [ {operations, string, <<"Resource Operations">>} - , {dataType, string, <<"Resource Type">>} - , {path, string, <<"Resource Path">>} - , {name, string, <<"Resource Name">>}]. - -lookup_cmd_api() -> - Metadata = #{get => - #{description => <<"look up resource">>, - parameters => lookup_cmd_paramters(), - responses => - #{<<"200">> => object_schema(lookup_cmd_properties()), - <<"404">> => error_schema("client not found error", ['CLIENT_NOT_FOUND']) - } - }}, - {?PREFIX ++ "/lookup_cmd", Metadata, lookup_cmd}. - - -observe_api() -> - Metadata = #{post => - #{description => <<"(cancel)observe resource">>, - parameters => base_paramters() ++ - [make_paramter(enable, query, true, "boolean")], - responses => - #{<<"200">> => schema(<<"Successed">>), - <<"404">> => error_schema("client not found error", ['CLIENT_NOT_FOUND']) - } - }}, - {?PREFIX ++ "/observe", Metadata, observe}. - -read_api() -> - Metadata = #{post => - #{description => <<"read resource">>, - parameters => base_paramters(), - responses => - #{<<"200">> => schema(<<"Successed">>), - <<"404">> => error_schema("client not found error", ['CLIENT_NOT_FOUND']) - } - }}, - {?PREFIX ++ "/read", Metadata, read}. - -write_api() -> - Metadata = #{post => - #{description => <<"write to resource">>, - parameters => base_paramters() ++ - [ make_paramter(type, query, true, "string", - [<<"Integer">>, - <<"Float">>, - <<"Time">>, - <<"String">>, - <<"Boolean">>, - <<"Opaque">>, - <<"Objlnk">>]) - , make_paramter(value, query, true, "string") - ], - responses => - #{<<"200">> => schema(<<"Successed">>), - <<"404">> => error_schema("client not found error", ['CLIENT_NOT_FOUND']) - } - }}, - {?PREFIX ++ "/write", Metadata, write}. - lookup_cmd(get, #{bindings := Bindings, query_string := QS}) -> ClientId = maps:get(clientid, Bindings), case emqx_gateway_cm_registry:lookup_channels(lwm2m, ClientId) of @@ -150,37 +161,40 @@ format_cmd_content(Content, <<"discover">>, Result) -> [H | Content1] = Content, {_, [HObjId]} = emqx_lwm2m_session:parse_object_list(H), [ObjId | _]= path_list(HObjId), - ObjectList = case Content1 of - [Content2 | _] -> - {_, ObjL} = emqx_lwm2m_session:parse_object_list(Content2), - ObjL; - [] -> [] - end, + ObjectList = + case Content1 of + [Content2 | _] -> + {_, ObjL} = emqx_lwm2m_session:parse_object_list(Content2), + ObjL; + [] -> [] + end, R = case emqx_lwm2m_xml_object:get_obj_def(binary_to_integer(ObjId), true) of {error, _} -> lists:map(fun(Object) -> #{Object => Object} end, ObjectList); ObjDefinition -> - lists:map( - fun(Object) -> - [_, _, RawResId| _] = path_list(Object), - ResId = binary_to_integer(RawResId), - Operations = case emqx_lwm2m_xml_object:get_resource_operations(ResId, ObjDefinition) of - "E" -> - #{operations => list_to_binary("E")}; - Oper -> - #{'dataType' => list_to_binary(emqx_lwm2m_xml_object:get_resource_type(ResId, ObjDefinition)), - operations => list_to_binary(Oper)} - end, - Operations#{path => Object, - name => list_to_binary(emqx_lwm2m_xml_object:get_resource_name(ResId, ObjDefinition))} - end, ObjectList) + lists:map(fun(Obj) -> to_operations(Obj, ObjDefinition) end, ObjectList) end, Result#{content => R}; format_cmd_content(Content, _, Result) -> Result#{content => Content}. +to_operations(Obj, ObjDefinition) -> + [_, _, RawResId| _] = path_list(Obj), + ResId = binary_to_integer(RawResId), + Operations = + case emqx_lwm2m_xml_object:get_resource_operations(ResId, ObjDefinition) of + "E" -> #{operations => <<"E">>}; + Oper -> + #{'dataType' => + list_to_binary(emqx_lwm2m_xml_object:get_resource_type(ResId, ObjDefinition)), + operations => list_to_binary(Oper) + } + end, + Operations#{path => Obj, + name => list_to_binary(emqx_lwm2m_xml_object:get_resource_name(ResId, ObjDefinition))}. + path_list(Path) -> case binary:split(binary_util:trim(Path, $/), [<<$/>>], [global]) of [ObjId, ObjInsId, ResId, ResInstId] -> [ObjId, ObjInsId, ResId, ResInstId]; @@ -189,19 +203,6 @@ path_list(Path) -> [ObjId] -> [ObjId] end. -make_paramter(Name, In, IsRequired, Type) -> - #{name => Name, - in => In, - required => IsRequired, - schema => #{type => Type}}. - -make_paramter(Name, In, IsRequired, Type, Enum) -> - #{name => Name, - in => In, - required => IsRequired, - schema => #{type => Type, - enum => Enum}}. - observe(post, #{bindings := #{clientid := ClientId}, query_string := #{<<"path">> := Path, <<"enable">> := Enable}}) -> MsgType = case Enable of