diff --git a/.gitignore b/.gitignore index 06a50614f0b12ea9d0a0c4ba100b297f93d7a72b..e03263b62cde25275cc7c4c303b025a187aeb41e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,10 @@ workspace.xml .idea/ .DS_Store _build +*.crashdump + +# Generated code +./priv/ # Excessive for iOS models apps/roster/priv/macbert/Model/PublishService.swift diff --git a/Makefile b/Makefile index dd4a9409d9f4017b9c71f5525a81e6935bd932d9..71c4c8364bef16bb796cee13bfce74c508cec789 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,87 @@ +REBAR ?= ./rebar3 + RELEASE := mq COOKIE := node_runner VER := 1.0.0 -default: deps compile +export RELX_REPLACE_OS_VARS=true +export NODE_NAME ?= mq + +all: compile + +compile: + @$(REBAR) compile + +clean: + @rm -rf priv + @$(REBAR) clean + +eunit: + @$(REBAR) eunit + + +export NYNJA_HOST ?= 127.0.0.1 +# Start and test a local release +# Use -$(REBAR) to enable cleanup when tests failed +# Need to manually `make local-build` to save time +rel-test: + ./_build/local/rel/server/bin/server start + sleep 15 + -$(REBAR) ct --dir test --spec test/integration_test.ts + ./_build/local/rel/server/bin/server stop + +## The 'ls', 'la', 'lscr' targets assume an already built +## 'local' target, and go directly at the scripts +## +lstart: + @./_build/local/rel/server/bin/server start + +lattach: + @./_build/local/rel/server/bin/server attach + +lscript: + @./_build/local/rel/server/bin/server $(SCRIPT) + +## These targets ensure that the system is built before +## executing the scripts. Safer, but slower. +## + +console: local-build + @./_build/local/rel/server/bin/server console + +script: local-build + @./_build/local/rel/server/bin/server $(SCRIPT) + +start: local-build + @./_build/local/rel/server/bin/server start + +attach: + @./_build/local/rel/server/bin/server attach + +local-build: + @$(REBAR) as local release + +upgrade-tool: local-build + @./noam_console.sh + +prod-build: + @$(REBAR) as prod release + +prod-console: prod-build + @./_build/prod/rel/server/bin/server console + +prod-start: prod-build + @./_build/prod/rel/server/bin/server start + +prod-attach: + @./_build/prod/rel/server/bin/server attach + +tar: + @$(REBAR) as prod tar + -include otp.mk +.PHONY: \ + all compile clean eunit rel-test local-build \ + start console attach lstart lattach lscript \ + prod-build prod-console prod-start prod-attach \ + tar diff --git a/README.md b/README.md index 18a1a741506bbfed6cb608cf522a8ab76623bdfc..67ffd9a74ea7959f9f35874b17161d59152abcd3 100644 --- a/README.md +++ b/README.md @@ -26,79 +26,26 @@ Developers Setup ---------------- ``` -$ curl -fsSL https://raw.github.com/synrc/mad/master/mad > mad \ - && chmod +x mad \ - && sudo cp /usr/local/bin -$ mad dep com rep -Configuration: [{n2o, - [{port,8000}, - {app,review}, - {pickler,n2o_secret}, - {formatter,bert}, - {log_modules,config}, - {log_level,config}]}, - {emq_dashboard, - [{listeners_dash, - [{http,18083,[{acceptors,4},{max_clients,512}]}]}]}, - {emq_modules, - [{modules, - [{emq_mod_presence,[{qos,1}]}, - {emq_mod_subscription,[{<<"%u/%c/#">>,2}]}, - {emq_mod_rewrite, - [{rewrite,"x/#","^x/y/(.+)$","z/y/$1"}, - {rewrite,"y/+/z/#","^y/(.+)/z/(.+)$", - "y/z/$2"}]}]}]}, - {emqttd, - [{listeners, - [{http,8083,[{acceptors,4},{max_clients,512}]}, - {tcp,1883,[{acceptors,4},{max_clients,512}]}]}, - {sysmon, - [{long_gc,false}, - {long_schedule,240}, - {large_heap,8000000}, - {busy_port,false}, - {busy_dist_port,true}]}, - {session, - [{upgrade_qos,off}, - {max_inflight,32}, - {retry_interval,20}, - {max_awaiting_rel,100}, - {await_rel_timeout,20}, - {enable_stats,off}]}, - {queue,[]}, - {allow_anonymous,true}, - {protocol, - [{max_clientid_len,1024},{max_packet_size,64000}]}, - {acl_file,"etc/acl.conf"}, - {plugins_etc_dir,"etc/plugins/"}, - {plugins_loaded_file,"etc/loaded_plugins"}, - {pubsub, - [{pool_size,8},{by_clientid,true},{async,true}]}]}, - {kvs, - [{dba,store_mnesia}, - {schema,[kvs_user,kvs_acl,kvs_feed,kvs_subscription]}]}] -Applications: [kernel,stdlib,gproc,lager_syslog,pbkdf2,asn1,fs,ranch,mnesia, - compiler,inets,crypto,syntax_tools,xmerl,gen_logger,esockd, - cowlib,goldrush,public_key,lager,ssl,cowboy,mochiweb,emqttd, - erlydtl,kvs,mad,emqttc,nitro,rest,sh,syslog,review] -Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:4:4] - [async-threads:10] [hipe] [kernel-poll:false] [dtrace] - -Eshell V8.3 (abort with ^G) -starting emqttd on node 'nonode@nohost' -Nonexistent: [] -Plugins: [{mqtt_plugin,emq_auth_username,"2.1.1", - "Authentication with Username/Password",false}, - {mqtt_plugin,emq_dashboard,"2.1.1","EMQ Web Dashboard",false}, - {mqtt_plugin,emq_modules,"2.1.1","EMQ Modules",false}, - {mqtt_plugin,n2o,"4.5-mqtt","N2O Server",false}] -Names: [emq_dashboard,n2o] -dashboard:http listen on 0.0.0.0:18083 with 4 acceptors. -Async Start Attempt {handler,"timer",n2o,system,n2o,[],[]} -Proc Init: init -mqtt:ws listen on 0.0.0.0:8083 with 4 acceptors. -mqtt:tcp listen on 0.0.0.0:1883 with 4 acceptors. -emqttd 2.1.1 is running now +$ make +===> Verifying dependencies... +... +===> Compiling roster +===> Compiling service +$ make console +===> Verifying dependencies... +===> Compiling roster +===> Compiling service +===> Starting relx build process ... +===> Resolving OTP Applications from directories: +... +===> release successfully created! +Exec: /.../erlang/20.3/erts-9.3/bin/erlexec -boot /.../nynja/server/_build/local/rel/server/releases/1.0.0/server -mode embedded -boot_var ERTS_LIB_DIR /.../erlang/20.3/lib -config /.../nynja/server/_build/local/rel/server/releases/1.0.0/sys.config -args_file /.../nynja/server/_build/local/rel/server/releases/1.0.0/vm.args -- console +Root: /.../nynja/server/_build/local/rel/server +/.../nynja/server/_build/local/rel/server +Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:32] [hipe] [kernel-poll:true] +... +Eshell V9.3 (abort with ^G) +(mq@127.0.0.1)1> > ``` @@ -132,8 +79,7 @@ Creating Single File Bundle --------------------------- ``` -$ mad release emqttd -$ ./emqttd rep +$ make tar ``` User/Device Registration @@ -160,32 +106,9 @@ ok > emqttd_ctl:run(["help"]). ``` -MQTT Erlang Client ------------------- - -``` -$ mad com -==> "/Users/maxim/depot/voxoz/emqttc/examples/gen_server" -Compiling /src/gen_server_example.erl -Writing /ebin/gen_server_example.app -OK -bash-3.2$ ./run -Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] - [async-threads:10] [hipe] [kernel-poll:false] [dtrace] - -Eshell V8.2 (abort with ^G) -1> [info] [Client <0.58.0>]: connecting to 127.0.0.1:1883 -[info] [Client <0.58.0>] connected with 127.0.0.1:1883 -[info] [Client <0.58.0>] RECV: CONNACK_ACCEPT -Client <0.58.0> is connected -[warning] [simpleClient@127.0.0.1:64618] resubscribe [{<<"TopicA">>,1}] -Message from TopicA: <<"hello...1">> -Message from TopicB: <<"hello...1">> -``` Credits ------- * Maxim Sokhatsky - -OM A HUM +* Ulf Wiger diff --git a/apps/nynja_oam/README.md b/apps/nynja_oam/README.md new file mode 100644 index 0000000000000000000000000000000000000000..523691e4e6cd1476ec8269151b503001934b72eb --- /dev/null +++ b/apps/nynja_oam/README.md @@ -0,0 +1,65 @@ +## nynja_oam Support + +Currently, this application mainly contains start script extensions for performing +database-related operations. + + +### db_snapshot + +**Usage:** `bin/server db_snapshot TARFILE` + +**Prerequisite:** Node is not running + +Used in order to be able to quickly reset the database e.g. between test runs. +A clean node is started, mnesia is started and tables are loaded. After this, +transaction logs are dumped, mnesia is stopped, and a compressed tar file of +the mnesia directory is created. + +Since table loading is done, running this command on a large database may +take some time. + +### db_restore + +**Usage:** `bin/server db_restore TARFILE` + +**Prerequisite:** Node is not running + +With a snapshot file (`TARFILE`) created using `db_snapshot` above, a +clean node is started, the mnesia directory is deleted and re-created +from the snapshot archive. A simple check is done to ensure that the +snapshot file is indeed a readable tar file, and that it contains a +directory matching the mnesia directory name. + +### db_install_fallback + +**Usage:** `bin/server db_install_fallback FILE` + +**Prerequisite:** Node is not running + +Used to load a mnesia fallback into e.g. a local build. The script starts a +clean node (i.e. using -boot start_clean), loads mnesia tables, then installs +the fallback. If this goes well, mnesia is stopped and restarted. The tables +are loaded, and then the node is shut down. + +Since both the original database and the resulting database are loaded +completely, running this command may take some time. + +### db_sync + +**Usage:** `bin/server db_sync NODE` + +**Prerequisite:** System running as NODE. No Mnesia database on disk for the +current node. + +Used to start a new major revision of the system, sync the database from an +existing, running, system, then start all applications in the next step. + +### db_sync_cont + +**Usage:** `bin/server db_sync_cont` + +**Prerequisite:** `db_sync` has been run successfully, and the node is still up. + +This command starts the remainder of the applications, making the node operational. +If the node runs on the same machine as the old version of the system, make sure +that the old node is first stopped, in order to avoid port conflicts. diff --git a/apps/nynja_oam/priv/extensions/db_install_fallback.sh b/apps/nynja_oam/priv/extensions/db_install_fallback.sh new file mode 100644 index 0000000000000000000000000000000000000000..9d3e75c9dc7ac077df13e8af83a652b40a3f4439 --- /dev/null +++ b/apps/nynja_oam/priv/extensions/db_install_fallback.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +case $1 in + help) + echo "bin/server db_install_fallback SNAPSHOT_FILE" + ;; + *) + ;; +esac + +code="noam_utils:install_fallback(\"$1\"),halt()" + +exec "$BINDIR/erlexec" $FOREGROUNDOPTIONS \ + -boot "$ROOTDIR/bin/start_clean" \ + -config "$RELX_CONFIG_PATH" \ + -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \ + -args_file "$VMARGS_PATH" \ + -eval "$code" diff --git a/apps/nynja_oam/priv/extensions/db_restore.sh b/apps/nynja_oam/priv/extensions/db_restore.sh new file mode 100644 index 0000000000000000000000000000000000000000..63c6628426a9480def9dc95470dfca7ab9e86f92 --- /dev/null +++ b/apps/nynja_oam/priv/extensions/db_restore.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +case $1 in + help) + echo "bin/server db_restore SNAPSHOT_FILE" + ;; + *) + ;; +esac + +code="noam_utils:restore_snapshot(\"$1\"),halt()" + +exec "$BINDIR/erlexec" $FOREGROUNDOPTIONS \ + -boot "$ROOTDIR/bin/start_clean" \ + -config "$RELX_CONFIG_PATH" \ + -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \ + -args_file "$VMARGS_PATH" \ + -eval "$code" diff --git a/apps/nynja_oam/priv/extensions/db_snapshot.sh b/apps/nynja_oam/priv/extensions/db_snapshot.sh new file mode 100644 index 0000000000000000000000000000000000000000..5a7ec55a2b58fb9b9a01a19d8b9775f04a69241c --- /dev/null +++ b/apps/nynja_oam/priv/extensions/db_snapshot.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +case $1 in + help) + echo "bin/server db_snapshot SNAPSHOT_FILE" + ;; + *) + ;; +esac + +code="noam_utils:db_snapshot(\"$1\"),halt()" + +exec "$BINDIR/erlexec" $FOREGROUNDOPTIONS \ + -boot "$ROOTDIR/bin/start_clean" \ + -config "$RELX_CONFIG_PATH" \ + -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \ + -args_file "$VMARGS_PATH" \ + -eval "$code" diff --git a/apps/nynja_oam/priv/extensions/db_sync.sh b/apps/nynja_oam/priv/extensions/db_sync.sh new file mode 100644 index 0000000000000000000000000000000000000000..3615783d5709fd5746d58ec16da17960ec94338c --- /dev/null +++ b/apps/nynja_oam/priv/extensions/db_sync.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +case $1 in + help) + echo "bin/server db_sync NODE" + ;; + *) + ;; +esac + +code="noam_utils:db_sync(\"$1\")" + +$($SCRIPT_DIR/server start_boot "$ROOTDIR/bin/start_clean" -eval "$code") + diff --git a/apps/nynja_oam/priv/extensions/db_sync_cont.sh b/apps/nynja_oam/priv/extensions/db_sync_cont.sh new file mode 100644 index 0000000000000000000000000000000000000000..af2cd9fe36f24dcab28c2b56d7b9ee1c91eb4823 --- /dev/null +++ b/apps/nynja_oam/priv/extensions/db_sync_cont.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +case $1 in + help) + echo "bin/server db_sync_cont OLD_NODE" + ;; + *) + ;; +esac + +code="noam_utils:db_sync_cont(\"$1\")" + +relx_nodetool "eval" ${code} diff --git a/apps/nynja_oam/src/noam_console.erl b/apps/nynja_oam/src/noam_console.erl new file mode 100644 index 0000000000000000000000000000000000000000..c78d667a69a8ab8a14f86270b36164c6b33fa7d4 --- /dev/null +++ b/apps/nynja_oam/src/noam_console.erl @@ -0,0 +1,664 @@ +%% +%% -*- mode:erlang -*- +%%! -name noam -setcookie emq_dist_cookie -hidden +-module(noam_console). +-mode(compile). +-export([main/1]). + +usage() -> + io:fwrite("Usage: escript ~w.beam -old OldNode -new NewNode~n", [?MODULE]), + halt(1). + +main(Args) -> + ensure_erlang_distribution(), + St = parse_args(Args), + St1 = ping_nodes(St), + io:fwrite("Initial state: ~p~n", [St1]), + menu_loop(St1). + +menu(#{mode := upgrade} = St) -> + upgrade_menu(St); +menu(St) -> + [node_menu(K, maps:get(K, St)) || K <- [old, new]]. + +upgrade_menu(St) -> + io:write("NYI: upgrade menu~n", []), + menu(maps:remove(mode, St)). + +node_menu(K, #{node := Node}) -> + NodeUp = node_up(Node), + [ + [{label("ping_~w", [K]), str("Ping ~w node", [K])}] + , [ {start_old, "Start old node"} || K == old andalso NodeUp == false] + , [ [ {label("test_~w", [K]), str("Test ~w node", [K])} + , {label("backup_~w", [K]), str("Backup ~w Mnesia db", [K])} + , {label("wft_~w", [K]), str("Check if tables loaded on ~w", [K])} + , {label("list_backups_~w", [K]), str("List backups on ~w node", [K])} + , {label("install_fallback_~w", [K]), str("Install fallback on ~w node", [K])} + , {label("remove_replicas_~w", [K]), str("Remove (~w) replicas on ~w node", [other(K), K])} + , [ {label("force_activate_~w", [K]), str("Force-activate ~w node", [K])} + || K == old] + , [ {label("switch_over_~w", [K]), str("Switch over to ~w node", [K])} + || K == new] + , {label("stop_~w", [K]), str("Stop ~p node", [K])} + , {label("reboot_~w", [K]), str("Reboot ~w node", [K])} + , '---' + ] + || NodeUp == true ] + , [ [ {start_new, "Bring up new node in standby"} + || K == new andalso (not node_up(Node))] ] + ]; +node_menu(_, _) -> + []. + +other(old) -> new; +other(new) -> old. + +node_name(K, St) -> + case maps:get(K, St) of + #{node := N} -> + {N, St}; + _ -> + case prompt_for_nodename_(K, St) of + {ok, Node, St1} -> + {Node, St1}; + error -> + error + end + end. + +dir(K, St) -> + case maps:get(K, St) of + #{root_dir := Dir} -> + {Dir, St}; + _ -> + case try_ping(K, St) of + #{K := #{root_dir := Dir}} = St1 -> + {Dir, St1}; + St1 -> + case prompt(str("Root dir of ~w node: ", [K])) of + {string, D} -> + Map1 = maps:get(K, St1), + {D, St1#{K => Map1#{root_dir => D}}}; + _ -> + error + end + end + end. + +node_up(N) -> + lists:member(N, nodes(connected)). + +serve(exit , _ ) -> halt(0); +serve(ping , St) -> ping_nodes(St); +serve(ping_old , St) -> try_ping(old, St); +serve(ping_new , St) -> try_ping(new, St); +serve(start_old , St) -> start(old, St); +serve(start_new , St) -> start(new, St); +serve(stop_old , St) -> stop(old, St); +serve(stop_new , St) -> stop(new, St); +serve(backup_old, St) -> backup(old, St); +serve(backup_new, St) -> backup(new, St); +serve(list_backups_old, St) -> list_backups(old, St); +serve(list_backups_new, St) -> list_backups(new, St); +serve(install_fallback_old, St) -> install_fallback(old, St); +serve(install_fallback_new, St) -> install_fallback(new, St); +serve(remove_replicas_old , St) -> remove_replicas(old, St); +serve(remove_replicas_new , St) -> remove_replicas(new, St); +serve(force_activate_old , St) -> force_activate(old, St); +serve(force_activate_new , St) -> nyi(force_activate_new, St); +serve(switch_over_old , St) -> nyi(switch_over_old, St); +serve(switch_over_new , St) -> switch_over(new, St); +serve(wft_old , St) -> wft(old, St); +serve(wft_new , St) -> wft(new, St); +serve(reboot_old, St) -> reboot(old, St); +serve(reboot_new, St) -> reboot(new, St); +serve(Label , St) -> nyi(Label, St). + +nyi(Label, St) -> + io:fwrite("Not yet implemented, op: ~p~n", [Label]), + St. + +ensure_erlang_distribution() -> + case node() of + nonode@nohost -> + {ok, _} = net_kernel:start([noam, longnames]), + erlang:set_cookie(node(), emq_dist_cookie), + ok; + _ -> + ok + end. + +backup(K, St) -> + #{node := Node} = maps:get(K, St), + io:fwrite("Requesting backup on ~w ...~n", [Node]), + Res = rpc:call(Node, roster, backup, []), + io:fwrite("Backup (~p) result: ~p~n", [Node, Res]), + list_backups(K, St). + +list_backups(K, St) -> + #{node := Node} = maps:get(K, St), + io:fwrite("Backups = ~p~n", [get_backup_list(Node)]), + St. + +get_backup_list(Node) -> + WildPat = str("~s_bkp*", [atom_to_list(Node)]), + rpc:call(Node, filelib, wildcard, [WildPat]). + +install_fallback(K, St) -> + #{node := Node} = maps:get(K, St), + Backups = get_backup_list(Node), + case prompt("Name of backup: ") of + {string, B} -> + case lists:member(B, Backups) of + true -> + io:fwrite("Installing fallback ...", []), + Res = rpc:call(Node, mnesia, install_fallback, [B]), + io:fwrite("Result = ~p~n", [Res]); + false -> + io:fwrite("No such backup~n", []) + end; + _Other -> + io:fwrite("Invalid input~n", []) + end, + St. + +remove_replicas(K, St) -> + case node_name(other(K), St) of + {OtherNode, St1} -> + case maybe_stop(OtherNode, St1) of + {ok, _, St2} -> + rpc(K, roster_oam, remove_replicas, [OtherNode], St2); + error -> + St1 + end; + error -> + St + end. + +force_activate(old, St) -> + case node_name(new, St) of + {OldNode, St1} -> % old in this context + case maybe_stop(OldNode, St1) of + {ok, stopped, St2} -> + force_activate(old, St2); + {ok, down, St2} -> + rpc(old, roster_oam, revert_db_sync, [OldNode], St2); + error -> + St1 + end; + error -> + io:fwrite("Couldn't force-activate ~n", []), + St + end. + +maybe_stop(OldNode, St) -> + case node_up(OldNode) of + true -> + notice([ {"Other node (~w) still running", [OldNode]} + , {"Stop this node first.", []}]), + case choice(str("Stop ~w? Yes/No [Yes]: ", [OldNode]), [yes, no]) of + yes -> + St1 = stop(new, St), + {ok, stopped, St1}; + no -> + error + end; + false -> + {ok, down, St} + end. + +switch_over(new, St) -> + case get_values([fun node_name_arg/2, fun dir/2], new, St) of + {[NodeArg, Dir], St1} -> + {OldNode, St2} = node_name(old, St1), + Cmd = str("(cd ~s && NODE_NAME=~s bin/server db_sync_cont ~s)", + [Dir, NodeArg, OldNode]), + os_cmd(Cmd), + St2; + error -> + St + end. + +%% wait for tables (5 seconds) +wft(old, St) -> + rpc(old, roster_oam, ensure_tables_loaded, [5000, 3], St); +wft(new, St) -> + rpc(new, noam_utils, wait_for_tables, [5000, 3], St). + + +reboot(K, St) -> + #{node := Node} = maps:get(K, St), + io:fwrite("Trying to reboot ~p ... ~n", [Node]), + Res = rpc:call(Node, init, restart, []), + io:fwrite("Res = ~p~n", [Res]), + St. + +ping_nodes(St) -> + St1 = try_ping(old, St), + St2 = try_ping(new, St1), + St2. + +try_ping(K, St) when K == old; K == new -> + case node_name(K, St) of + {Node, St1} -> + IsRunning = + lists:member(Node, nodes()) orelse + net_kernel:hidden_connect_node(Node), + case IsRunning of + true -> + io:fwrite("~p node (~p) is running~n", [K, Node]), + M1 = get_node_meta(maps:get(K, St1), St1), + St1#{K => M1}; + false -> + io:fwrite("~p node (~p) DOWN~n", [K, Node]), + St1 + end; + error -> + St + end. + +get_node_meta(#{node := N} = M, _St) -> + case rpc:call(N, init, script_id, []) of + {"Erlang/OTP", "20"} -> + %% old model + MnesiaDir = rpc:call(N, mnesia, system_info, [directory]), + RootDir = filename:dirname(MnesiaDir), + M#{type => old_system, + root_dir => RootDir}; + {"server", Vsn} -> + {ok, [[RootDir]]} = rpc:call(N, init, get_argument, [root]), + M#{type => new_system, + vsn => Vsn, + root_dir => RootDir} + end. + +start(K, St) -> + case dir(K, St) of + {D, St1} -> + case has_mnesia_dir(K, St1) of + {{true, MnesiaDir}, St2} -> + case choice("Found Mnesia directory. Keep/Delete/Move/Abort [Keep]: ", + [keep, delete, move, abort]) of + delete -> + del_mnesia_dir(MnesiaDir), + do_start(K, D, St2); + move -> + move_mnesia_dir(MnesiaDir), + do_start(K, D, St2); + keep -> + do_start(K, D, St2); + abort -> + St2 + end; + {false, St2} -> + do_start(K, D, St2); + error -> + St1 + end; + error -> + St + end. + +stop(K, St) -> + case node_name(K, St) of + {Node, St1} -> + erlang:monitor_node(Node, true), + try rpc(K, init, stop, [], St1), + receive + {nodedown, Node} -> + St1 + after 10000 -> + io:fwrite("No nodedown after 10 seconds~n", []), + St1 + end + after + erlang:monitor_node(Node, false) + end; + error -> + St + end. + +do_start(new, Dir, St) -> + {OldNode, St1} = node_name(old, St), + {NodeArg, St2} = node_name_arg(new, St1), + Cmd = str("(cd ~s && NODE_NAME=~s bin/server db_sync ~s)", + [Dir, NodeArg, OldNode]), + os_cmd(Cmd), + timer:sleep(1000), + try_ping(new, St2); +do_start(old, Dir, St) -> + notice([{"Cannot start old node automatically (wrong OTP vsn).", []}, + {"Go to ~s and type \"make start\"", [Dir]}]), + St. + +has_mnesia_dir(K, St) -> + case get_values([fun node_name/2, fun dir/2], K, St) of + {[Node, Dir], St1} -> + MDir = str("~s/Mnesia.~s", [Dir, Node]), + case filelib:is_dir(MDir) of + true -> + {{true, MDir}, St1}; + false -> + {false, St1} + end; + error -> + error + end. + +del_mnesia_dir(Dir) -> + Base = filename:basename(Dir), + Dirname = filename:dirname(Dir), + Cmd = str("(cd ~s && rm -rf ~s)", [Dirname, Base]), + os_cmd(Cmd). + +move_mnesia_dir(Dir) -> + Base = filename:basename(Dir), + Dirname = filename:dirname(Dir), + {A,B,C} = os:timestamp(), + TmpDir = str("/tmp/~s.TMP-~w-~w-~w", [Base, A, B, C]), + Cmd = str("(cd ~s && mv ~s ~s)", [Dirname, Base, TmpDir]), + os_cmd(Cmd). + +node_name_arg(K, St) -> + {Node, St1} = node_name(K, St), + case {host_part(Node), host_part(node())} of + {H, H} -> + {node_part(Node), St1}; + _ -> + {Node, St1} + end. + +get_values(Fs, K, St) -> + try lists:mapfoldl( + fun(F, Stx) when is_function(F, 2) -> + case F(K, Stx) of + {_, _} = Res -> + Res; + error -> + throw(error) + end + end, St, Fs) + catch + throw:error -> + error + end. + +host_part(Node) -> + [_, Host] = re:split(atom_to_list(Node), "@", [{return,list}]), + Host. + +node_part(Node) -> + [N, _] = re:split(atom_to_list(Node), "@", [{return, list}]), + N. + +%% start_slaves(St) -> +%% start_slave(old, start_slave(new, St)). + +%% start_slave(K, St) -> +%% case maps:get(K, St) of +%% #{slave := SlaveNode, node := _} = Map -> +%% case node_up(SlaveNode) of +%% true -> +%% io:fwrite("Slave already up (~p)~n", [SlaveNode]), +%% St; +%% false -> +%% do_start_slave(maps:remove(slave, Map), K, St) +%% end; +%% #{node := _} = Map -> +%% do_start_slave(Map, K, St) +%% end. + +%% do_start_slave(#{node := Node} = Map, K, St) -> +%% [_, Host] = re:split(atom_to_list(Node), "@", [{return, list}]), +%% case slave:start_link(Host) of +%% {ok, SlaveNode} -> +%% io:fwrite("Slave node (~p): ~p~n", [K, SlaveNode]), +%% St#{K => Map#{slave => SlaveNode}}; +%% {error, _} = Error -> +%% io:fwrite("ERROR starting slave node (~p): ~p~n", [K, Error]), +%% St +%% end. + +menu_loop(St) -> + menu_loop(menu(St), St). + +menu_loop(M, St) -> + menu_loop(default_prompt(), M, St). + +menu_loop(Prompt, M, St) -> + menu_loop_(Prompt, add_exit(enumerate(lists:flatten(M))), St). + +add_exit(M) -> + case lists:last(M) of + {0, exit, _} -> + M; + _ -> + M ++ [{0, exit, "Exit"}] + end. + +menu_loop_(Prompt0, M, St) -> + Prompt = maybe_add_default(Prompt0, M), + io:fwrite("~nMenu:~n" + "-------------------------~n"), + lists:foreach( + fun({N, _, Descr}) -> + io:fwrite("~p. ~s~n", [N, Descr]); + ({N, _, Descr, default}) -> + io:fwrite("~p. ~s~n", [N, Descr]); + ('---') -> + io:fwrite(" - - - -~n", []) + end, M), + Retry = fun() -> menu_loop(Prompt, M, St) end, + St1 = serve(validate(prompt(Prompt), M, Retry), St), + menu_loop(Prompt0, menu(St1), St1). + +enumerate([H|_] = M) when is_integer(element(1,H)) -> + M; +enumerate(M) -> + {M1, _} = + lists:mapfoldl( + fun('---', Acc) -> + {'---', Acc}; + (Entry, Acc) when is_atom(element(1, Entry)) -> + {insert_n(Acc, Entry), Acc+1} + end, 1, M), + M1. + +insert_n(N, {A, B}) -> + {N, A, B}; +insert_n(N, {A, B, C}) when is_atom(A) -> + {N, A, B, C}. + +default_prompt() -> + "Enter choice ". + +prompt(Prompt) -> + case io:get_line(Prompt) of + "\n" -> + default; + L -> + Trimmed = string:trim(L, both, "\s\t\n"), + try list_to_integer(Trimmed) of + N -> + {value, N} + catch + error:_ -> + {string, Trimmed} + end + end. + +validate(default, M, Retry) -> + case default(M) of + false -> + io:fwrite("Please enter a value.~n", []), + Retry(); + Found -> + element(2, Found) + end; +validate({value, N}, M, Retry) -> + case lists:keyfind(N, 1, M) of + false -> + io:fwrite("Unrecognized.", []), + Retry(); + Found -> + element(2, Found) + end; +validate(Other, _, Retry) -> + io:fwrite("Invalid input: ~p~n", [Other]), + Retry(). + +maybe_add_default(Prompt, M) -> + case default(M) of + false -> + Prompt; + Found -> + N = element(1, Found), + DefStr = " (" ++ integer_to_list(N) ++ ")", + case lists:reverse(Prompt) of + ":" ++ Rev -> + lists:reverse(Rev) ++ DefStr ++ ":"; + " :" ++ Rev -> + lists:reverse(Rev) ++ DefStr ++ ": "; + " " ++ Rev -> + lists:reverse(Rev) ++ DefStr ++ " "; + _ -> + Prompt + end + end. + +default(M) -> + lists:keyfind(default, 4, M). + +parse_args(Args) -> + case parse_(Args, #{}) of + #{old := _, new := _} = St -> + St; + St -> + case prompt_for_unknown(St) of + {ok, St1} -> + St1; + error -> + usage() + end + end. + +prompt_for_unknown(#{old := _, new := _} = St) -> + {ok, St}; +prompt_for_unknown(#{new := _} = St) -> + prompt_for_nodename(old, St); +prompt_for_unknown(#{} = St) -> + prompt_for_nodename(new, St). + +prompt_for_nodename(K, St) -> + case prompt_for_nodename_(K, St) of + {ok, _, St1} -> + prompt_for_unknown(St1); + Other -> + Other + end. + +prompt_for_nodename_(K, St) -> + Prompt = str("Enter node name for ~w node: ", [K]), + case io:fread(Prompt, "~a") of + {ok, [Node]} -> + {ok, Node, St#{K => #{node => Node}}}; + _ -> + error + end. + +rpc(K, M, F, A, St) -> + case node_name(K, St) of + {Node, St1} -> + Res = rpc:call(Node, M, F, A), + io:fwrite("rpc:call(~p, ~p, ~p, ~p) ->~n" + " ~p~n", [Node, M, F, A, Res]), + St1; + error -> + io:fwrite("Couldn't execute rpc ~n", []), + St + end. + +parse_(["-old", OldN | Args], St) -> + OldNode = list_to_atom(OldN), + parse_(Args, St#{old => #{node => OldNode}}); +parse_(["-new", NewN | Args], St) -> + NewN1 = maybe_complete_nodename(NewN), + NewNode = list_to_atom(NewN1), + parse_(Args, St#{new => #{node => NewNode}}); +parse_(_, St) -> + St. + +maybe_complete_nodename(N) -> + case re:run(N, "@") of + {match, _} -> + N; + nomatch -> + N ++ "@" ++ host_part(node()) + end. + +label(Fmt, Args) -> + list_to_atom(str(Fmt, Args)). + +str(Fmt, Args) -> + lists:flatten(io_lib:format(Fmt, Args)). + +choice(Prompt, Alts) -> + Pats = mk_pats(Alts), + Retry = fun() -> choice(Prompt, Alts) end, + case prompt(Prompt) of + {string, Str} -> + try_choice(string:to_lower(Str), Pats, Retry); + {value, V} -> + try_choice(integer_to_list(V), Pats, Retry); + default -> + hd(Alts) + end. + +try_choice(Str, Pats, Retry) -> + try lists:foldl( + fun({Pat, Lbl}, error) -> + case string:prefix(Pat, Str) of + nomatch -> + error; + _ -> + throw({ok, Lbl}) + end + end, error, Pats) of + error -> + io:fwrite("Invalid choice~n", []), + Retry() + catch + throw:{ok, Res} -> + Res + end. + +mk_pats([H|T]) when is_atom(H) -> + [{string:to_lower(atom_to_list(H)), H} | mk_pats(T)]; +mk_pats([H|T]) when is_integer(H) -> + [{integer_to_list(H), H} | mk_pats(T)]; +mk_pats([]) -> + []. + +os_cmd(Cmd) -> + Res = os:cmd(Cmd), + io:fwrite("$ ~s ->~n" + " ~s~n", [Cmd, Res]), + Res. + +notice(FmtArgs) -> + write_line(), + [io:fwrite(add_nl(Fmt), Args) || {Fmt, Args} <- FmtArgs], + write_line(), + ok. + +add_nl(Str) -> + case lists:reverse(lists:flatten(Str)) of + "\n" ++ _ -> + Str; + _ -> + Str ++ "\n" + end. + +write_line() -> + io:fwrite("+------------------------------------------------------~n", []). diff --git a/apps/nynja_oam/src/noam_utils.erl b/apps/nynja_oam/src/noam_utils.erl new file mode 100644 index 0000000000000000000000000000000000000000..fee32745e60efa8841fb69d9a0b4c1746d24cc05 --- /dev/null +++ b/apps/nynja_oam/src/noam_utils.erl @@ -0,0 +1,337 @@ +-module(noam_utils). + +-export([ db_snapshot/1 + , wait_for_tables/0 + , wait_for_tables/2 + , restore_snapshot/1 + , install_fallback/1 + , db_sync/1 + , db_sync_cont/1 ]). + +-define(LOG(Fmt, Args), io:fwrite(Fmt ++ "~n", Args)). + +db_snapshot(File) -> + ?LOG("File = ~p", [File]), + ok = ensure_mnesia_loaded_and_started(), + SchemaDir = mnesia:system_info(directory), + ?LOG("SchemaDir = ~p", [SchemaDir]), + ParentDir = filename:dirname(SchemaDir), + Base = filename:basename(SchemaDir), + dumped = mnesia:dump_log(), + ?LOG("Transaction log dumped", []), + application:stop(mnesia), + ?LOG("Mnesia stopped", []), + Cmd = [ "(cd ", ParentDir + , " && " + , "tar czf ", File, " ", Base + , ")" ], + ?LOG("Cmd = ~s", [Cmd]), + Res = os:cmd(Cmd), + ?LOG("Tar result = ~s", [Res]), + ok. + +restore_snapshot(File) -> + ?LOG("File = ~p", [File]), + no = mnesia:system_info(is_running), + ?LOG("Verified mnesia isn't running", []), + ok = ensure_app_loaded(mnesia), + SchemaDir = mnesia:system_info(directory), + ParentDir = filename:dirname(SchemaDir), + Base = filename:basename(SchemaDir), + ?LOG("Schema basename=~p", [Base]), + %% Verify that tar file appears to contain a relative + %% path to a direct subdir matching the mnesia directory + %% (We don't try to verify the content of it.) + ok = verify_snapshot_base(File, SchemaDir), + ?LOG("Tar file seems to match schema dir", []), + %% We now delete the existing Mnesia directory and unpack + %% the contents of the tar file. + Cmd = case mk_tmp_dir("old-mnesia") of + {ok, TmpDir} -> + [ "(cd ", ParentDir + , " && " + , "mv ", Base, " ", TmpDir + , " && " + , "echo \"ls: `ls -l`\"" + , " && " + , "tar xzf ", File + , ")" ]; + error -> + [ "(cd ", ParentDir + , " && " + , "rm -rf ", Base + , " && " + , "echo \"ls: `ls -l`\"" + , " && " + , "tar xzf ", File + , ")" ] + end, + ?LOG("Cmd = ~s", [Cmd]), + Res = os:cmd(Cmd), + ?LOG("Res = ~s", [Res]), + ok. + +mk_tmp_dir(Descr) -> + {A,B,C} = os:timestamp(), + Dir = filename:join(["/tmp", Descr, + lists:flatten( + io_lib:format("~w-~w-~w", [A, B, C]))]), + case filelib:ensure_dir(filename:join(Dir, "dummy")) of + ok -> + {ok, Dir}; + {error, Reason} -> + ?LOG("Cannot create tmp dir ~s: ~p", [Dir, Reason]), + error + end. + +install_fallback(File) -> + ?LOG("File = ~p", [File]), + ok = ensure_mnesia_loaded_and_started(), + case mnesia:install_fallback(File) of + ok -> + ?LOG("Fallback installed, restarting mnesia ...", []), + application:stop(mnesia), + ensure_mnesia_loaded_and_started(), + init:stop(); + Error -> + abort("~p", [Error]) + end. + +db_sync_cont(OldNodeStr) -> + OldNode = to_node_atom(OldNodeStr), + case mnesia_is_running_and_tabs_loaded() of + true -> + case verify_old_node(OldNode) of + running -> + ok = rpc:call(OldNode, roster_oam, disable, []), + continue_db_sync_start(); + down -> + continue_db_sync_start(); + invalid -> + {error, {unknown_node, OldNode}} + end; + false -> + ?LOG("Mnesia not ready", []), + {error, mnesia_not_running} + end. + +continue_db_sync_start() -> + ?LOG("Continuing system start", []), + continue_db_sync_start_(). + +continue_db_sync_start_() -> + Apps = app_start_list(), + [mnesia|Rest] = lists:dropwhile(fun(A) -> + A =/= mnesia + end, Apps), + [start_app(A) || A <- Rest], + ok. + +db_sync(NodeStr) -> + ?LOG("NodeStr = ~p", [NodeStr]), + Node = to_node_atom(NodeStr), + case mnesia_prepared_for_db_sync(Node) of + true -> + case net_adm:ping(Node) of + pong -> + bootstrap_mnesia_to_node(Node), + ?LOG("~p is ALIVE", [Node]); + pang -> + ?LOG("~p is DEAD", [Node]) + end, + ok; + false -> + abort("Mnesia not prepared for sync", []) + end. + +ensure_mnesia_loaded_and_started() -> + ok = application:ensure_started(mnesia), + ?LOG("mnesia started", []), + mnesia:info(), + wait_for_tables(). + +mnesia_is_running_and_tabs_loaded() -> + case mnesia_lib:is_running() of + yes -> + %% Note: we expect tables to already be loaded, + %% so short timeout. + case mnesia:wait_for_tables(tabs(), 5000) of + ok -> + true; + _ -> + false + end; + Other -> + ret_false("Mnesia running? ~p", [Other]) + end. + +wait_for_tables() -> + wait_for_tables(tabs()). + +wait_for_tables(Tabs) when is_list(Tabs) -> + wait_for_tables(Tabs, 5000, 12*30). + +wait_for_tables(Timeout, Retries) -> + wait_for_tables(tabs(), Timeout, Retries). + +wait_for_tables(Tabs, Timeout, Retries) when is_integer(Timeout), + is_integer(Retries), Retries > 0 -> + ok = wait_for_tables_(Tabs, Timeout, Retries), + ?LOG("Tables loaded", []), + ok. + +tabs() -> + mnesia:system_info(tables) -- [schema]. + +wait_for_tables_(Tabs, _, 0) -> + {timeout, Tabs}; +wait_for_tables_(Tabs, Timeout, Retries) when Retries > 0 -> + case mnesia:wait_for_tables(Tabs, Timeout) of + {timeout, Remain} -> + ?LOG("wait_for_tables timeout, Remain = ~p", [Remain]), + ?LOG("Tabs loaded: ~p", [Tabs -- Remain]), + wait_for_tables_(Remain, Timeout, Retries - 1); + ok -> + ok + end. + +verify_old_node(Node) -> + RemoteNodes = mnesia_lib:remote_copy_holders(mnesia:table_info(schema, cstruct)), + case lists:member(Node, RemoteNodes) of + true -> + case lists:member(Node, nodes()) of + true -> + running; + false -> + down + end; + false -> + invalid + end. + +ensure_app_loaded(App) -> + case application:load(App) of + ok -> + ok; + {error,{already_loaded,_}} -> + ok; + Other -> + Other + end. + +verify_snapshot_base(TarF, SchemaDir) -> + Parent = filename:dirname(SchemaDir), + Base = filename:basename(SchemaDir), + Cmd = [ "(cd ", Parent + , " && " + , "tar tzf ", TarF, " )"], + ?LOG("Cmd = ~p", [Cmd]), + Res = os:cmd(Cmd), + FirstLine = hd(string:tokens(Res, [$\n])), + ?LOG("Tar tzf Res (line 1) = ~p", [FirstLine]), + case (Base ++ "/") == FirstLine of + true -> + ok; + false -> + ?LOG("ERROR: ~p doesn't match Mnesia base (~p)", [FirstLine, Base]), + {error, invalid_tar} + end. + +mnesia_prepared_for_db_sync(_Node) -> + case mnesia_lib:is_running() of + no -> + Location = mnesia:system_info(directory), + case mnesia:system_info(use_dir) of + true -> + ret_false("Mnesia dir exists at ~p", [Location]); + false -> + true + end; + yes -> ret_false("Mnesia already running", []); + Other -> ret_false("Mnesia is ~p", [Other]) + end. + +ret_false(Fmt, Args) -> + ?LOG(Fmt, Args), + false. + +bootstrap_mnesia_to_node(Node) -> + start_apps_before_mnesia(), + mnesia:start([{extra_db_nodes, [Node]}]), + {atomic, ok} = + mnesia:change_table_copy_type(schema, node(), disc_copies), + ?LOG("Mnesia connected to ~p", [Node]), + Tabs = tabs(), + add_me_to_tabs(Tabs, Node), + wait_for_tables(Tabs), + mnesia:info(). + +add_me_to_tabs(Tabs, OrigNode) -> + TabsAndTypes = tabs_and_types(Tabs, OrigNode), % [{Tab, Type}] + Wy = integer_to_list(max_length([Ty || {_,Ty} <- TabsAndTypes])), + Wt = integer_to_list(max_length([T || {T,_ } <- TabsAndTypes])), + lists:foreach( + fun({T, Type}) -> + T0 = erlang:system_time(millisecond), + {atomic, ok} = mnesia:add_table_copy(T, node(), Type), + T1 = erlang:system_time(millisecond), + ?LOG("Added local replica (~-" ++ Wy ++ "w) of " + "~-" ++ Wt ++ "w (~w ms)", + [Type, T, T1-T0]) + end, TabsAndTypes), + ?LOG("Added local copies for tabs", []), + ok. + +max_length(L) -> + lists:max([length(atom_to_list(X)) || X <- L]). + +tabs_and_types(Tabs, Node) -> + [{T, mnesia_lib:storage_type_at_node(Node, T)} || T <- Tabs]. + +abort(Fmt, Args) -> + ?LOG("ABORT: " ++ Fmt, Args), + halt(1). + +start_apps_before_mnesia() -> + Apps = app_start_list(), + [application:load(A) || A <- Apps], + Pre = lists:takewhile(fun(A) -> + A =/= mnesia + end, Apps), + [start_app(A) || A <- Pre], + ok. + +start_app(A) -> + ?LOG("start app ~p ...", [A]), + {ok, Started} = application:ensure_all_started(A, permanent), + case Started -- [A] of + [] -> ok; + Others -> + ?LOG("also started ~p", [Others]), + ok + end. + +app_start_list() -> + {ok, {script, _, Cmds}} = get_real_boot_script(), + [kernel, stdlib | Apps] = + [A || {apply,{application,start_boot,[A,permanent]}} <- Cmds], + Apps. + +get_real_boot_script() -> + {RelName, _} = init:script_id(), + {ok, [[RelConf]]} = init:get_argument(config), + RCDir = filename:dirname(RelConf), + BootScript = filename:join(RCDir, RelName ++ ".boot"), + {ok, Bin} = file:read_file(BootScript), + {ok, binary_to_term(Bin)}. + +to_node_atom(A) when is_atom(A) -> + A; +to_node_atom(S) when is_list(S) -> + case re:split(S, "@", [{return,list}]) of + [_,_] -> + %% Would be nice if we could use list_to_existing_atom/1 here. + list_to_atom(S); + _ -> + error(strange_atom_string) + end. diff --git a/apps/nynja_oam/src/nynja_oam.app.src b/apps/nynja_oam/src/nynja_oam.app.src new file mode 100644 index 0000000000000000000000000000000000000000..91f39971d92e7731075d0aaaf1aef6343516b8a9 --- /dev/null +++ b/apps/nynja_oam/src/nynja_oam.app.src @@ -0,0 +1,9 @@ +%% -*- mode:erlang; erlang-indent-level:4; indent-tabs-mode:nil -*- + +{application, nynja_oam, + [{description, "Operation & Maintenance functions for Nynja server"}, + {vsn, git}, + {registered, []}, + {applications, [kernel, stdlib, mnesia, service, roster]}, + {env, []} + ]}. diff --git a/apps/rebar.config b/apps/rebar.config deleted file mode 100644 index c337a9c61111b599156aff4ca5e7b7e0d4ea6787..0000000000000000000000000000000000000000 --- a/apps/rebar.config +++ /dev/null @@ -1,2 +0,0 @@ -{sub_dirs, [ "roster", "service" ]}. -{deps_dir, ["../deps"]}. diff --git a/apps/roster/include/roster_test.hrl b/apps/roster/include/roster_test.hrl index 8cc0a85752fde4e64135c98c649904d1675722f1..cdcfe4b94c527479a3ff79ff87a13561bc85d0f1 100644 --- a/apps/roster/include/roster_test.hrl +++ b/apps/roster/include/roster_test.hrl @@ -2,6 +2,7 @@ -define(ROSTER_TEST_HRL, true). -define(LOC, "127.0.0.1"). +%%-define(REMOTE_NODE, true). -define(HOST, ?LOC). -define(DRP_TMOUT, case ?HOST of ?LOC -> 100;_ -> 1000 end). -define(TIMEOUT, 7000). diff --git a/apps/roster/include/static/rest_var.hrl b/apps/roster/include/static/rest_var.hrl deleted file mode 100644 index 2c13ed749a0e10fabd6c4cb04108a06f30502f4a..0000000000000000000000000000000000000000 --- a/apps/roster/include/static/rest_var.hrl +++ /dev/null @@ -1,45 +0,0 @@ -%% ------------------------------------------------------------------ -%% Static Variables for REST modules -%% ------------------------------------------------------------------ - -%% Endpoints --define(SESSIONS_ENDPOINT, "/sessions"). - --define(WHITELIST_ENDPOINT, "/whitelist"). --define(ADMIN_WHITELIST_ENDPOINT, "/admin_whitelist"). - -%% FN is for Fake Numbers --define(ADMIN_FN_ENDPOINT, "/fake_numbers"). --define(FN_ENDPOINT, "/fn"). - --define(MSG_PUSH_ENDPOINT, "/push/message"). --define(ROOM_ENDPOINT, "/room"). --define(PUBLISH_ENDPOINT, "/publish"). --define(USERS_ENDPOINT, "/users"). --define(METRICS_ENDPOINT, "/metrics"). - --define(RCI_ROOM_TYPE_ENDPOINT, "/cri/rooms/type"). --define(RCI_ROOM_ENDPOINT, "/cri/rooms"). --define(RCI_ROOM_MEMBERS_ENDPOINT, "/cri/rooms/members"). --define(RCI_BUBBLE_ENDPOINT, "/cri/bubbles"). - -%% Request Query and Body Param Names --define(PHONE_ID_HEADER, "PhoneId"). - --define(ROOM_ID_PARAM, "room_id"). --define(PHONE_IDS_PARAM, "phone_ids"). --define(PHONE_ID_PARAM, "phone_id"). --define(JOIN_FLAG_PARAM, "join"). - -%% sessions module --define(PHONE_PARAM, "phone"). - - - -%% HTTP Status Codes --define(HTTP_CODE_200, 200). --define(HTTP_CODE_400, 400). --define(HTTP_CODE_401, 401). --define(HTTP_CODE_403, 403). --define(HTTP_CODE_404, 404). --define(HTTP_CODE_405, 405). \ No newline at end of file diff --git a/apps/roster/priv/www/assets/js/phone_numbers.js b/apps/roster/priv/www/assets/js/phone_numbers.js index fbfd969dc98069093ccca8abfe61b0c65184ce50..f6f496806a3b2ef0d69b048c5c855f68048e81a4 100644 --- a/apps/roster/priv/www/assets/js/phone_numbers.js +++ b/apps/roster/priv/www/assets/js/phone_numbers.js @@ -17,11 +17,8 @@ $(document).ready(function() { url: GetCurrentUrl(), type: 'GET', success: function(data) { - var parsed_data = JSON.parse(data); - var status = parsed_data["Status"]; - var payload = parsed_data["Data"]; - if (status == "Success" ) { - FillWhitelist(payload, order); + if (data.Status == "Success" ) { + FillWhitelist(data.Data, order); } } }); @@ -31,11 +28,10 @@ $(document).ready(function() { $.ajax({ url: GetCurrentUrl(), type: 'PUT', + contentType: 'application/json', data: '{"phone":['+ data + ']}', success: function(data) { - var parsed_data = JSON.parse(data); - var status = parsed_data["Status"]; - if (status == "Success" ) { + if (data.Status == "Success" ) { document.getElementById('close-modal-window').click(); GetWhitelistFromServer("down"); } @@ -57,9 +53,7 @@ $(document).ready(function() { url: GetCurrentUrl() + '?' + $.param({'phone': data}, true), type: 'DELETE', success: function(data) { - var parsed_data = JSON.parse(data); - var status = parsed_data["Status"]; - if (status == "Success" ) { + if (data.Status == "Success" ) { GetWhitelistFromServer("down"); } }, @@ -200,4 +194,4 @@ $(document).ready(function() { element.dataset.order = new_order; GetWhitelistFromServer(new_order) }); -}); \ No newline at end of file +}); diff --git a/apps/roster/rebar.config b/apps/roster/rebar.config deleted file mode 100644 index a3a320496122b26a277284d0f2e3cd5d95a77267..0000000000000000000000000000000000000000 --- a/apps/roster/rebar.config +++ /dev/null @@ -1,4 +0,0 @@ -{deps_dir, ["../../deps"]}. -{lib_dirs, ["../../apps"]}. -{deps, []}. -{erl_opts, [debug_info]}. \ No newline at end of file diff --git a/apps/roster/src/api/amazon_api.erl b/apps/roster/src/api/amazon_api.erl index 2768e3941f2a1a6624e13f0f197521b213d08ba0..a8c47277377fe2013c4b74372c530d1d6484b77f 100644 --- a/apps/roster/src/api/amazon_api.erl +++ b/apps/roster/src/api/amazon_api.erl @@ -1,4 +1,5 @@ -module(amazon_api). +-include_lib("kernel/include/logger.hrl"). -export([push_to_s3/4, test_upload/0, gen_temp_creds/0, test_gen_temp_creds/0, test_parse_sts_response/0]). -define(AWS_ACCESS_KEY_ID, proplists:get_value(access_key_id, application:get_env(roster, amazon_api, []))). @@ -28,6 +29,10 @@ %% Helpers %% **************************************************************************** +content_type(Filename) -> + {Class, Type, _} = cow_mimetypes:all(Filename), + iolist_to_binary([Class, "/", Type]). + %% ---------------------------------------------------------------------------- %% AWS S3 helpers %% ---------------------------------------------------------------------------- @@ -67,17 +72,17 @@ gen_hash(Data) -> parse_sts_response(Data) -> try - {_, DecodedIn} = mochijson:decode(Data), - {_, GetSessionTokenResponse} = proplists:get_value("GetSessionTokenResponse", DecodedIn), - {_, GetSessionTokenResult} = proplists:get_value("GetSessionTokenResult", GetSessionTokenResponse), - {_, Credentials} = proplists:get_value("Credentials", GetSessionTokenResult), - AccessKeyId = proplists:get_value("AccessKeyId", Credentials), - SecretAccessKey = proplists:get_value("SecretAccessKey", Credentials), - SessionToken = proplists:get_value("SessionToken", Credentials), - Expiration = erlang:round(proplists:get_value("Expiration", Credentials)), + DecodedIn = jsx:decode(iolist_to_binary(Data)), + GetSessionTokenResponse = proplists:get_value(<<"GetSessionTokenResponse">>, DecodedIn), + GetSessionTokenResult = proplists:get_value(<<"GetSessionTokenResult">>, GetSessionTokenResponse), + Credentials = proplists:get_value(<<"Credentials">>, GetSessionTokenResult), + AccessKeyId = proplists:get_value(<<"AccessKeyId">>, Credentials), + SecretAccessKey = proplists:get_value(<<"SecretAccessKey">>, Credentials), + SessionToken = proplists:get_value(<<"SessionToken">>, Credentials), + Expiration = erlang:round(proplists:get_value(<<"Expiration">>, Credentials)), {ok, {AccessKeyId, SecretAccessKey, SessionToken, Expiration}} catch - E:R -> roster:info(?MODULE, "~p:Parsing AWS STS Response Failed!~p:~p", [Data, E, R]), + E:R -> ?LOG_INFO("~p:Parsing AWS STS Response Failed!~p:~p(~p)", [Data, E, R]), {error, R} end. @@ -92,8 +97,7 @@ push_to_s3(BucketName, Key, Data, Headers) when is_binary(BucketName) -> push_to_s3(BucketName, Key, Data, Headers) when is_binary(Key) -> push_to_s3(BucketName, binary_to_list(Key), Data, Headers); push_to_s3(BucketName, Key, Data, Headers) when Headers == [] -> - ContentType = mochiweb_mime:from_extension(filename:extension(Key)), - RequestHeaders = [{"content-type", ContentType}], + RequestHeaders = [{"content-type", content_type(Key)}], push_to_s3(BucketName, Key, Data, RequestHeaders); push_to_s3(BucketName, Key, Data, Headers) -> UniqueKey = make_unique_key(Key), @@ -102,7 +106,7 @@ push_to_s3(BucketName, Key, Data, Headers) -> mini_s3:put_object(BucketName, UniqueKey, Data, [{acl, public_read}], Headers), get_s3_link(BucketName, UniqueKey) catch E:R -> - roster:info(?MODULE, "Pushing to Amazon S3 failed! ~p:~p", [E,R]), + ?LOG_INFO("Pushing to Amazon S3 failed! ~p:~p", [E,R]), {error, R} end. %% **************************************************************************** @@ -137,7 +141,7 @@ gen_temp_creds() -> ok -> parse_sts_response(RequestResponse); _ -> {error, RequestResponse} end, - roster:info(?MODULE, "STSGetSessionTokenResponse:~p", [Response]), + ?LOG_INFO("STSGetSessionTokenResponse:~p", [Response]), Response. %% **************************************************************************** @@ -150,6 +154,7 @@ gen_temp_creds() -> -define(AMAZON_TEST_APPLICATION_NAME, roster). +%% TODO use sys.config for location and possibly add at same place as google api read_local_file(Filename) -> case code:priv_dir(?AMAZON_TEST_APPLICATION_NAME) of {error, bad_name} -> @@ -157,17 +162,16 @@ read_local_file(Filename) -> PrivDir -> ok end, - {Status, Binary} = file:read_file(filename:join([PrivDir, Filename])), - case Status of - error -> - roster:info(?MODULE, "Cannot read local file!~n~p:~p", [Status, Binary]); - ok -> + Path = filename:join(PrivDir, Filename), + case file:read_file(Path) of + {error, Reason} -> + ?LOG_INFO("Cannot read local file ~p: ~p", [Path, Reason]); + {ok, Binary} -> prepare_file_to_uploading(Filename, Binary) end. prepare_file_to_uploading(Filename, Data) -> - ContentType = mochiweb_mime:from_extension(filename:extension(Filename)), - Headers = [{"content-type", ContentType}], + Headers = [{"content-type", content_type(Filename)}], {A, B, C} = os:timestamp(), Timestamp = lists:concat([A, B, C]), Key = lists:concat(["TestFile", Timestamp, filename:extension(Filename)]), @@ -188,4 +192,4 @@ test_gen_temp_creds() -> test_parse_sts_response() -> Data = "{\"GetSessionTokenResponse\":{\"GetSessionTokenResult\":{\"Credentials\":{\"AccessKeyId\":\"ASIAJ6H77ARLVWEMXGAQ\",\"Expiration\":1.507853429E9,\"SecretAccessKey\":\"Xb0wOH4PyYe5YaJBu7TqjSAXRq2vaInPR1JbSiRJ\",\"SessionToken\":\"FQoDYXdzEO3//////////wEaDIbWWOcDonXumUQViSKsAeRh+g/QQGrYWpxxBwkX1diNpalwK29hlUlh0a//zmEP5ZuuWbQPMiW0JxbPltnj0G4T1S9Nt7qdOUUNsCPNc/1UKp6SrO26kdC1dvbfyWcQbpABROUtXn0nN/Du+94aM8Fe4uUgXcNJdu0NILz4U5WQRT5h/Z5k388ZjHQxHTqDxw/waIEy5PVGQyQvjv0HvN+kdDgGdVdfMIhEeBS9ucNMxtYagwIc8M9guQMotbf9zgU=\"}},\"ResponseMetadata\":{\"RequestId\":\"56cb6126-af46-11e7-8109-ed1456c23af6\"}}}", {Status, _} = parse_sts_response(Data), - ok == Status. \ No newline at end of file + ok == Status. diff --git a/apps/roster/src/api/email_api.erl b/apps/roster/src/api/email_api.erl index 08c9155cf6abcf32a35c01396b75cef031f4afc2..2830f4485b97721126f1b40af4bb721d5334f084 100644 --- a/apps/roster/src/api/email_api.erl +++ b/apps/roster/src/api/email_api.erl @@ -1,6 +1,7 @@ -module(email_api). -compile(export_all). +-include_lib("kernel/include/logger.hrl"). -define(SMTP_HOST, proplists:get_value(host, application:get_env(roster,email_api, []))). -define(EMAIL_LOGIN, proplists:get_value(login, application:get_env(roster,email_api, []))). @@ -12,7 +13,7 @@ %% ------------------------------------------------------------------ send_email(ToEmail,Subject, Body) -> - roster:info(?MODULE, "~p:Email ~p:~p", [ToEmail, Subject, Body]), + ?LOG_INFO("~p:Email ~p:~p", [ToEmail, Subject, Body]), %% create payload @@ -23,11 +24,11 @@ send_email(ToEmail,Subject, Body) -> R = gen_smtp_client:send({ToEmail, [ToEmail], EncodedBody}, [{relay, ?SMTP_HOST}, {username, ?EMAIL_LOGIN}, {password, ?EMAIL_PASSWORD}]), - roster:info(?MODULE, "Gen SMTP Response:~p", [R]), + ?LOG_INFO("Gen SMTP Response:~p", [R]), case R of {ok, _} -> {ok, email_in_progress}; {error, _, {_, _, Reason}} -> - roster:info(?MODULE, "Reason:~p", [Reason]), + ?LOG_INFO("Reason:~p", [Reason]), {error, Reason} end. @@ -40,4 +41,4 @@ send_email(ToEmail,Subject, Body) -> test_email_sending() -> Body = <<"Hello,

This is test mail! Playing with HTML letters!">>, Subject = iolist_to_binary(["Test ", vox_api:generate_random_data(4)]), - send_email(?TEST_EMAIL, Subject, Body). \ No newline at end of file + send_email(?TEST_EMAIL, Subject, Body). diff --git a/apps/roster/src/api/google_api.erl b/apps/roster/src/api/google_api.erl index a0f5d4591004d3ee5efaed5f651502b239a00523..58e8e5bef8b16c3f72873fcf8f595dab1c4d45fb 100644 --- a/apps/roster/src/api/google_api.erl +++ b/apps/roster/src/api/google_api.erl @@ -1,6 +1,7 @@ -module(google_api). -compile(export_all). +-include_lib("kernel/include/logger.hrl"). -include_lib("enenra/include/enenra.hrl"). -define(GOOGLE_API_KEY, proplists:get_value(key, application:get_env(roster, google_api, []))). @@ -27,50 +28,52 @@ %% ------------------------------------------------------------------ detect_language(Text) -> - roster:info(?MODULE, "DetectLanguage: ~p", [Text]), + ?LOG_INFO("DetectLanguage: ~p", [Text]), %% TODO encode url symbols. Not all text, just whitespaces, dots, etc. %% NOTE! Dont use http_uri:encode. This lib encodes all text, fuuuu QueryParams = iolist_to_binary(["key=", ?GOOGLE_API_KEY]), PostVariables = jsx:encode([{<<"q">>, Text}]), - roster:info(?MODULE, "PostVariables: ~p", [PostVariables]), + ?LOG_INFO("PostVariables: ~p", [PostVariables]), Request = binary_to_list(iolist_to_binary([?DETECT_LANGUAGE_URL, "?", QueryParams])), - roster:info(?MODULE, "Request: ~p", [Request]), + ?LOG_INFO("Request: ~p", [Request]), {RequestStatus, RequestResult} = roster_rest:send_request(post, {Request, [], ?CONTENT_TYPE, PostVariables}, [], []), - roster:info(?MODULE, "RequestStatus: ~p", [RequestStatus]), - roster:info(?MODULE, "RequestResult: ~p", [RequestResult]), + ?LOG_INFO("RequestStatus: ~p", [RequestStatus]), + ?LOG_INFO("RequestResult: ~p", [RequestResult]), ok. translate(Text, Target) -> - roster:info(?MODULE, "Debug.Target:~p.Text: ~p", [Target, Text]), + ?LOG_INFO("Debug.Target:~p.Text: ~p", [Target, Text]), QueryParams = iolist_to_binary(["key=", ?GOOGLE_API_KEY]), PostVariables = jsx:encode([{<<"q">>, Text}, {<<"target">>, Target}]), - roster:info(?MODULE, "PostVariables: ~p", [PostVariables]), + ?LOG_INFO("PostVariables: ~p", [PostVariables]), Request = binary_to_list(iolist_to_binary([?TRANSLATION_URL, "?", QueryParams])), {RequestStatus, RequestResult} = roster_rest:send_request(post, {Request, [], ?CONTENT_TYPE, PostVariables}, [], []), - roster:info(?MODULE, "RequestStatus: ~p", [RequestStatus]), - roster:info(?MODULE, "RequestResult: ~p", [RequestResult]), + ?LOG_INFO("RequestStatus: ~p", [RequestStatus]), + ?LOG_INFO("RequestResult: ~p", [RequestResult]), ok. %% ------------------------------------------------------------------ %% Transcribe API %% ------------------------------------------------------------------ start() -> - filelib:ensure_dir("./priv/tmp/"), - gen_server:call(ibrowse, {set_config_value, download_dir, "./priv/tmp/"}), - PathFile = filename:absname(proplists:get_value(app_credentials, application:get_env(roster, google_api, indefined))), - case filelib:is_file(PathFile) of - true -> - os:putenv("GOOGLE_APPLICATION_CREDENTIALS", PathFile), - case enenra:load_credentials(os:getenv("GOOGLE_APPLICATION_CREDENTIALS")) of - {ok, Creds} -> application:set_env(roster, google_creds, Creds); - Err-> roster:error(?MODULE, "cannot load googlr creds: ~p", [Err]) - end; - _ -> roster:error(?MODULE, "google credential file ~p not found", PathFile) - end, - case ?GOOGLE_API_KEY of - [] -> get_access_token(); - <<"AIza", _/binary>> -> ok - end. + filelib:ensure_dir("./priv/tmp/"), + gen_server:call(ibrowse, {set_config_value, download_dir, "./priv/tmp/"}), + PathFile = filename:absname(proplists:get_value(app_credentials, + application:get_env(roster, google_api, []))), + os:putenv("GOOGLE_APPLICATION_CREDENTIALS", PathFile), + case filelib:is_file(PathFile) of + true -> + case enenra:load_credentials(PathFile) of + {ok, Creds} -> application:set_env(roster, google_creds, Creds); + Err-> ?LOG_ERROR("cannot load google credentials: ~p", [Err]) + end; + _ -> + ?LOG_ERROR("google credential file ~p not found", PathFile) + end, + case ?GOOGLE_API_KEY of + [] -> get_access_token(); + <<"AIza", _/binary>> -> ok + end. del_gs_object(<<"gs://", Path/binary>>) -> Bucket = ?GS_BUCKET, @@ -78,8 +81,8 @@ del_gs_object(<<"gs://", Path/binary>>) -> [Bucket|PathObject] -> Object = http_uri:encode(roster:binary_join(PathObject, <<"/">>)), case del_gs_object(Bucket, Object) of - ok -> roster:info(?MODULE, "object ~p is deleted successfully", [Object]), ok; - Err -> roster:error(?MODULE, "cannot delete object ~p", [Err]), Err + ok -> ?LOG_INFO("object ~p is deleted successfully", [Object]), ok; + Err -> ?LOG_ERROR("cannot delete object ~p", [Err]), Err end; [_|_] -> ok end. @@ -87,7 +90,7 @@ del_gs_object(Bucket, Object) -> case application:get_env(roster, google_creds) of {ok, Creds} -> enenra:delete_object(Bucket, Object, Creds); - undefined -> roster:error(?MODULE, "creds not found to delete GS object", []), + undefined -> ?LOG_ERROR("creds not found to delete GS object", []), {error, creds_not_found} end. uri_to_gs(<<"https://www.googleapis.com/storage/", _/binary>> = Uri) -> @@ -111,7 +114,7 @@ token_expiration(Token) -> #{<<"exp">> := ExpTime} = jsx:decode(list_to_binary(Body), [return_maps]), {Token, binary_to_integer(ExpTime)}; {error, E} -> - roster:error(?MODULE, "invalid access token: ~p", [E]), + ?LOG_ERROR("invalid access token: ~p", [E]), {error, invalid_access_token} end. @@ -125,11 +128,11 @@ get_access_token() -> "ya29"++_ = Token-> application:set_env(roster, access_token, token_expiration(Token)), Token; ErrInfo -> - roster:error(?MODULE, "invalid acess token: ~p", [ErrInfo]), + ?LOG_ERROR("invalid acess token: ~p", [ErrInfo]), application:set_env(roster, access_token, {error, invalid_access_token}), {error, invalid_access_token} end; - {Token, ExpTime} -> Token + {Token,_ExpTime} -> Token end; <<"AIza", _/binary>> = Key -> Key end. @@ -148,7 +151,7 @@ transcribe(Type, Data, Lang, Encoding, ReturnFun) when Type == short; Type == lo {long, <<"gs://", _/binary>> = GS} -> {?TRANSCRIBE_LONG_URL, #{uri => GS}}; {short, <<"gs://", _/binary>> = GS} -> {?TRANSCRIBE_URL, #{uri => GS}}; {short, _} -> {?TRANSCRIBE_URL, #{content => Data}}; - {long, _} -> roster:error(?MODULE, "invalid url for transcribe of long audio: ~p", [Data]), + {long, _} -> ?LOG_ERROR("invalid url for transcribe of long audio: ~p", [Data]), {error, invalid_data} end, case D of {error, _} -> ReturnFun(D); @@ -162,7 +165,7 @@ transcribe(Type, Data, Lang, Encoding, ReturnFun) when Type == short; Type == lo {ok, Result} -> case jsx:decode(list_to_binary(Result), [return_maps]) of #{<<"results">> := Alternatives} when Type == short -> -%% roster:info(?MODULE, "alternative transcribes: ~p", [Alternatives]), +%% ?LOG_INFO("alternative transcribes: ~p", [Alternatives]), ReturnFun( case merge_transcribe(Alternatives) of <<>> -> {error, invalid_transcribe}; @@ -170,7 +173,7 @@ transcribe(Type, Data, Lang, Encoding, ReturnFun) when Type == short; Type == lo #{<<"name">> := OperationName} -> LongRequest = {access_url(?TRANSCRIBE_OPERATIONS_URL ++ OperationName, AccessToken), AuthHeaders}, send_operation(LongRequest, OperationName, ?LONG_TRANSCRIBE_COUNTER, ?LONG_TRANSCRIBE_TIMEOUT, ReturnFun); - #{} -> roster:error(?MODULE, "no text for transcribe in audio file", []), + #{} -> ?LOG_ERROR("no text for transcribe in audio file", []), ReturnFun({error, invalid_transcribe}) end; {error, _} = Err -> ReturnFun(Err) @@ -186,7 +189,7 @@ merge_transcribe(Alternatives) -> send_operation(_LongRequest, _OperationName, 0, _Timeout, Fun) -> Fun({error, timeout}); send_operation(LongRequest, OperationName, Counter, Timeout, Fun) -> -%% roster:info(?MODULE, "transcribe counter = ~p", [Counter]), +%% ?LOG_INFO("transcribe counter = ~p", [Counter]), case roster_rest:send_request(get, LongRequest, [], []) of {ok, R} -> case jsx:decode(list_to_binary(R), [return_maps]) of @@ -198,7 +201,7 @@ send_operation(LongRequest, OperationName, Counter, Timeout, Fun) -> <<>> -> {error, invalid_transcribe}; Text -> Text end); #{<<"name">> := OperationName} -> timer:apply_after(Timeout, ?MODULE, send_operation, [LongRequest, OperationName, Counter - 1, Timeout, Fun]); - #{} = R -> roster:error("invalid long operation ~p", [R]), + #{} = R -> ?LOG_ERROR("invalid long operation ~p", [R]), Fun({error, invalid_transcribe}) end; {error, _} = Err -> Fun(Err) @@ -209,7 +212,7 @@ download(Uri, Timeout) -> case ibrowse:send_req(Uri, [], get, [], [{save_response_to_file, true}], Timeout) of {ok, "200", _, File} -> File; {ok, _, _, ErrString} = Res-> - roster:error(?MODULE, "~p", [Res]), + ?LOG_ERROR("~p", [Res]), {error, ErrString}; Error -> Error end. @@ -225,7 +228,7 @@ convert_ffmpeg(FileIn) -> Result = os:cmd(io_lib:format(?CONVERT_CMD, [FileIn, FileIn])), case filelib:is_file(FileOut = FileIn++".wav") of true -> {file, FileOut, FileIn}; - _ -> roster:error("ffmpeg error:~n~p", [Result]), + _ -> ?LOG_ERROR("ffmpeg error:~n~p", [Result]), {error, file_not_created} end. @@ -243,15 +246,16 @@ gs_upload(FileIn, Bucket, ContentType) -> case enenra:upload_file(FileOut, #object{name = Name, bucket = Bucket, contentType = ContentType, md5Hash = Md5, size = Size}, Creds) of {ok, #object{}} -> - roster:info(?MODULE, "object ~p is uploaded successfully", [GsUri = iolist_to_binary(["gs://",Bucket,"/",Name])]), + GsUri = iolist_to_binary(["gs://",Bucket,"/",Name]), + ?LOG_INFO("object ~p is uploaded successfully", [GsUri]), [file:delete(filename:absname(File)) || File<-[FileIn2, FileOut]], {gs, GsUri}; Err -> - roster:error(?MODULE, "upload file ~p to Google Storage is failed: ~p", [FileOut, Err]), + ?LOG_ERROR("upload file ~p to Google Storage is failed: ~p", [FileOut, Err]), Err end; undefined -> - roster:error(?MODULE, "google creds not found", []), + ?LOG_ERROR("google creds not found", []), {error, creds_not_found} end; Err -> Err @@ -283,16 +287,16 @@ test_transcribe() -> {ok, Binary} = file:read_file("apps/roster/priv/transcribe_test.flac"), <<_:8, _/binary>> = google_api:transcribe(short, base64:encode(Binary), <<"en-US">>). test_long_transcribe() -> - roster:info(?MODULE, "wait about 30 sec", []), + ?LOG_INFO("wait about 30 sec", []), spawn( fun() -> Pid = self(), google_api:transcribe(long, <<"gs://gcs-test-data/vr.flac">>, <<"en-US">>, <<"ENCODING_UNSPECIFIED">>, fun(Text) -> Pid ! Text end), receive - <<_:8, _/binary>> = Text -> roster:info(?MODULE, "~p", [Text]), Text + <<_:8, _/binary>> = Text -> ?LOG_INFO("~p", [Text]), Text after - 50000 -> roster:info(?MODULE, "subscribe timeout", []) + 50000 -> ?LOG_INFO("subscribe timeout", []) end end). @@ -306,15 +310,15 @@ test_convert_transcribe_long() -> test_convert_transcribe_long("https://gcs-test-data.storage.googleapis.com/vr.flac"). test_convert_transcribe_long(Url) -> {gs, GsUri} = gs_upload(Url), - roster:info(?MODULE, "\tbegin long transcribe~n\t\twait about 30 sec", []), + ?LOG_INFO("\tbegin long transcribe~n\t\twait about 30 sec", []), spawn( fun() -> Pid = self(), google_api:transcribe(long, GsUri, <<"en-US">>, <<"ENCODING_UNSPECIFIED">>, fun(Text) -> del_gs_object(GsUri), Pid ! Text end), receive - <<_:8, _/binary>> = Text -> roster:info(?MODULE, "~p", [Text]), Text + <<_:8, _/binary>> = Text -> ?LOG_INFO("~p", [Text]), Text after - 50000 -> roster:info(?MODULE, "transcribe timeout", []) + 50000 -> ?LOG_INFO("transcribe timeout", []) end end). diff --git a/apps/roster/src/api/prometheus_api.erl b/apps/roster/src/api/prometheus_api.erl index 08e3d07497bb8f4d91fc830e293cadb538e023a6..55b5693f434c67d0cb0f648656ccc6c79c0fa8d0 100644 --- a/apps/roster/src/api/prometheus_api.erl +++ b/apps/roster/src/api/prometheus_api.erl @@ -2,6 +2,7 @@ %% Prometheus metrics declaration module -module(prometheus_api). +-include_lib("kernel/include/logger.hrl"). -include_lib("roster/include/static/prometheus_text.hrl"). -include_lib("roster/include/static/prometheus_var.hrl"). @@ -20,7 +21,7 @@ init() -> ], [begin prometheus_gauge:declare([{name, Name}, {help, Desc}, {labels, Label}]), - roster:info(?MODULE, "InitMetric:~p", [Name]) + ?LOG_INFO("InitMetric:~p", [Name]) end || {Name, Desc, Label} <- GaugeMetrics], prometheus_histogram:declare([{name, ?ROSTER_REQUEST_LATENCY}, {labels, [request]}, diff --git a/apps/roster/src/api/push/android.erl b/apps/roster/src/api/push/android.erl index 3db053955524ec3e7a7bb7d901634fbd715ddad9..db15d4c91e67bce9b68001655ec2723a5f03353e 100644 --- a/apps/roster/src/api/push/android.erl +++ b/apps/roster/src/api/push/android.erl @@ -1,4 +1,5 @@ -module(android). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -export([description/0, notify/3, test_push_notification/0]). @@ -27,7 +28,7 @@ notify(MessageTitle, MessageBody, DeviceId) when is_binary(DeviceId) -> notify(_, MessageBody, DeviceId) -> %% create payload Payload = binary_to_list(iolist_to_binary(["registration_id=", DeviceId, "&data=", MessageBody, "&priority=high"])), - roster:info(?MODULE, "Payload ~p~n~n", [Payload]), + ?LOG_INFO("Payload ~p~n~n", [Payload]), Request = {?FCM_SEND_ENDPOINT, fcm_headers(), ?FCM_CONTENT_TYPE, Payload}, {_, _} = roster_rest:send_request(post, Request, [], []), ok. diff --git a/apps/roster/src/api/push/ios.erl b/apps/roster/src/api/push/ios.erl index 6d89196ed023fe583973976235986e8e9f4079fb..8a8f011671cf8093a79aa5073810319a94d0d740 100644 --- a/apps/roster/src/api/push/ios.erl +++ b/apps/roster/src/api/push/ios.erl @@ -1,4 +1,5 @@ -module(ios). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("roster/include/static/push_notification_var.hrl"). @@ -35,7 +36,7 @@ notify(A, C, T, DeviceId, SessionSettings) -> %% create ios payload string PayloadString = binary_to_list(iolist_to_binary(["{\"aps\": {\"nynja\": ", Aps, "}}"])), -% roster:info(roster, "PayloadString ~p~n~n", [PayloadString]), +% ?LOG_INFO("PayloadString ~p~n~n", [PayloadString]), %% prepare push data Payload = list_to_binary(PayloadString), @@ -43,7 +44,7 @@ notify(A, C, T, DeviceId, SessionSettings) -> FormattedDeviceId = list_to_integer(DeviceId, 16), Packet = <<0:8, 32:16/big, FormattedDeviceId:256/big, PayloadLength:16/big, Payload/binary>>, {_, {CertFile, KeyFile}} = get_bandle(SessionSettings), - roster:info(?MODULE, "CertFile: ~p", [CertFile]), + ?LOG_INFO("CertFile: ~p", [CertFile]), Options = [{certfile, path_to_pem_file(CertFile)}, {keyfile, path_to_pem_file(KeyFile)}, {mode, binary}], [send_push(Addr, Packet, Options, 1) || {_, Addr} <- get_gateway(SessionSettings)], ok. @@ -53,7 +54,7 @@ notify(A, C, T, DeviceId, SessionSettings) -> %% ------------------------------------------------------------------ send_push(Addr, Payload, Options, Attempt) -> - roster:info(?MODULE, "Addr: ~p, Attempt:~p", [Addr, Attempt]), + ?LOG_INFO("Addr: ~p, Attempt:~p", [Addr, Attempt]), %% NOTE set Duration = Attempt * 100 for tests Duration = Attempt * 500, {Status, Socket} = ssl:connect(Addr, ?APNS_PORT, Options, Duration), @@ -61,14 +62,14 @@ send_push(Addr, Payload, Options, Attempt) -> ok -> ssl:send(Socket, Payload), ssl:close(Socket), - roster:info(?MODULE, "Push sent", []); + ?LOG_INFO("Push sent", []); error -> if Attempt > 10 -> - roster:info(?MODULE, "Final error", []); + ?LOG_INFO("Final error", []); true -> timer:sleep(Duration), - roster:info(?MODULE, "Error with socket opening. Reason:~p", [Socket]), + ?LOG_INFO("Error with socket opening. Reason:~p", [Socket]), send_push(Addr, Payload, Options, Attempt + 1) end end. @@ -113,4 +114,4 @@ test_push_notification() -> #'Feature'{id = <<"ID_Bandle">>, key = <<"IOS_BANDLE">>,value = <<"com.nynja.mobile.communicator">>, group = <<"AUTH_DATA">>}], Msg = lists:concat(["Test it! ", vox_api:generate_random_data(4)]), Custom = <<"g2gSZAAHTWVzc2FnZWEQZAAFY2hhaW5oA2QAA3AycG0AAAAOMzgwNjM4MDk1MTU4XzdtAAAADjM4MDk5NDM4Mjc5OF84ampqam0AAAAOMzgwOTk0MzgyNzk4XzhtAAAADjM4MDYzODA5NTE1OF83am4GAD5BRlNeAWpqbAAAAAFoBmQABERlc2NqbQAAAARIaGhoYQBqampqamQABHNlbnQ=">>, - notify(Msg, Custom, <<"message">>, ?APNS_TEST_DEVICE_ID, SessionSettings). \ No newline at end of file + notify(Msg, Custom, <<"message">>, ?APNS_TEST_DEVICE_ID, SessionSettings). diff --git a/apps/roster/src/api/regexp_api.erl b/apps/roster/src/api/regexp_api.erl index d3f88d0a270835e0ccc30351407bcd1e2888e259..357d8ec69ace7eef436374966ec8f35fce1969af 100644 --- a/apps/roster/src/api/regexp_api.erl +++ b/apps/roster/src/api/regexp_api.erl @@ -1,4 +1,5 @@ -module(regexp_api). +-include_lib("kernel/include/logger.hrl"). -compile(export_all). %% NOTE! Max, this is just-for-fun module. I can delete it later (if you wish) @@ -11,18 +12,18 @@ validate_string(String) when String == [] orelse String == <<>> -> {ok, []}; validate_string(String) -> validate_string(String, <<"^[a-zA-Z0-9_].{1,32}$">>). validate_string(String, RegExp) -> - roster:info(?MODULE, "StringToValidate:~p", [String]), - roster:info(?MODULE, "RegExp:~p", [RegExp]), + ?LOG_INFO("StringToValidate:~p", [String]), + ?LOG_INFO("RegExp:~p", [RegExp]), Status = try case re:run(String, RegExp) of {match, _} -> ok; nomatch -> error end catch E:R -> - roster:info(?MODULE, "RegExpError:~p:~p", [E, R]), + ?LOG_INFO("RegExpError:~p:~p", [E, R]), error end, - roster:info(?MODULE, "ValidationStatus:~p", [Status]), + ?LOG_INFO("ValidationStatus:~p", [Status]), {Status, String}. %% ------------------------------------------------------------------ @@ -83,4 +84,4 @@ test_neg7() -> %% Nick with just space Str = <<" ">>, {Status, _} = validate_string(Str), - Status == error. \ No newline at end of file + Status == error. diff --git a/apps/roster/src/api/telesign_api.erl b/apps/roster/src/api/telesign_api.erl index 5df92803c068283643c022814ef48b1ddbe0e51f..de0a10e19024c47d42cc2bbc4b71a59c72800c1b 100644 --- a/apps/roster/src/api/telesign_api.erl +++ b/apps/roster/src/api/telesign_api.erl @@ -1,5 +1,6 @@ -% -*- coding: utf8 -*- +% -*- coding: utf-8 -*- -module(telesign_api). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -export([spawned_call_sms_send/3, test_sms_sending/0, spawned_telesign_voice_call/4, telesign_voice_call/4, test_voice_call/0, send_report_to_telesign/1, send_sms/2, send_sms/3, test_jwt_generation/0, @@ -42,10 +43,10 @@ l2b(X) -> list_to_binary(X). generate_jwt(Phone) -> telesign_auto_verify_generate_token(Phone). -generate_sms(Phone) -> +generate_sms(_Phone) -> { ok, - l2b(io_lib:format("~6..0s",[i2l(crypto:rand_uniform(0,999999), 10)])) + l2b(io_lib:format("~6..0s",[i2l(rand:uniform(0,999999), 10)])) }. generate_unique_value() -> @@ -83,23 +84,23 @@ decode_jwt(JWT) -> #{ <<"xid">> := Payload } = DecodedJWT, {ok, binary_to_list(Payload)} catch - E:R -> roster:info(?MODULE, "Getting XID failed!~n~p:~p", [E,R]), + E:R -> ?LOG_INFO("Getting XID failed!~n~p:~p", [E,R]), {error, R} end; _ -> - roster:info(?MODULE, "Decoding error~n ~p:~p", [Status, DecodedJWT]), + ?LOG_INFO("Decoding error~n ~p:~p", [Status, DecodedJWT]), {error, DecodedJWT} end, {ResponseStatus, ResponseData}. parse_telesign_auto_verify_response(Response) -> try - {_, EncodedResult} = mochijson:decode(Response), - {_, EncodedResultStatusStruct} = proplists:get_value("status", EncodedResult), - ResultCode = proplists:get_value("code", EncodedResultStatusStruct), - ResultDescription = proplists:get_value("description", EncodedResultStatusStruct), + EncodedResult = jsx:decode(iolist_to_binary(Response)), + EncodedResultStatusStruct = proplists:get_value(<<"status">>, EncodedResult), + ResultCode = proplists:get_value(<<"code">>, EncodedResultStatusStruct), + ResultDescription = proplists:get_value(<<"description">>, EncodedResultStatusStruct), {ok, {ResultCode, ResultDescription}} catch - E:R -> roster:info(?MODULE, "Parsing Telesing Response Failed!~n~p:~p", [E,R]), + E:R -> ?LOG_INFO("Parsing Telesing Response Failed!~n~p:~p", [E,R]), {error, R} end. @@ -122,7 +123,7 @@ validate_language(Lang) -> %% ------------------------------------------------------------------ send_report_to_telesign(Body) -> - {_, EncodedBody} = mochijson:decode(Body), + EncodedBody = jsx:decode(Body), ReferenceId = proplists:get_value("reference_id", EncodedBody), Headers = generate_telesign_headers([], ?HTTP_PUT_METHOD, iolist_to_binary([?TELESIGN_COMPLETION_RESOURCE, "/", ReferenceId])), @@ -146,7 +147,7 @@ send_sms(PhoneNumber, MessageText, SendFun) when is_integer(PhoneNumber) -> send_sms(PhoneNumber, MessageText, SendFun) when is_binary(MessageText) -> send_sms(PhoneNumber, binary_to_list(MessageText), SendFun); send_sms(PhoneNumber, MessageText, SendFun) -> - roster:info(?MODULE, "~p:SMS ~p", [PhoneNumber, MessageText]), + ?LOG_INFO("~p:SMS ~p", [PhoneNumber, MessageText]), POSTVariables = binary_to_list(iolist_to_binary(["phone_number=", PhoneNumber, "&template=", MessageText])), Headers = generate_telesign_headers(POSTVariables, ?HTTP_POST_METHOD, ?TELESIGN_SMS_VERIFY_RESOURCE), URL = binary_to_list(iolist_to_binary([?TELESIGN_API_ENDPOINT, ?TELESIGN_SMS_VERIFY_RESOURCE])), @@ -176,7 +177,7 @@ telesign_voice_call(PhoneNumber, VerifyCode, TTSMessage, Language) when is_binar telesign_voice_call(PhoneNumber, VerifyCode, TTSMessage, Language) when is_binary(Language) -> telesign_voice_call(PhoneNumber, VerifyCode, TTSMessage, binary_to_list(Language)); telesign_voice_call(PhoneNumber, VerifyCode, TTSMessage, Language) -> - roster:info(?MODULE, "~p:Call ~p:~p", [PhoneNumber, TTSMessage, VerifyCode]), + ?LOG_INFO("~p:Call ~p:~p", [PhoneNumber, TTSMessage, VerifyCode]), ValidLanguage = validate_language(Language), POSTVariables = iolist_to_binary(["phone_number=", PhoneNumber, "&language=", ValidLanguage, "&verify_code=", VerifyCode]), POSTVariablesWithTTS = case TTSMessage of @@ -227,7 +228,7 @@ telesign_auto_verify_generate_token(PhoneNumber) -> ], Key = decode_api_key(), {ok, JWT} = jwt:encode(Algorithm, Claims, Key), - roster:info(?MODULE, "~p:JWT:~p", [PhoneNumber, binary:part(JWT,0,20)]), + ?LOG_INFO("~p:JWT:~p", [PhoneNumber, binary:part(JWT,0,20)]), {ok, JWT}. check_jwt(JWT) -> @@ -304,4 +305,4 @@ test_auto_verify_status_negative_case_2() -> %% emulate Status Not Available TeleSign Error {_, JWT} = telesign_auto_verify_generate_token(?TEST_PHONE_NUMBER), {Status, _} = check_jwt(JWT), - error == Status. \ No newline at end of file + error == Status. diff --git a/apps/roster/src/api/vox_api.erl b/apps/roster/src/api/vox_api.erl index 22d4ab440402230b4ca057522a6c56399bdaf89c..e84cc566bd17046ef572404e24a67de55c64f0f8 100644 --- a/apps/roster/src/api/vox_api.erl +++ b/apps/roster/src/api/vox_api.erl @@ -1,4 +1,5 @@ -module(vox_api). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -compile(export_all). @@ -23,7 +24,7 @@ generate_random_data(Length) -> AllowedChars = "abcdefghijklmnopqrstuvwxyz1234567890", MaxLength = length(AllowedChars), lists:foldl( - fun(_, Acc) -> [lists:nth(crypto:rand_uniform(1, MaxLength), AllowedChars)] ++ Acc end, + fun(_, Acc) -> [lists:nth(rand:uniform(1, MaxLength), AllowedChars)] ++ Acc end, [], lists:seq(1, Length) ). @@ -43,11 +44,11 @@ create_voximplant_user_preprocessing(Id) -> create_participants_json(Participants) -> %% NOTE! input should be in format: [{VoxId1, PhoneNumber1, Name1}, {VoxId2, PhoneNumber2, Name2}] - lists:droplast(lists:flatten([begin - {VoxId, PhoneNumber, Name} = P, - lists:append(mochijson:encode({struct, [{"username", VoxId}, - {"phone", PhoneNumber}, {"display_name", Name}]}), ",") - end || P <- Participants])). + jsx:encode(lists:map(fun({VoxId, PhoneNumber, Name}) -> + [{<<"username">>, iolist_to_binary(VoxId)}, + {<<"phone">>, iolist_to_binary(PhoneNumber)}, + {<<"display_name">>, iolist_to_binary(Name)}] + end, Participants)). get_server_ip() -> EnvIP = application:get_env(roster, server_ip), @@ -75,8 +76,8 @@ create_voximplant_user_request(UserName, UserDisplayName, UserPassword) -> Response = case AddUserSuccess of ok -> - {_, EncodedAddUserResult} = mochijson:decode(AddUserResult), - CreatedUserId = proplists:get_value("user_id", EncodedAddUserResult), + EncodedAddUserResult = jsx:decode(iolist_to_binary(AddUserResult)), + CreatedUserId = proplists:get_value(<<"user_id">>, EncodedAddUserResult), case CreatedUserId of undefined -> {error, AddUserResult}; @@ -86,8 +87,8 @@ create_voximplant_user_request(UserName, UserDisplayName, UserPassword) -> {BindUserSuccess, BindUserResult} = roster_rest:send_request(get, {BindUserURL, []}, [], []), case BindUserSuccess of ok -> - {_, EncodedBindUserResult} = mochijson:decode(BindUserResult), - BindError = proplists:get_value("error", EncodedBindUserResult), + EncodedBindUserResult = jsx:decode(iolist_to_binary(BindUserResult)), + BindError = proplists:get_value(<<"error">>, EncodedBindUserResult), case BindError of undefined -> {ok, {[{user_name, list_to_binary(UserName)}, @@ -128,7 +129,7 @@ start_conference(Name, Participants) -> {_, ServerIP} = get_server_ip(), ConfName = iolist_to_binary(["conf_", Name]), JsonParticipants = create_participants_json(Participants), - ScriptCustomData = iolist_to_binary([<<"{\"participants\":[">>, JsonParticipants,"]}"]), + ScriptCustomData = iolist_to_binary([<<"{\"participants\":">>, JsonParticipants,"}"]), AuthParams = iolist_to_binary(["account_id=", ?VOXIMPLANT_ACCOUNT_ID, "&api_key=", ?VOXIMPLANT_API_KEY]), ConfParams = iolist_to_binary(["url=", ServerIP, "&conference_name=", ConfName, "&rule_id=", ?VOXIMPLANT_CONFERENCE_RULE_ID, "&script_custom_data=", ScriptCustomData]), RequestQueryParams = iolist_to_binary([AuthParams, "&", ConfParams]), @@ -138,17 +139,17 @@ start_conference(Name, Participants) -> Response = case StartConferenceStatus of ok -> - {_, EncodedStartConferenceResult} = mochijson:decode(StartConferenceResult), - MediaSessionAccessUrl = proplists:get_value("media_session_access_url", EncodedStartConferenceResult), + EncodedStartConferenceResult = jsx:decode(iolist_to_binary(StartConferenceResult)), + MediaSessionAccessUrl = proplists:get_value(<<"media_session_access_url">>, EncodedStartConferenceResult), case MediaSessionAccessUrl of undefined -> {error, StartConferenceResult}; _ -> - {ok, MediaSessionAccessUrl} + {ok, binary_to_list(MediaSessionAccessUrl)} end; _ -> {StartConferenceStatus, StartConferenceResult} end, - roster:info(?MODULE, "Response:~p~n", [Response]), + ?LOG_INFO("Response:~p~n", [Response]), Response. %% ------------------------------------------------------------------ @@ -171,9 +172,9 @@ test_voximplant_user_creation() -> UserName = lists:concat(["Client-", generate_random_data(6)]), UserDisplayName = UserName, UserPassword = generate_random_data(8), - roster:info(?MODULE, "UserName ~p~n", [UserName]), - roster:info(?MODULE, "UserDisplayName ~p~n", [UserDisplayName]), - roster:info(?MODULE, "UserPassword ~p~n~n", [UserPassword]), + ?LOG_INFO("UserName ~p~n", [UserName]), + ?LOG_INFO("UserDisplayName ~p~n", [UserDisplayName]), + ?LOG_INFO("UserPassword ~p~n~n", [UserPassword]), {Status, _} = create_voximplant_user_request(UserName, UserDisplayName, UserPassword), %% TODO add user deletion ok == Status. @@ -183,4 +184,4 @@ test_start_conference() -> ok == Status. test_create_participants_json() -> - create_participants_json(?TEST_PARTICIPANTS). \ No newline at end of file + create_participants_json(?TEST_PARTICIPANTS). diff --git a/apps/roster/src/micro.erl b/apps/roster/src/micro.erl index 04db6a13f350275ea84f552673f0e7730417e848..4fd3443062975559f6096d410bc852b583dc0908 100644 --- a/apps/roster/src/micro.erl +++ b/apps/roster/src/micro.erl @@ -1,5 +1,6 @@ -module(micro). -compile(export_all). +-include_lib("kernel/include/logger.hrl"). -include_lib("kvs/include/metainfo.hrl"). -include("roster.hrl"). -include("micro.hrl"). @@ -33,9 +34,7 @@ uuid_to_id(Table, Acc) -> _ -> [] end; _ -> [] - end; -uuid_to_id(Table, ?IS_UUID = Acc) -> - uuid_to_id(Table, uuid:to_binary(binary_to_list(Acc))). + end. norm_uuid(<<_:128>> = Uuid) -> list_to_binary(uuid:to_string(Uuid)); norm_uuid(?IS_UUID = Uuid) -> Uuid. @@ -50,7 +49,7 @@ phone_uuid(<<"emqttd_", _/binary>> = ClientId) -> all_accounts() -> all('LinkRoster'). all(Table) when Table == 'LinkRoster'; Table == 'LinkProfile'-> - [{Table, norm_uuid(Uuid), Id} || {Table, Uuid, Id} <- kvs:all(Table)]. + [{Table, norm_uuid(Uuid), Id} || {_T, Uuid, Id} <- kvs:all(Table)]. linked_obj(RosterId) when is_integer(RosterId) -> case kvs:get('Roster', RosterId) of @@ -67,7 +66,7 @@ phone_id(PhoneId) -> _ -> %% PhoneId is UUID in this case case kvs:get('LinkRoster', PhoneId) of {ok, #'LinkRoster'{phone_id = P}} -> P; - {error, _} -> roster:info(?MODULE, "roster ~p not found in LinkRoster", [PhoneId]), [] + {error, _} -> ?LOG_INFO("roster ~p not found in LinkRoster", [PhoneId]), [] end end. @@ -90,7 +89,7 @@ link_user(#'Profile'{phone = Phone, rosters = RosterIds}, #'Profile'{phone = Pho kvs:put(#'LinkProfile'{id = PhoneUuid, phone = Phone}), Res = lists:flatten( [case catch kvs:put(#'LinkRoster'{id = AccUuid, phone_id = roster:phone_id(Phone, RosterId)}) of - {'EXIT', Err} -> roster:error(?MODULE, "invalid roster uuid ~p for profile ~p~n~p", [AccUuid, PhoneUuid, Err]), + {'EXIT', Err} -> ?LOG_ERROR("invalid roster uuid ~p for profile ~p~n~p", [AccUuid, PhoneUuid, Err]), {error, invalid_roster}; _ -> [] end || {RosterId, #'Roster'{id = AccUuid}} <- lists:zip(RosterIds, Rosters)]), diff --git a/apps/roster/src/micro_db.erl b/apps/roster/src/micro_db.erl index a0bc0253f00209b026dc08b84b4ea1a2fc938603..55f456fcbb5c75be0ceaf1838e204ce702c19a10 100644 --- a/apps/roster/src/micro_db.erl +++ b/apps/roster/src/micro_db.erl @@ -1,6 +1,7 @@ -module(micro_db). -compile(export_all). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include("micro.hrl"). %%-include_lib("emqttd/include/emqttd.hrl"). @@ -14,7 +15,7 @@ to_rec(Table, [H|T], Acc) when is_map(H)-> to_rec(Table, [H|_] = File) when is_integer(H) -> case file:read_file(File) of {ok, Json} -> to_rec(Table, Json); - {error, _} = E -> roster:info(?MODULE, "read file ~p error: ~p", [File, E]), E + {error, _} = E -> ?LOG_INFO("read file ~p error: ~p", [File, E]), E end; to_rec(Table, Json) when is_binary(Json) -> diff --git a/apps/roster/src/processes/job.erl b/apps/roster/src/processes/job.erl index dc6ec9dfce25f7c44da44987d3b20629933e120a..6418d14de4cb407113f435bb2acc899182f9c4cf 100644 --- a/apps/roster/src/processes/job.erl +++ b/apps/roster/src/processes/job.erl @@ -25,11 +25,11 @@ action({request,'Stop'}, Proc) -> io:format("Stop Process~n"), {reply,'Action',Proc}; -action({request,'Timeout'}, #process{id=Id}=Proc) -> +action({request,'Timeout'}, #process{id=_Id}=Proc) -> io:format("Timeout Process~n"), %#process{options=Opts}=bpe:load(Id), case bpe:doc(#'Schedule'{},Proc) of - #'Schedule'{id=Current, state=TRef}=Sh -> timer:cancel(TRef); + #'Schedule'{state=TRef} -> timer:cancel(TRef); _ -> skip end, % TRef= proplists:get_value(timer,Opts,undefined), %, @@ -40,7 +40,7 @@ action({request,'Timeout'}, #process{id=Id}=Proc) -> %% io:format("Event Process~n"), %% {reply,Proc}; -action({request,'Action'}, #process{id=Id, options=Opts}=Proc) -> +action({request,'Action'}, #process{}=Proc) -> io:format("Action Process ~n"), %C= proplists:get_value(send_pid,Opts,undefined), CTimeout= case lists:keytake(timeoutEvent,1,bpe:events(Proc)) of @@ -56,12 +56,12 @@ action({request,'Action'}, #process{id=Id, options=Opts}=Proc) -> T when T =<3*?QUANT -> %io:format("Pub ~p, ~p ~n", [Id,Timeout]), catch n2o_async:pid(system,roster_bpe) ! {publish, Proc}, {reply,'Stop',Proc}; - T -> catch n2o_async:pid(system,roster_bpe) ! {timeout, Sh, Timeout}, + _T -> catch n2o_async:pid(system,roster_bpe) ! {timeout, Sh, Timeout}, {reply,'Timeout',Proc} end; _ -> {reply,'Stop',Proc} end; -action({request,'Final'}, #process{id=Id, options=Opt}=Proc) -> +action({request,'Final'}, #process{}=Proc) -> io:format(" Final~n"), %roster:send_event(C, <<>>, <<>>, #'Job'{id=Id, status=delete}), %n2o_async:pid(system,roster_bpe) ! #'Job'{id=Id, status=delete}, @@ -79,28 +79,28 @@ worker(#process{id=Id}=P) -> __ -> skip end . -worker_do({Days,Time},P) when Days >= 14 -> skip; -worker_do({Days,Time},#process{id=Id}=P) when P#process.task =:= 'Action' -> +worker_do({Days,_Time},_P) when Days >= 14 -> skip; +worker_do({_Days,_Time},#process{id=Id}=P) when P#process.task =:= 'Action' -> % catch n2o_async:pid(system,roster_bpe) ! {restart, P}, bpe:start(P, []), %bpe:complite(Id), kvs:info(?MODULE,"BPE Start: ~p~n",[Id]); -worker_do({Days,Time},#process{id=Id, options = Ops}=P) when P#process.task =:= 'Stop' -> +worker_do({_Days,_Time},#process{id=Id}=P) when P#process.task =:= 'Stop' -> bpe:start(P, []), % bpe:complite(Id), io:format("BPE Start: ~p~n",[Id]); -worker_do({Days,Time},#process{id=Id}=P) when P#process.task =:= 'Init' -> +worker_do({_Days,_Time},#process{id=Id}=P) when P#process.task =:= 'Init' -> bpe:start(P, []), % bpe:complite(Id), kvs:info(?MODULE,"BPE Start: ~p~n",[Id]); %%worker_do({Days,Time},P) when P#process.task =:= 'Final' -> kvs:info(?MODULE,"BPE Start: ~p~n",[bpe:start(P,[])]); %%worker_do({Days,Time},P) when P#process.task =:= 'FirstDelay' -> kvs:info(?MODULE,"BPE Start: ~p~n",[bpe:start(P,[])]); -worker_do({Days,Time},#process{id=Id}=P) when P#process.task =:= 'Timeout' -> +worker_do({_Days,_Time},#process{id=Id}=P) when P#process.task =:= 'Timeout' -> bpe:start(P, []), kvs:info(?MODULE,"BPE Start: ~p~n",[Id]); % bpe:complite(Id); -worker_do({Days,Time},P) ->kvs:info(?MODULE,"BPE Start: ~p~n",[P]). %skip. +worker_do({_Days,_Time},P) ->kvs:info(?MODULE,"BPE Start: ~p~n",[P]). %skip. next(Fun,R,Current,Acc) -> @@ -126,7 +126,7 @@ next(R,Current) -> clear_schedule()-> [kvs:delete('Schedule', Time) || #'Schedule'{id=Time} <- kvs:all('Schedule'), Time < roster:now_msec()]. -clear_hist(#process{id=Id}=P)-> case bpe:hist(Id) of - [H|T] -> [kvs:remove(hist,I) || #'hist'{id=I} <- T]; +clear_hist(#process{id=Id})-> case bpe:hist(Id) of + [_|T] -> [kvs:remove(hist,I) || #'hist'{id=I} <- T]; __ -> skip end. diff --git a/apps/roster/src/processes/job_process.erl b/apps/roster/src/processes/job_process.erl index 4662dec78862b63eedcea13eb04632a57630f02e..653afbf4708aaee8a459244db628cfa417b18e17 100644 --- a/apps/roster/src/processes/job_process.erl +++ b/apps/roster/src/processes/job_process.erl @@ -85,6 +85,6 @@ update_opt(#process{options = Opts}=P,Opt)-> . get_proc(Proc,Key) -> - P=#process{options = Opts}=bpe:load(Proc), + #process{options = Opts}=bpe:load(Proc), case proplists:get_value(Key,Opts,undefined) of T when is_integer(T) -> T; _ -> 0 end . diff --git a/apps/roster/src/protocol/micro_auth.erl b/apps/roster/src/protocol/micro_auth.erl index 00a0ef818f8816c38b1ce6f11c03e0bae69a22d2..241c2bf21e8b009d82c935848a2a84c6a87297a2 100644 --- a/apps/roster/src/protocol/micro_auth.erl +++ b/apps/roster/src/protocol/micro_auth.erl @@ -1,4 +1,5 @@ -module(micro_auth). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include("micro.hrl"). -include("static_auth.hrl"). @@ -18,23 +19,23 @@ init([Listeners]) -> {ok, Listeners}. description() -> "Micro Authentication Module". check(#mqtt_client{client_id = <<"sys_micro_", _/binary>> = ClientId, client_pid = ClientPid}, _Pwd, _State) -> - roster:info(?MODULE, "~p:Auth:auth(micro)/check", [ClientId]), + ?LOG_INFO("~p:Auth:auth(micro)/check", [ClientId]), emqttd_client:subscribe(ClientPid, [{roster:action_topic(ClientId), 2}]), ignore; check(#mqtt_client{client_id = <<"emqttd_", _/binary>> = ClientId, username = <<"micro">>, ws_initial_headers = [_|_] = Headers} = MC, Pwd, State) when not is_tuple(Pwd) -> - roster:info(?MODULE, "~p:Auth:auth(micro)/check/ws headers: ~p", [ClientId, Headers]), + ?LOG_INFO("~p:Auth:auth(micro)/check/ws headers: ~p", [ClientId, Headers]), Jwt = get_token(normalize(Headers)), check(MC, {token, Jwt}, State); -check(#mqtt_client{client_id = <<"emqttd_", _/binary>> = ClientId, +check(#mqtt_client{client_id = <<"emqttd_", _/binary>>, username = <<"micro">>} = MC, ?SYS_TOKEN=Pwd, State) -> check(MC, {token, Pwd}, State); check(#mqtt_client{client_id = <<"emqttd_", _/binary>> = ClientId, username = <<"micro">>, client_pid = ClientPid, will_topic = WT}, {token, Token}, _State) -> - roster:info(?MODULE, "~p:Auth:auth(micro)/check", [ClientId]), + ?LOG_INFO("~p:Auth:auth(micro)/check", [ClientId]), case WT of <<"version/", BVer/binary>> -> M = application:get_env(roster, validate_token, ?MODULE), @@ -48,42 +49,42 @@ check(#mqtt_client{client_id = <<"emqttd_", _/binary>> = ClientId, {ok, #'Auth'{user_id = PhoneId, type = logout} = Auth0} -> Auth1 = Auth0#'Auth'{last_online = roster:now_msec(), type = verified}, kvs:put(Auth1), - roster:info(?MODULE, "~p:Auth:auth(micro)/check:session created, post logout ", [ClientId]), + ?LOG_INFO("~p:Auth:auth(micro)/check:session created, post logout ", [ClientId]), AuthPid ! roster_auth:control_ver(Auth1, Ver), ok; {ok, #'Auth'{user_id = PhoneId} = Auth} -> AuthPid ! roster_auth:control_ver(Auth#'Auth'{type = []}, Ver), ok; {ok, #'Auth'{user_id = _PhoneId2} = Auth} -> - roster:info(?MODULE, "invalid account: ~p", [Auth]), + ?LOG_INFO("invalid account: ~p", [Auth]), {error, invalid_account_id}; {error, _} -> case PhoneId of - [] -> roster:info(?MODULE, "account not found: ~p", [AccUuid]), + [] -> ?LOG_INFO("account not found: ~p", [AccUuid]), {error, roster_not_found}; _ -> kvs:put(Auth = #'Auth'{client_id = ClientId, user_id = PhoneId, phone = roster:phone(PhoneId), created = roster:now_msec(), last_online = roster:now_msec(), type = verified}), - roster:info(?MODULE, "~p:Auth:auth(micro)/check:session created", [ClientId]), + ?LOG_INFO("~p:Auth:auth(micro)/check:session created", [ClientId]), AuthPid ! roster_auth:control_ver( Auth#'Auth'{client_id = ClientId, user_id = PhoneId, type = verified}, Ver), ok end end; error -> - roster:info(?MODULE, "~p:Auth:auth(micro)/check:invalid_token", [ClientId]), + ?LOG_INFO("~p:Auth:auth(micro)/check:invalid_token", [ClientId]), {error, invalid_token} end; _ -> {error, invalid_version} end; -check(#mqtt_client{client_id = <<"emqttd_", _/binary>> = ClientId, username = Username}, _, State) when Username /= <<"api">>, Username /= <<"micro">> -> - roster:info(?MODULE, "~p:Auth:auth(micro)/check:invalid_username: ~p", [ClientId, Username]), +check(#mqtt_client{client_id = <<"emqttd_", _/binary>> = ClientId, username = Username}, _,_State) when Username /= <<"api">>, Username /= <<"micro">> -> + ?LOG_INFO("~p:Auth:auth(micro)/check:invalid_username: ~p", [ClientId, Username]), {error, invalid_username}; check(_Client, _Password, _Opts) -> ignore. info(#'Auth'{type = update, client_id = <<"emqttd_", _/binary>> = ClientId, token = Token, settings = Settings} = Auth, Req, #cx{params = <<"sys_", _/binary>>, client_pid = C} = State) -> - roster:info(?MODULE, "~p:Auth/update(micro):~p", [ClientId, Auth]), + ?LOG_INFO("~p:Auth/update(micro):~p", [ClientId, Auth]), IO = case kvs:get('Auth', ClientId) of {ok, #'Auth'{type = Type} = StoredAuth} -> %% TODO merge or set settings? SendAuth = StoredAuth#'Auth'{token = Token, settings = Settings, type = update}, @@ -102,7 +103,7 @@ info(#'Auth'{type = delete, client_id = <<"emqttd_", _/binary>> = ClientId} = Au info(#'Auth'{type = logout, client_id = <<"emqttd_", _/binary>> = ClientId} = Auth, Req, #cx{params = SysClientId, client_pid = C} = State) -> IO = case kvs:get('Auth', ClientId) of {ok, #'Auth'{} = StoredAuth} -> - roster_presence:on_disconnect(A = StoredAuth#'Auth'{type = logout}, C), + roster_presence:on_disconnect(StoredAuth#'Auth'{type = logout}, C), roster:send_action(C, SysClientId, Auth), %%TODO send uuid phone and roster_id to micro? <<>>; {error, _} -> #io{code = #error{code = session_not_found}} @@ -111,18 +112,18 @@ info(#'Auth'{type = logout, client_id = <<"emqttd_", _/binary>> = ClientId} = Au info(#'Auth'{phone = Phone} = Auth, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:~p:Auth/unknown(micro):~p", [Phone, ClientId, Auth]), + ?LOG_INFO("~p:~p:Auth/unknown(micro):~p", [Phone, ClientId, Auth]), {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State}. %% micro_auth handlers proc(init, #handler{name = micro_auth} = Async) -> {ok, C} = emqttc:start_link([{client_id, <<"micro_auth">>}, {logger, {console, error}}, {reconnect, 5}]), - roster:info(?MODULE, "ASYNC AUTH PROC started", []), + ?LOG_INFO("ASYNC AUTH PROC started", []), {ok, Async#handler{state = C, seq = 0}}; proc(#'Auth'{type = {ver, Ver}, phone = Phone, client_id = <<"emqttd_", _/binary>> = ClientId}, #handler{state = C} = H) -> - roster:info(?MODULE, "~p:~p:Auth(micro)/login", [Phone, ClientId]), + ?LOG_INFO("~p:~p:Auth(micro)/login", [Phone, ClientId]), catch roster_presence:on_connect(Phone, ClientId, C, Ver), %%TODO send Auth updated session? {reply, [], H}; @@ -130,9 +131,10 @@ proc({disconnect, #'Auth'{client_id = <<"emqttd_", _/binary>>} = Auth}, #handler{state = C} = H) -> roster_presence:on_disconnect(Auth, C), {reply, [], H}; -proc(#'Auth'{type = {reg, Ver}, client_id = <<"emqttd_", _/binary>> = ClientId, phone = Phone, user_id = UserId} = Stored, +proc(#'Auth'{type = {reg, Ver}, client_id = <<"emqttd_", _/binary>> = ClientId, + phone = Phone, user_id = UserId}, #handler{state = C} = H) -> - roster:info(?MODULE, "~p:~p:Auth(micro)/login(created session)", [Phone, ClientId]), + ?LOG_INFO("~p:~p:Auth(micro)/login(created session)", [Phone, ClientId]), roster_presence:on_verify(ClientId, UserId), roster_presence:on_connect(Phone, ClientId, C, Ver), {reply, [], H}; @@ -140,7 +142,7 @@ proc(#'Auth'{type = {reg, Ver}, client_id = <<"emqttd_", _/binary>> = ClientId, proc({mqttc, C, connected}, State = #handler{state = C, seq = S}) -> {ok, State#handler{seq = S + 1}}; proc({mqttc, _C, disconnected}, State) -> {ok, State}; proc(Unknown, #handler{} = H) -> - roster:info(?MODULE, "invalid auth data :~p", [Unknown]), + ?LOG_INFO("invalid auth data :~p", [Unknown]), {reply, [], H}. %% JWT parser without public key @@ -150,7 +152,7 @@ validate_token(Token) -> #{<<"sub">> := Sub} = Payload, {ok, base64:decode(Sub)} catch Err:Rea -> - roster:error(?MODULE,"Catch invalid token :~p~n",[n2o:stack_trace(Err,Rea)]), + ?LOG_ERROR("Catch invalid token :~p~n",[n2o:stack_trace(Err,Rea)]), error end. normalize(Headers) -> normalize(Headers, []). @@ -219,4 +221,4 @@ get_token(Headers) -> %% case kvs:get('LinkRoster', AccId) of %% {ok, #'LinkRoster'{phone_id = PhoneId}} -> roster:roster_id(PhoneId); %% _ -> [] -%% end. \ No newline at end of file +%% end. diff --git a/apps/roster/src/protocol/micro_profile.erl b/apps/roster/src/protocol/micro_profile.erl index 64b9094c4b5ed1d401d37f738afda6055d72fab2..120a941d0150287c56cb280a3512ba5d36e5a049 100644 --- a/apps/roster/src/protocol/micro_profile.erl +++ b/apps/roster/src/protocol/micro_profile.erl @@ -1,4 +1,5 @@ -module(micro_profile). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include("micro.hrl"). -include_lib("n2o/include/n2o.hrl"). @@ -7,10 +8,10 @@ info(#'Profile'{phone=LinkPhone, rosters=Rosters, status=create, settings = Settings} = Profile, Req, %% TODO are Rosters sent? #cx{params = ClientId = <<"sys_", _/binary>>, client_pid = C} = State) -> Phone = micro:uuid_to_id('LinkProfile', LinkPhone), - roster:info(?MODULE, "~p:~p:Profile/create:~s", [LinkPhone,ClientId,Profile]), + ?LOG_INFO("~p:~p:Profile/create:~s", [LinkPhone,ClientId,Profile]), IO2 = case kvs:get('Profile', Phone) of {ok, #'Profile'{}} -> - roster:info(?MODULE, "~p:~p:Profile/already_exist", [LinkPhone, ClientId]), + ?LOG_INFO("~p:~p:Profile/already_exist", [LinkPhone, ClientId]), #io{code = #error{code = already_exist}, data = LinkPhone}; _ -> OldPhone = roster:get_data_val(<<"PHONE">>, Settings), @@ -51,13 +52,13 @@ info(#'Profile'{phone=LinkPhone, rosters=Rosters, status=create, settings = Sett end end end, - roster:info(?MODULE, "~p:~p:Profile/create.result:~p", [LinkPhone,ClientId, IO2]), + ?LOG_INFO("~p:~p:Profile/create.result:~p", [LinkPhone,ClientId, IO2]), {reply, {bert, IO2}, Req, State}; info(#'Profile'{phone=LinkPhone, rosters=[], status = update} = Profile, Req, #cx{params = ClientId = <<"sys_", _/binary>>, client_pid = C} = State) -> Phone = micro:uuid_to_id('LinkProfile', LinkPhone), - roster:info(?MODULE, "~p:~p:Profile(micro)/update:~s", [LinkPhone,ClientId,io_lib:format("~p",[Profile])]), + ?LOG_INFO("~p:~p:Profile(micro)/update:~s", [LinkPhone,ClientId,io_lib:format("~p",[Profile])]), IO = case kvs:get('Profile', Phone) of {ok, #'Profile'{phone = Phone} = StoredProfile} -> P = roster:patch_profile(StoredProfile, Profile), @@ -70,12 +71,12 @@ info(#'Profile'{phone=LinkPhone, rosters=[], status = update} = Profile, Req, {reply, {bert, IO}, Req, State}; info(#'Profile'{status = remove, phone=UUID, rosters =Accounts }=Data, Req, - #cx{params = ClientId= <<"sys_", _/binary>>, client_pid = C} = State) -> - roster:info(?MODULE, "~p:~p:Accounts/remove:~s", [UUID,ClientId,io_lib:format("~p",[Data])]), + #cx{params = ClientId= <<"sys_", _/binary>>} = State) -> + ?LOG_INFO("~p:~p:Accounts/remove:~s", [UUID,ClientId,io_lib:format("~p",[Data])]), Rosters = lists:flatten([begin [_,RId]=roster:parts_phone_id(micro:uuid_to_id('LinkRoster',A)), RId end || A <- Accounts]), UID = micro:uuid_to_id('LinkProfile',UUID), case roster_profile:info(#'Profile'{status = delete, phone=UID, rosters = [_|_]=Rosters}, Req,State) of - {reply, {bert, {io, {error, R}, <<>>}}, Req, State}=E -> E; + {reply, {bert, {io, {error, _R}, <<>>}}, Req, State}=E -> E; {reply, {bert, P=#'Profile'{phone=UID, rosters = NewRosters}}, Req, State} -> Accs=[ begin kvs:delete('LinkRoster', Id), micro:norm_uuid(Id) end || RosterId <- Rosters--NewRosters, #'LinkRoster'{id=Id} <- kvs:index('LinkRoster',phone_id,roster:phone_id(UID,RosterId))], @@ -84,5 +85,5 @@ info(#'Profile'{status = remove, phone=UUID, rosters =Accounts }=Data, Req, info(#'Profile'{phone = LinkPhone} = Profile, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Profile/unknown:~p", [ClientId, Profile]), + ?LOG_INFO("~p:Profile/unknown:~p", [ClientId, Profile]), {reply, {bert, #io{code = #error{code = invalid_data}, data = LinkPhone}}, Req, State}. diff --git a/apps/roster/src/protocol/micro_roster.erl b/apps/roster/src/protocol/micro_roster.erl index 29762e223ff21f50e41b26a6adbc2b9968143cac..9468a61d93af17611acf85ef9e674fa23ae38e3f 100644 --- a/apps/roster/src/protocol/micro_roster.erl +++ b/apps/roster/src/protocol/micro_roster.erl @@ -1,4 +1,5 @@ -module(micro_roster). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include("micro.hrl"). -include_lib("n2o/include/n2o.hrl"). @@ -10,7 +11,7 @@ info(#'Roster'{status = add, phone = LinkPhone, nick = Nick, id = LinkRosterId} #cx{params = <<"sys_", _/binary>> = ClientId} = State) -> Phone = micro:uuid_to_id('LinkProfile', LinkPhone), PhoneId = micro:uuid_to_id('LinkRoster', LinkRosterId), - roster:info(?MODULE, "~p:~p:Roster(micro)/add:~s", [LinkRosterId, ClientId, io_lib:format("~p", [Roster])]), + ?LOG_INFO("~p:~p:Roster(micro)/add:~s", [LinkRosterId, ClientId, io_lib:format("~p", [Roster])]), IO = case kvs:get('Profile', Phone) of {ok, #'Profile'{rosters = RosterIds} = Profile} -> RosterId = roster:roster_id(PhoneId), @@ -42,7 +43,7 @@ info(#'Roster'{status = add, phone = LinkPhone, nick = Nick, id = LinkRosterId} info(#'Roster'{status = update, id = LinkRosterId, nick = Nick} = Roster, Req, #cx{params = <<"sys_", _/binary>> = ClientId} = State) when Nick /= []-> PhoneId = micro:uuid_to_id('LinkRoster', LinkRosterId), - roster:info(?MODULE, "~p:~p:Roster(micro)/update:~s", [LinkRosterId, ClientId, io_lib:format("~p", [Roster])]), + ?LOG_INFO("~p:~p:Roster(micro)/update:~s", [LinkRosterId, ClientId, io_lib:format("~p", [Roster])]), IO2 = case kvs:get('Roster', RosterId = roster:roster_id(PhoneId)) of {ok, #'Roster'{}} -> @@ -60,6 +61,6 @@ info(#'Roster'{status = update, id = LinkRosterId, nick = Nick} = Roster, Req, end, {reply, {bert, IO2}, Req, State}; info(#'Roster'{} = Roster, Req, #cx{params = <<"sys_", _/binary>> = ClientId} = State) -> - roster:info(?MODULE, "~p:Profile(micro)/unknown:~p", [ClientId, Roster]), + ?LOG_INFO("~p:Profile(micro)/unknown:~p", [ClientId, Roster]), {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State}. diff --git a/apps/roster/src/protocol/roster_acl.erl b/apps/roster/src/protocol/roster_acl.erl index 570bea6ef9a2b58ad953289146fff8d0f8ffd387..c07721aaa68d32fcb75e2e50c53cbe76f4227bb8 100644 --- a/apps/roster/src/protocol/roster_acl.erl +++ b/apps/roster/src/protocol/roster_acl.erl @@ -72,13 +72,13 @@ check_acl({#mqtt_client{client_id = <<"emqttd_", _/binary>> = Client}, publish, allow; check_acl({#mqtt_client{client_id = <<"emqttd_", _/binary>>}, publish, <<"auth/", _/binary>>}, #state{}) -> deny; -check_acl({#mqtt_client{client_id = <<"emqttd_", _/binary>> = Client}, publish, <<"events/", _/binary>>} = Who, #state{}) -> +check_acl({#mqtt_client{client_id = <<"emqttd_", _/binary>> = Client}, publish, <<"events/", _/binary>>}, #state{}) -> case kvs:get('Auth', Client) of {ok, #'Auth'{type = expired}} -> deny; {ok, _} -> allow; {error, _} -> deny end; -check_acl({Client, PubSub, Topic} = Who, #state{nomatch = Default}) -> +check_acl({Client, PubSub, Topic}, #state{nomatch = Default}) -> case match(Client, Topic, lookup(PubSub)) of {matched, allow} -> allow; {matched, deny} -> deny; @@ -116,4 +116,4 @@ reload_acl() -> %% @doc ACL Module Description -spec(description() -> string()). description() -> - "Roster ACL with etc/acl.conf". \ No newline at end of file + "Roster ACL with etc/acl.conf". diff --git a/apps/roster/src/protocol/roster_auth.erl b/apps/roster/src/protocol/roster_auth.erl index dadb3d88e4d548cf2a276527f1fd6d1899ba463f..0c22755a789908fd08f6944cb2a02ddd3e9fd6b8 100644 --- a/apps/roster/src/protocol/roster_auth.erl +++ b/apps/roster/src/protocol/roster_auth.erl @@ -1,4 +1,5 @@ -module(roster_auth). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include("static_auth.hrl"). %-include_lib("bpe/include/bpe.hrl"). @@ -21,13 +22,13 @@ stop() -> n2o_async:stop(system, roster_auth). init([Listeners]) -> - roster:info(?MODULE, "init/2: Listeners: ~p", [Listeners]), + ?LOG_INFO("init/2: Listeners: ~p", [Listeners]), {ok, Listeners}. description() -> "Roster Authentication Module". %%check(#mqtt_client{client_id = <<"nagios-", _/binary>> = ClientId, username = <<"api">>}, _, _) -> -%% roster:info(?MODULE, "~p:Nagios/test", [ClientId]), +%% ?LOG_INFO("~p:Nagios/test", [ClientId]), %% ignore; check(#mqtt_client{ @@ -36,17 +37,16 @@ check(#mqtt_client{ client_pid = ClientPid, will_topic = <<"version/", _BVer/binary>> }, _, _) -> - roster:info(?MODULE, "~p:Auth:reg/check", [ClientId]), + ?LOG_INFO("~p:Auth:reg/check", [ClientId]), emqttd_client:subscribe(ClientPid, [{roster:action_topic(ClientId), 2}]), ignore; check(#mqtt_client{ client_id = <<"reg_", _/binary>> = ClientId, username = <<"api">>, - client_pid = ClientPid, will_topic = _WT }, _, _) -> - roster:error(?MODULE, "~p:Auth:reg/check:error:version ~s", [ClientId, _WT]), + ?LOG_ERROR("~p:Auth:reg/check:error:version ~s", [ClientId, _WT]), {error, invalid_version}; check(#mqtt_client{ @@ -55,7 +55,7 @@ check(#mqtt_client{ }, undefined, _State) -> - roster:error(?MODULE, "~p:Auth:auth/check:error:token undefined", [ClientId]), + ?LOG_ERROR("~p:Auth:auth/check:error:token undefined", [ClientId]), {error, invalid_token}; check(#mqtt_client{ @@ -75,8 +75,8 @@ check(#mqtt_client{ } = Auth) when Type == verified; Type == []; Type == expired -> - roster:info(?MODULE, "[WS Headers]: ~p", [Headers]), - roster:info(?MODULE, "~p:Auth:auth/check:token_equal:~p~n~p", [ClientId, WT, binary:part(Token,0,min(16,size(Token)))]), + ?LOG_INFO("[WS Headers]: ~p", [Headers]), + ?LOG_INFO("~p:Auth:auth/check:token_equal:~p~n~p", [ClientId, WT, binary:part(Token,0,min(16,size(Token)))]), emqttd_client:subscribe(ClientPid, [{roster:action_topic(ClientId), 2}]), IpF = #'Feature'{ id = <>, @@ -110,7 +110,7 @@ check(#mqtt_client{ type = expired, token = element(2,roster:gen_token([],[])) }, - roster:info(?MODULE, "Auth:auth/roamToken:~p", [Auth3]), + ?LOG_INFO("Auth:auth/roamToken:~p", [Auth3]), kvs:put(Auth3), AuthPid ! Auth3#'Auth'{ type = update }, ok; @@ -155,11 +155,11 @@ check(#mqtt_client{ token = Tok, phone = Phone } = Auth) -> - roster:info(?MODULE, "~p:Auth:auth/check:token_differ~p~n~p", [ClientId, WT, binary:part(Token, 0, min(16, size(Token)))]), + ?LOG_INFO("~p:Auth:auth/check:token_differ~p~n~p", [ClientId, WT, binary:part(Token, 0, min(16, size(Token)))]), case roster:parse_token2(Token) of {error, invalid_token} -> - roster:error(?MODULE, "Auth:auth/check:token:~p.InvalidToken:~p", [binary:part(Tok, 0, 16), Token]), + ?LOG_ERROR("Auth:auth/check:token:~p.InvalidToken:~p", [binary:part(Tok, 0, 16), Token]), {error, mismatch_user_data}; OldTok -> case binary:part(Tok, 0, 10) == OldTok orelse roster:parse_token2(Tok) == OldTok of @@ -175,7 +175,7 @@ check(#mqtt_client{ kvs:put(Auth#'Auth'{token = Token, type = Type}), check(MqttClient, Token, Auth#'Auth'{token = Token, type = Type}); _ -> - roster:error(?MODULE, "Auth:auth/check:token:~p.InvalidOldToken:~p", [binary:part(Tok, 0, 16), OldTok]), + ?LOG_ERROR("Auth:auth/check:token:~p.InvalidOldToken:~p", [binary:part(Tok, 0, 16), OldTok]), {error, mismatch_user_data} end end; @@ -183,44 +183,40 @@ check(#mqtt_client{ check(#mqtt_client{ client_id = <<"emqttd_", _/binary>> = ClientId, username = <<"api">>, - client_pid = ClientPid, - will_topic = <<"version/", _BVer/binary>> = WT, - ws_initial_headers = Headers + will_topic = <<"version/", _BVer/binary>> = WT } = MqttClient, Token, State) -> - roster:info(?MODULE, "~p:Auth:auth/check:getting_auth:~p~n~p", [ClientId, WT, binary:part(Token,0,min(16,size(Token)))]), + ?LOG_INFO("~p:Auth:auth/check:getting_auth:~p~n~p", [ClientId, WT, binary:part(Token,0,min(16,size(Token)))]), %%%% case maybe_get_auth(State, ClientId) of {ok, Auth} -> check(MqttClient, Token, Auth); {error, not_found} = Error404 -> - roster:error(?MODULE, "~p:Auth/check:~p", [ClientId, Error404]), + ?LOG_ERROR("~p:Auth/check:~p", [ClientId, Error404]), Error404; Err -> - roster:error(?MODULE, "~p:Auth:auth/check:~p", [ClientId, Err]), + ?LOG_ERROR("~p:Auth:auth/check:~p", [ClientId, Err]), Err end; check(#mqtt_client{ client_id = <<"emqttd_", _/binary>> = ClientId, username = <<"api">>, - client_pid = _ClientPid, will_topic = _WT, - peername = {Ip, _}, - ws_initial_headers = Headers - } = MC, + peername = {_Ip, _} + }, _Token, _State) -> - roster:error(?MODULE, "~p:Auth:reg/check:error:version ~s", [ClientId, _WT]), + ?LOG_ERROR("~p:Auth:reg/check:error:version ~s", [ClientId, _WT]), {error, invalid_version}; check(#mqtt_client{ client_id = <<"emqttd_", _/binary>> = ClientId, username = Username }, _, - State) when Username /= <<"api">>, + _State) when Username /= <<"api">>, Username /= <<"micro">> -> - roster:info(?MODULE, "~p:Auth:auth/check:invalid_username: ~p", [ClientId, Username]), + ?LOG_INFO("~p:Auth:auth/check:invalid_username: ~p", [ClientId, Username]), {error, invalid_username}; check(_Client, _Password, _Opts) -> ignore. @@ -243,7 +239,7 @@ info(#'Auth'{ phone = Phone, client_id = RegClientId }), - roster:info(?MODULE, "~p:~p:Auth/fake", [Phone, RegClientId]), + ?LOG_INFO("~p:~p:Auth/fake", [Phone, RegClientId]), info(Auth2#'Auth'{ type = verify, token = [] @@ -275,7 +271,7 @@ info(#'Auth'{ {reply, {bert, #io{ code = case check_fake_numbers(Phone) of {ok, _} -> - roster:info(?MODULE, "~p:~p:Auth/reg.FakeNumber", [Phone, RegClientId]), + ?LOG_INFO("~p:~p:Auth/reg.FakeNumber", [Phone, RegClientId]), kvs:put(Auth#'Auth'{ sms_code = ?FAKE_SMS, attempts = ?MAX_ATTEMPTS, @@ -285,7 +281,7 @@ info(#'Auth'{ _ -> case check_whitelist(Phone) of {error, _} -> - roster:info(?MODULE, "~p:~p:Auth/reg.NotAllowed", [Phone, RegClientId]), + ?LOG_INFO("~p:~p:Auth/reg.NotAllowed", [Phone, RegClientId]), #error{code = number_not_allowed}; {ok, _} -> Fun = case Services of @@ -298,7 +294,7 @@ info(#'Auth'{ attempts = ?MAX_ATTEMPTS, client_id = RegClientId }), - roster:info(?MODULE, + ?LOG_INFO( "~p:~p:Auth/reg~p:~p", [ Phone, @@ -325,7 +321,7 @@ info(#'Auth'{ }, Req, #cx{params = <<"reg_", _/binary>> = RegClientId} = State) when DevKey /= [] -> - roster:info(?MODULE, "~p:Auth/resend", [RegClientId]), + ?LOG_INFO("~p:Auth/resend", [RegClientId]), {reply, {bert, #io{ code = case kvs:get('Auth', RegClientId) of {ok, #'Auth'{ @@ -360,7 +356,7 @@ info(#'Auth'{ }, Req, #cx{params = <<"reg_", _/binary>> = RegClientId} = State) -> - roster:info(?MODULE, "~p:~p:Auth/voice", [Phone, RegClientId]), + ?LOG_INFO("~p:~p:Auth/voice", [Phone, RegClientId]), {reply, {bert, #io{ code = case kvs:get('Auth', RegClientId) of {ok, #'Auth'{ @@ -398,7 +394,7 @@ info(#'Auth'{ } = Stored} -> case verify(Services, Code) of true -> - roster:info(?MODULE, "~p:~p:Auth/verify~p:~p", [Phone, RegClientId, Services, DevKey]), + ?LOG_INFO("~p:~p:Auth/verify~p:~p", [Phone, RegClientId, Services, DevKey]), {roster, RosterId} = case kvs:get('Profile', Phone) of {ok, #'Profile'{ @@ -423,7 +419,7 @@ info(#'Auth'{ } || #'Feature'{key = Key} = F <- Settings ], - kvs:put(P = #'Profile'{ + kvs:put( #'Profile'{ phone = Phone, rosters = [Roster], settings = NewSettings, @@ -490,7 +486,7 @@ info(#'Auth'{ }, Req, #cx{params = <<"emqttd_", _/binary>> = ClientId} = State) -> - roster:info(?MODULE, "~p:Auth/push.Request:~p:~p", [ClientId, OS, Push]), + ?LOG_INFO("~p:Auth/push.Request:~p:~p", [ClientId, OS, Push]), R = case kvs:get('Auth', ClientId) of {ok, Auth2} -> %% check for push token uniqueness @@ -504,13 +500,13 @@ info(#'Auth'{ {error, _} -> #error{code = mismatch_user_data} end, - roster:info(?MODULE, "~p:Auth/push.Response:~p", [ClientId, R]), + ?LOG_INFO("~p:Auth/push.Response:~p", [ClientId, R]), {reply, {bert, #io{code = R}}, Req, State}; info(#'Auth'{type = clear}, Req, #cx{params = <<"emqttd_", _/binary>> = ClientId} = State) -> - roster:info(?MODULE, "~p:Auth/clear", [ClientId]), + ?LOG_INFO("~p:Auth/clear", [ClientId]), IO = #io{ code = case kvs:get('Auth', ClientId) of {ok, #'Auth'{user_id = PhoneId}} -> @@ -539,7 +535,7 @@ info(#'Auth'{ } = Auth, Req, #cx{params = <<"emqttd_", _/binary>> = ClientId} = State) -> - roster:info(?MODULE, "~p:Auth/delete", [ClientId]), + ?LOG_INFO("~p:Auth/delete", [ClientId]), IO = case kvs:get('Auth', ClientId) of {ok, #'Auth'{user_id = PhoneId}} -> case [roster:force_logout(A) || #'Auth'{client_id = ClId} = A <- kvs:index('Auth', user_id, PhoneId), ClId == ClientId2] of @@ -556,7 +552,7 @@ info(#'Auth'{ info(#'Auth'{type = get}, Req, #cx{params = <<"emqttd_", _/binary>> = ClientId} = State) -> - roster:info(?MODULE, "~p:Auth/get", [ClientId]), + ?LOG_INFO("~p:Auth/get", [ClientId]), IO = case kvs:get('Auth', ClientId) of {ok, #'Auth'{user_id = PhoneId}} -> [ @@ -571,7 +567,7 @@ info(#'Auth'{type = get}, info(#'Auth'{type = logout}, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Auth/logout", [ClientId]), + ?LOG_INFO("~p:Auth/logout", [ClientId]), D = case ClientId of <<"emqttd_", _/binary>> -> case kvs:get('Auth', ClientId) of @@ -589,7 +585,7 @@ info(#'Auth'{type = logout}, info(#'Auth'{phone = Phone} = Auth, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:~p:Auth/unknown:~p", [Phone, ClientId, Auth]), + ?LOG_INFO("~p:~p:Auth/unknown:~p", [Phone, ClientId, Auth]), {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State}. %% roster_auth handlers @@ -604,9 +600,9 @@ proc(init, #handler{name = roster_auth} = Async) -> {ok, _} -> ok; Err -> - roster:error(?MODULE, "error:~p:locus", [Err]) + ?LOG_ERROR("error:~p:locus", [Err]) end, - roster:info(?MODULE, "ASYNC AUTH PROC:~p started", [C]), + ?LOG_INFO("ASYNC AUTH PROC:~p started", [C]), {ok, Async#handler{state = C, seq = 0}}; %%proc(#'Auth'{type = verified, client_id = <<"emqttd_", Post/binary>> = ClientId, user_id = UserId} = Stored, #handler{} = H) -> @@ -633,7 +629,7 @@ proc(#'Auth'{ }, #handler{state = C} = H) -> NewC = roster:restart_module(C, roster_auth), - roster:info(?MODULE, "~p:~p:Auth/login:~p", [Phone, ClientId, binary:part(Token, 0, 16)]), + ?LOG_INFO("~p:~p:Auth/login:~p", [Phone, ClientId, binary:part(Token, 0, 16)]), catch roster_presence:on_connect(Phone, ClientId, NewC, Ver), {reply, [], H#handler{state = NewC}}; proc(#'Auth'{ @@ -644,7 +640,7 @@ proc(#'Auth'{ {ok, #'Auth'{token = T}} = kvs:get('Auth', ClientId), %NewC=roster:restart_module(C,roster_auth), {'Token', Token2} = roster:gen_token([], binary:part(T, 0, 10)), - roster:info(?MODULE, "~p:Auth/update:~p", [ClientId, binary:part(Token2, 0, 16)]), + ?LOG_INFO("~p:Auth/update:~p", [ClientId, binary:part(Token2, 0, 16)]), catch n2o_vnode:send( C, roster:action_topic(ClientId), @@ -668,7 +664,7 @@ proc({vox, #'Profile'{phone = Phone, services = Services} = Profile, Cli}, proplists:get_value(Key, VoxResponse1) || Key <- [user_id, user_name, user_password] ], - roster:info(?MODULE, "~p:~p:vox:~p:~p:~p", [Phone, Cli, Id, U, P]), + ?LOG_INFO("~p:~p:vox:~p:~p:~p", [Phone, Cli, Id, U, P]), #'Service'{ type = vox, id = Id, @@ -695,7 +691,7 @@ proc({vox, #'Profile'{phone = Phone, services = Services} = Profile, Cli}, end, {reply, [], H}; proc({sms, Phone, SmsCode}, #handler{} = H) -> - roster:info(?MODULE, "~p:sms:~p", [Phone, SmsCode]), + ?LOG_INFO("~p:sms:~p", [Phone, SmsCode]), Half = size(SmsCode) - round(size(SmsCode) / 2), [Codes1, Codes2] = [binary:part(SmsCode, S, Half) || S <- [0, Half]], ClientSms = <<"Welcome to Nynja! Your verification code: ", Codes1/binary, " ", Codes2/binary>>, @@ -709,7 +705,7 @@ proc({sms, Phone, SmsCode}, #handler{} = H) -> {reply, [], H}; proc({voice, Phone, SmsCode, Lang}, #handler{} = H) -> - roster:info(?MODULE, "~p:telesign:~p:~p", [Phone, SmsCode, Lang]), + ?LOG_INFO("~p:telesign:~p:~p", [Phone, SmsCode, Lang]), % telesign_api:telesign_voice_call(Phone, SmsCode, [], Lang), {reply, [], H}; @@ -719,7 +715,7 @@ proc({mqttc, C, connected}, proc({mqttc, _C, disconnected}, State) -> {ok, State}; proc(Unknown, #handler{} = H) -> - roster:info(?MODULE, "invalid auth data :~p", [Unknown]), + ?LOG_INFO("invalid auth data :~p", [Unknown]), {reply, [], H}. client(<<"reg_", Post/binary>>) -> @@ -783,7 +779,7 @@ delete_duplicated_tokens(ClientId, PushToken) -> case SessionId of ClientId -> skip; _ -> - roster:info(?MODULE, "DeleteOldAuthSession:~p:~p", [PushToken, SessionId]), + ?LOG_INFO("DeleteOldAuthSession:~p:~p", [PushToken, SessionId]), kvs:delete('Auth', SessionId) end || #'Auth'{client_id = SessionId} <- kvs:index('Auth', push, PushToken) diff --git a/apps/roster/src/protocol/roster_bpe.erl b/apps/roster/src/protocol/roster_bpe.erl index 2e105a5bc3f01114e617b00951706c852e032d70..96b89d4649b5d02ee2ad1fb87630ae56dcea653d 100644 --- a/apps/roster/src/protocol/roster_bpe.erl +++ b/apps/roster/src/protocol/roster_bpe.erl @@ -1,4 +1,5 @@ -module(roster_bpe). +-include_lib("kernel/include/logger.hrl"). -include_lib("roster.hrl"). %%-include_lib("kvs/include/feed.hrl"). %%-include_lib("kvs/include/kvs.hrl"). @@ -16,7 +17,7 @@ start() -> n2o_async:start(#handler{module = ?MODULE, class = system, group = ro info(#'History'{roster_id = Roster, feed = Feed, size = N, entity_id = MId, status = get} = Data, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:~p:History/get:~p", [Roster, ClientId, Feed]), + ?LOG_INFO("~p:~p:History/get:~p", [Roster, ClientId, Feed]), PhoneId = roster:phone_id(ClientId), %FId = roster:roster_id(Roster), %N0=case N of Size when N < -8 -> -7; _ when MId=:=0 -> -7; Size when N > 8 -> 7; Size -> Size end, @@ -25,37 +26,40 @@ info(#'History'{roster_id = Roster, feed = Feed, size = N, entity_id = MId, stat case kvs:get(writer, Feed) of {ok, #writer{first = #'Job'{context = RID}} = W} -> {RID, W}; _ -> {0, 0} end; - _ -> roster:info(?MODULE, "~p:Feed/error:~p", [Roster, Feed]), {0, -1} + _ -> ?LOG_INFO("~p:Feed/error:~p", [Roster, Feed]), {0, -1} end, D = case kvs:get(reader, R) of {ok, #reader{cache = []} = Rdr} when MId == 0 -> - #writer{count = Count, cache = #'Job'{id = Id}} = Writer, + #writer{count = Count, cache = #'Job'{}} = Writer, case Count of - 0 -> n2o:error(?MODULE, "There are not messages: ~p:~n", [Writer]), {[], 0}, <<>>; - _ -> #reader{cache = {_, RId}} = kvs_stream:save(kvs_stream:top((Rdr#reader{dir = 0}))), + 0 -> + ?LOG_ERROR("There are not messages: ~p:~n", [Writer]), + <<>>; + _ -> + #reader{cache = {_, RId}} = kvs_stream:save(kvs_stream:top((Rdr#reader{dir = 0}))), %{ok,#'Job'{prev = ID }}=kvs:get('Job',RId), Js = roster:exclude({'Job', RId}, #'Job'.status, -?JOBS_LIMIT, 0, []), Data#'History'{data = Js} %[ case S of complete-> J#'Job'{data=[]}; _ -> J end || J=#'Job'{status=S}<-Js]} end; - {ok, #reader{pos = Pos} = Reader} when N == [], MId == 0 -> - #writer{count = Top, cache = #'Job'{id = Id}} = Writer, + {ok, #reader{pos =_Pos} =_Reader} when N == [], MId == 0 -> + #writer{count =_Top, cache = #'Job'{id = Id}} = Writer, %% #reader{cache={_,RId} }=kvs_stream:drop(Reader#reader{args = Pos, dir=1}), %% #reader{cache={_,RId} }=RR=kvs_stream:bot(Reader), % {ok,#'Job'{prev = ID }}=kvs:get('Job',RId), Js = roster:exclude({'Job', Id}, #'Job'.status, -?JOBS_LIMIT, 0, []), - NewP = Top, Data#'History'{data = Js}; - {ok, #reader{pos = Pos} = Reader} when N == [], MId =/= 0 -> - #writer{count = Top} = Writer, + Data#'History'{data = Js}; + {ok, #reader{}} when N == [], MId =/= 0 -> + #writer{} = Writer, %{ok,#'Job'{prev = ID }}=kvs:get('Job',MId), Js = roster:exclude({'Job', MId}, #'Job'.status, ?JOBS_LIMIT, 0, []), NewP = length(Js), Data#'History'{data = Js, size = NewP}; - {ok, #reader{pos = Pos}} when N < 0, MId =/= 0 -> + {ok, #reader{}} when N < 0, MId =/= 0 -> {ok, #'Job'{next = ID}} = kvs:get('Job', MId), Js = roster:exclude({'Job', ID}, #'Job'.status, N, 0, []), NewP = length(Js), Data#'History'{data = Js, size = NewP}; - {ok, #reader{pos = Pos}} when MId =/= 0 -> + {ok, #reader{}} when MId =/= 0 -> {ok, #'Job'{prev = ID}} = kvs:get('Job', MId), Js = roster:exclude({'Job', ID}, #'Job'.status, N, 0, []), NewP = length(Js), @@ -67,8 +71,8 @@ info(#'History'{roster_id = Roster, feed = Feed, size = N, entity_id = MId, stat {reply, {bert, D}, Req, State}; info(#'Job'{id = [], feed_id = {act, <<"publish">>, PhoneId} = Feed, time = [], data = [#'Message'{} | _] = Msgs, status = init} = RequestData, R, - #cx{params = ClientId, client_pid = C, session = Token} = S) -> - roster:info(?MODULE, "Job/init:~p", [Feed]), + #cx{params = ClientId} = S) -> + ?LOG_INFO("Job/init:~p", [Feed]), %J = J0#'Job'{id = Id = kvs:next_id('Job', 1)}, Replay = case roster:phone_id(ClientId) of PhoneId -> %roster:restart_module(Pid,?MODULE), @@ -79,8 +83,8 @@ info(#'Job'{id = [], feed_id = {act, <<"publish">>, PhoneId} = Feed, time = [], {reply, {bert, Replay}, R, S}; info(#'Job'{id = [], feed_id = {act, <<"roster">>,PhoneId} = Feed, time = [], data = Actions, status = init} = RequestData, R, - #cx{params = ClientId, client_pid = C, session = Token} = S) -> - roster:info(?MODULE, "Job/init:~p", [Feed]), + #cx{params = ClientId} = S) -> + ?LOG_INFO("Job/init:~p", [Feed]), %J = J0#'Job'{id = Id = kvs:next_id('Job', 1)}, Replay = case roster:phone_id(ClientId) of PhoneId ->[ action(A, R, S) || A <-Actions]; @@ -91,10 +95,10 @@ info(#'Job'{id = [], feed_id = {act, <<"roster">>,PhoneId} = Feed, time = [], da info(#'Job'{id = [], feed_id = {act, <<"publish">>, PhoneId} = Feed, time = Time, data = [#'Message'{} | _] = D0, status = init} = J0, R, - #cx{params = ClientId, client_pid = C} = S) -> + #cx{params = ClientId} = S) -> %J = J0#'Job'{id = Id = kvs:next_id('Job', 1)}, Current = roster:now_msec(), - roster:info(?MODULE, "Create Job :~p :Time ~p", [Feed, roster:msToUT(Time)]), + ?LOG_INFO("Create Job :~p :Time ~p", [Feed, roster:msToUT(Time)]), % Size=length(D0), %{D,T}=lists:split( case Size > ?JOBS_MS_LIMIT of true -> ?JOBS_MS_LIMIT; false -> Size end, D0), D = D0, @@ -115,9 +119,9 @@ info(#'Job'{id = [], feed_id = {act, <<"publish">>, PhoneId} = Feed, time = Time end, {reply, {bert, Replay}, R, S}; -info(#'Job'{id = Id, time = T0, feed_id = {act, <<"publish">>, PhoneId} = Feed, data = D, settings = Stt, status = update} = RequestData, R, +info(#'Job'{id = Id, time = T0, feed_id = {act, <<"publish">>, PhoneId}, data = D, settings = Stt, status = update} = RequestData, R, #cx{params = ClientId, client_pid = C} = S) -> - roster:info(?MODULE, "UPDATE Time:~p", [roster:msToUT(T0)]), + ?LOG_INFO("UPDATE Time:~p", [roster:msToUT(T0)]), Current = roster:now_msec(), Replay = case roster:phone_id(ClientId) of PhoneId when T0 > Current -> @@ -150,13 +154,13 @@ info(#'Job'{id = Id, time = T0, feed_id = {act, <<"publish">>, PhoneId} = Feed, {reply, {bert, Replay}, R, S}; -info(#'Job'{id = Id, proc = Proc, data = <<"stop">>, status = update} = M, R, S) -> - roster:info(?MODULE, "STOP:~w", [M]), +info(#'Job'{proc = Proc, data = <<"stop">>, status = update} = M, R, S) -> + ?LOG_INFO("STOP:~w", [M]), Docs = #'act'{data = <<"stop">>}, {reply, {bert, {io, bpe:amend(Proc, Docs, noflow), <<>>}}, R, S}; info(#'Job'{proc = Proc, data = D, time = Time, events = [], status = restart} = J0, R, S) -> - roster:info(?MODULE, "UPDATE :~w", [J0]), + ?LOG_INFO("UPDATE :~w", [J0]), P = bpe:load(Proc), % bpe:find_pid(Proc) ! {'DOWN', <<>>, <<>>, <<>>, <<>>}, T = calendar:time_difference(erlang:universaltime(), roster:msToUT(Time)), @@ -166,10 +170,10 @@ info(#'Job'{proc = Proc, data = D, time = Time, events = [], status = restart} = info(#'Job'{id = Id, feed_id = {act, <<"publish">>, UID} = Feed, status = delete} = RequestData, R, #cx{params = ClientId, client_pid = C} = S) -> - roster:info(?MODULE, "JOB delete:~w", [Feed]), + ?LOG_INFO("JOB delete:~w", [Feed]), PhoneId = roster:phone_id(ClientId), D = case kvs:get('Job', Id) of - {ok, #'Job'{feed_id = {act, <<"publish">>, PhoneId}, proc = Proc, time = Time} = J} when UID == PhoneId -> + {ok, #'Job'{feed_id = {act, <<"publish">>, PhoneId}} = J} when UID == PhoneId -> Job = J#'Job'{status = delete}, case kvs_stream:load_writer(Feed) of #writer{cache = #'Job'{id = JID}} = W when JID == Id -> @@ -184,18 +188,18 @@ info(#'Job'{id = Id, feed_id = {act, <<"publish">>, UID} = Feed, status = delete {reply, {bert, D}, R, S}; info(#'Job'{feed_id = Feed} = RequestData, R, S) -> - roster:info(?MODULE, "~p::Job/unknown", [Feed]), + ?LOG_INFO("~p::Job/unknown", [Feed]), {reply, {bert, roster_channel_helper:error_response_400(RequestData) %% #io{code = #error{code = invalid_data}} }, R, S}; -info(M, R, S) -> roster:info(?MODULE, "UNKNOWN:~w", [M]), {unknown, M, R, S}. +info(M, R, S) -> ?LOG_INFO("UNKNOWN:~w", [M]), {unknown, M, R, S}. proc(init, #handler{name = roster_bpe} = Async) -> {ok, C} = emqttc:start_link([{client_id, <<"sys_bpe">>}, {logger, {console, error}}, {reconnect, 5}]), Table = process, Proc = case kvs:get(feed, Table) of - {ok, #feed{top = Top} = Feed} -> + {ok, #feed{top = Top} =_Feed} -> %% ProcIDs=kvs:fold(fun(#process{name=Name}=A,Acc) -> %% case Name of 'Shedule' -> [element(2,A)|Acc]; _ -> Acc end end,[], %% Table, Feed#feed.top,undefined, #iterator.prev,#kvs{mod=store_mnesia}), @@ -219,18 +223,18 @@ proc(init, #handler{name = roster_bpe} = Async) -> {ok, Id0} = bpe:start(job:def(#'Schedule'{id = {0, {0, 0, 1}}, proc = <<"publish">>}), []), bpe:complete(Id0), Id0 end, % bpe:amend(Proc,{send_pid, C},noflow), - roster:info(?MODULE, "ASYNC BPE started: ~p; ~p", [C, Proc]), + ?LOG_INFO("ASYNC BPE started: ~p; ~p", [C, Proc]), % Proc is transfered through state {ok, Async#handler{state = {C, Proc}, seq = 0}}; -proc({update, #'Job'{id = Id0, time = Time, feed_id = {act, <<"publish">>, PhoneId} = Feed, data = D} = J, {OldTime, Limit}, - #cx{params = ClientId} = CX}, #handler{state = {C, Proc}} = H) when Limit < 8 -> +proc({update, #'Job'{id = Id0, time = Time, feed_id = {act, <<"publish">>, PhoneId} = Feed} = J, {OldTime, Limit}, + #cx{} = CX}, #handler{state = {C, Proc}} = H) when Limit < 8 -> roster:restart_module(C,roster_bpe), P = bpe:load(Proc), {NewJ0, Id} = case Id0 of [] -> NId = kvs:next_id('Job', 1), {J#'Job'{id = NId, proc = Proc}, NId}; _ -> {J#'Job'{proc = Proc}, Id0} end, {T, NewD, Par} = case bpe:doc(#'Schedule'{}, P) of - #'Schedule'{id = Current, data = Jobs} = Sh when abs(Time - Current) =< 2 * ?QUANT -> + #'Schedule'{id = Current, data = Jobs} =_Sh when abs(Time - Current) =< 2 * ?QUANT -> %bpe:amend(Proc, NewSh = Sh#'Schedule'{data = lists:usort(Jobs++[{'Job', Id}])},noflow), {Current, Jobs ++ [{'Job', Id}], noflow}; #'Schedule'{id = Current, data = Jobs} when Time - Current < -2 * ?QUANT orelse Jobs == <<"stop">> -> @@ -239,7 +243,7 @@ proc({update, #'Job'{id = Id0, time = Time, feed_id = {act, <<"publish">>, Phone %% #'Schedule'{id = Current, data = Jobs} = Sh when Time - Current < -2*?QUANT -> %% %bpe:amend(Proc, NewSh = #'Schedule'{id = Time, proc =Proc, data = [{'Job', Id}]}), %% {Time,[{'Job', Id}],[]}; - #'Schedule'{id = Current, data = Jobs} = Sh when abs(OldTime - Current) =< 2 * ?QUANT -> + #'Schedule'{id = Current, data = Jobs} =_Sh when abs(OldTime - Current) =< 2 * ?QUANT -> %bpe:amend(Proc, NewSh = Sh#'Schedule'{data =Jobs-- [{'Job', Id}]}), {Time, Jobs-- [{'Job', Id}], []}; [] -> @@ -249,10 +253,10 @@ proc({update, #'Job'{id = Id0, time = Time, feed_id = {act, <<"publish">>, Phone {Time, [], []} end, NewJ = NewJ0#'Job'{time = T, proc = Proc}, - roster:info(?MODULE, "Job started: ~p;~p", [Id, Limit]), + ?LOG_INFO("Job started: ~p;~p", [Id, Limit]), case kvs_stream:load_writer(Feed) of #writer{} = W when OldTime == 0 -> - #writer{first = #'Job'{context = R}} = kvs_stream:save(kvs_stream:add(W#writer{args = NewJ})); + #writer{first = #'Job'{}} = kvs_stream:save(kvs_stream:add(W#writer{args = NewJ})); #writer{cache = #'Job'{id = JID}} = W when JID == Id -> kvs_stream:save(W#writer{cache = NewJ}), kvs:put(NewJ); _ -> kvs:put(NewJ) @@ -268,29 +272,29 @@ proc({update, #'Job'{id = Id0, time = Time, feed_id = {act, <<"publish">>, Phone [] -> roster:send_ses(C, roster:phone(PhoneId), case OldTime of 0 -> NewJ; _ -> NewJ#'Job'{status = update} end), {reply, [], H#handler{state = {C, Proc}}}; - _ -> try amend(Proc, NewSh = #'Schedule'{id = T, proc = Proc, data = NewD}, Par), + _ -> try amend(Proc, #'Schedule'{id = T, proc = Proc, data = NewD}, Par), roster:send_ses(C, roster:phone(PhoneId), case OldTime of 0 -> NewJ; _ -> NewJ#'Job'{status = update} end), {reply, [], H#handler{state = {C, Proc}}} - catch Err:Rea -> n2o:error(?MODULE, "Catch:~p~n", [n2o:stack_trace(Err, Rea)]), + catch Err:Rea -> ?LOG_ERROR("Catch:~p~n", [n2o:stack_trace(Err, Rea)]), restart_bpe(P, C), timer:sleep(300), bpe:complete(Proc), - proc({update, NewJ0, {OldTime, Limit + 1}, CX}, #handler{state = {C, Proc}, seq = S} = H) + proc({update, NewJ0, {OldTime, Limit + 1}, CX}, #handler{state = {C, Proc}} = H) end end; -proc({update, #'Job'{id = Id0, time = Time, feed_id = {act, <<"publish">>, PhoneId} = Feed, data = D} = J, {OldTime, Limit}, - #cx{params = ClientId} = S}, #handler{state = {C, Proc}} = H) -> - roster:info(?MODULE, "BPE update limit ~p; ~p", [C, Proc]), +proc({update, #'Job'{feed_id = {act, <<"publish">>,_PhoneId}}, {_OldTime,_Limit}, + #cx{} = _S}, #handler{state = {C, Proc}} = H) -> + ?LOG_INFO("BPE update limit ~p; ~p", [C, Proc]), {reply, [], H}; proc({publish, #process{id = Id} = P}, #handler{state = {C, Proc}, seq = S} = H) -> - roster:info(?MODULE, "BPE PROC publish: ~p", [Id]), + ?LOG_INFO("BPE PROC publish: ~p", [Id]), roster:restart_module(C,roster_bpe), %#act{data=Jobs}=bpe:doc(#act{},Proc), %{ok, #'Schedule'{id=Tsh, data=Jobs}}=kvs:get('Schedule',Current), Curr= case bpe:doc(#'Schedule'{}, P) of - #'Schedule'{id = Current, data = [{'Job', I}|_]=Jobs} -> + #'Schedule'{id = Current, data = [{'Job',_I}|_]=Jobs} -> Msgs = lists:flatten([begin case kvs:get('Job', I) of {ok, #'Job'{feed_id = {act, <<"publish">>, PhoneId} = Feed, status = pending, data = Ms} = J} -> NewJ = J#'Job'{data = [], status = complete}, @@ -302,15 +306,15 @@ proc({publish, #process{id = Id} = P}, #handler{state = {C, Proc}, seq = S} = H) roster:send_ses(C, roster:phone(PhoneId), NewJ), Ms; _ -> [] end end || {'Job', I} <- Jobs]), - [begin roster:send_event(C, <<"sys_bpe">>, <<>>, Msg, roster:get_vnode(<<"sys_bpe">>,Msg)), timer:sleep(1) end || #'Message'{feed_id = Feed}=Msg <- Msgs], + [begin roster:send_event(C, <<"sys_bpe">>, <<>>, Msg, roster:get_vnode(<<"sys_bpe">>,Msg)), timer:sleep(1) end || #'Message'{}=Msg <- Msgs], Current; #'Schedule'{id = Current} -> Current; _ -> roster:now_msec() end, job:clear_hist(P), proc({next, P, Curr}, #handler{state = {C, Proc}, seq = S} = H); -proc({next, #process{id = Id} = P, Current}, #handler{state = {C, Proc}, seq = S} = H) -> - %roster:info(?MODULE, "BPE PROC next: ~p", [P]), +proc({next, #process{id = Id}, Current}, #handler{state = {_C, _Proc}} = H) -> + %?LOG_INFO("BPE PROC next: ~p", [P]), FirstS = try mnesia:dirty_first('Schedule') of '$end_of_table' -> Current; Key -> Key catch _ -> Current end, Stop = #'Schedule'{id = roster:now_msec(), data = <<"stop">>}, Docs = case job:next('Schedule', Current) of @@ -322,31 +326,31 @@ proc({next, #process{id = Id} = P, Current}, #handler{state = {C, Proc}, seq = S _ -> [Stop] end end, - try bpe:amend(Id, Docs) catch Err:Rea -> n2o:error(?MODULE, "Catch:~p~n", [n2o:stack_trace(Err, Rea)]) end, + try bpe:amend(Id, Docs) catch Err:Rea -> ?LOG_ERROR("Catch:~p~n", [n2o:stack_trace(Err, Rea)]) end, kvs:delete('Schedule', Current), {reply, [], H}; -proc({restart, #'process'{id = Id} = P}, #handler{state = {C, Proc}, seq = S} = H) -> - roster:info(?MODULE, "BPE PROC restarted", []), +proc({restart, #'process'{} = P}, #handler{state = {C, Proc}} = H) -> + ?LOG_INFO("BPE PROC restarted", []), % P1=job_process:update_event(P, 'Action', {0, {0, 0, 1}}), Proc = restart_bpe(P, C), bpe:complete(Proc), {reply, [], #handler{state = {C, Proc}} = H}; -proc({timeout, #'Schedule'{id = Current} = Sh, T}, #handler{state = {C, Proc}} = H) -> - roster:info(?MODULE, "BPE PROC Timeout: ~p", [T]), +proc({timeout, #'Schedule'{id =_Current} = Sh, T}, #handler{state = {_C, Proc}} = H) -> + ?LOG_INFO("BPE PROC Timeout: ~p", [T]), %{value,#boundaryEvent{timeout = BT},_}=lists:keytake('*',#boundaryEvent.name,bpe:events(P)), %{ok, TRef} = timer:apply_after(T, bpe, complete, [Proc]), {ok, TRef} = timer:apply_after(T, roster_bpe, publish, [Proc]), try bpe:amend(Proc, Sh#'Schedule'{state = TRef}, noflow) catch Err:Rea -> - n2o:error(?MODULE, "Catch:~p~n", [n2o:stack_trace(Err, Rea)]) end, kvs:put(Sh#'Schedule'{state = TRef}), + ?LOG_ERROR("Catch:~p~n", [n2o:stack_trace(Err, Rea)]) end, kvs:put(Sh#'Schedule'{state = TRef}), %job_process:update_opt(bpe:load(Proc),{timer,TRef}), {reply, [], H}; -proc({clean, #'process'{id = Id} = P}, #handler{state = {C, Proc}} = H) -> - roster:info(?MODULE, "BPE PROC clean", []), +proc({clean, #'process'{id = Id} = P}, #handler{state = {_C,_Proc}} = H) -> + ?LOG_INFO("BPE PROC clean", []), bpe:complete(Id), {value, #boundaryEvent{timeout = BT}, _} = lists:keytake('*', #boundaryEvent.name, bpe:events(P)), %bpe:find_pid(Id) ! {'DOWN', <<>>, <<>>, <<>>, <<>>}, @@ -354,14 +358,14 @@ proc({clean, #'process'{id = Id} = P}, #handler{state = {C, Proc}} = H) -> {reply, [], H}; proc({send, Msgs}, #handler{state = {C, Proc}} = H) -> - roster:info(?MODULE, "BPE PROC send", []), + ?LOG_INFO("BPE PROC send", []), roster:restart_module(C,roster_bpe), % [case kvs:get(mqtt_session, roster:get_vnode(Msg)) of {ok,#mqtt_session{sess_pid = Pid}} -> n2o_async:send(Pid,{publish, , Msg}); _ -> skip end || Msg = #'Message'{} <- Msgs], [begin roster:send_event(C, <<"sys_bpe">>, <<>>, Msg, roster:get_vnode(<<"sys_bpe">>,Msg)),timer:sleep(5) end || Msg = #'Message'{} <- Msgs], {reply, [], H#handler{state = {C, Proc}}}; -proc({mqttc, C, connected}, State = #handler{state = {C, Proc}, seq = S}) -> {ok, State#handler{seq = S + 1}}; +proc({mqttc, C, connected}, State = #handler{state = {C,_Proc}, seq = S}) -> {ok, State#handler{seq = S + 1}}; proc({mqttc, _C, disconnected}, State) -> {ok, State}. @@ -379,7 +383,7 @@ restart_bpe(#process{id = Id} = P, _C) -> _ -> {ok, Pi} = bpe:start(P, []), Pi end catch - Err:Rea -> {ok, ID} = bpe:start(P, []), ID + _Err:_Rea -> {ok, ID} = bpe:start(P, []), ID end. amend(ProcId, Form, []) -> bpe:amend(ProcId, Form); @@ -399,4 +403,4 @@ set_schedule(_, Time, #'Job'{id = Id, proc = Proc, time = T0} = J) -> action(#'History'{status=update}=Term,R,S)-> - roster_history:info(Term, R, S). \ No newline at end of file + roster_history:info(Term, R, S). diff --git a/apps/roster/src/protocol/roster_favorite.erl b/apps/roster/src/protocol/roster_favorite.erl index 603310f1077e82e2769fe1a7132e2743af3c514e..cc47393d7c367e37e88e061a0ab331bbc4a46965 100644 --- a/apps/roster/src/protocol/roster_favorite.erl +++ b/apps/roster/src/protocol/roster_favorite.erl @@ -1,23 +1,24 @@ -module(roster_favorite). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). -compile(export_all). info(#'Tag'{status = create} = Tag, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Tag/create:~p", [ClientId, Tag]), + ?LOG_INFO("~p:Tag/create:~p", [ClientId, Tag]), {reply, {bert, Tag}, Req, State}; info(#'Tag'{status = remove} = Tag, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Tag/remove:~p", [ClientId, Tag]), + ?LOG_INFO("~p:Tag/remove:~p", [ClientId, Tag]), {reply, {bert, Tag}, Req, State}; info(#'Tag'{status = edit} = Tag, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Tag/edit:~p", [ClientId, Tag]), + ?LOG_INFO("~p:Tag/edit:~p", [ClientId, Tag]), {reply, {bert, Tag}, Req, State}; info(#'ExtendedStar'{}, Req, #cx{params = ClientId, client_pid = C} = State) -> RosterId = roster:roster_id(PhoneId = roster:phone_id(ClientId)), - roster:info(?MODULE, "~p:ExtendedStar/get:~p", [ClientId, PhoneId]), + ?LOG_INFO("~p:ExtendedStar/get:~p", [ClientId, PhoneId]), IO = case kvs:get('Roster', RosterId) of #error{} = Err -> #io{code = Err}; {ok, #'Roster'{favorite = Favs} = Roster} -> @@ -28,7 +29,7 @@ info(#'ExtendedStar'{}, Req, #cx{params = ClientId, client_pid = C} = State) -> {reply, {bert, IO}, Req, State}; info(#'Star'{status = add, message = #'Message'{id = MsgId} = Msg} = Data, Req, #cx{params = ClientId, client_pid = C} = State) -> - roster:info(?MODULE, "~p:Star/add:~p", [ClientId, Data]), + ?LOG_INFO("~p:Star/add:~p", [ClientId, Data]), RosterId = roster:roster_id(PhoneId = roster:phone_id(ClientId)), IO = case kvs:get('Roster', RosterId) of {ok, #'Roster'{favorite = StFavMsgs} = Roster} -> @@ -46,7 +47,7 @@ info(#'Star'{status = add, message = #'Message'{id = MsgId} = Msg} = Data, Req, {reply, {bert, IO}, Req, State}; info(#'Star'{id = MsgId, status = remove} = Data, Req, #cx{params = ClientId, client_pid = C} = State) -> - roster:info(?MODULE, "~p:Star/remove:~p", [ClientId, Data]), + ?LOG_INFO("~p:Star/remove:~p", [ClientId, Data]), Response = case kvs:get('Roster', roster:roster_id(ClientId)) of {ok, #'Roster'{favorite = StFavMsgs} = Roster} -> case lists:keyfind(MsgId, #'Star'.id, StFavMsgs) of @@ -59,13 +60,13 @@ info(#'Star'{id = MsgId, status = remove} = Data, Req, #cx{params = ClientId, cl end; #error{} -> #io{code = #error{code = roster_not_found}} end, - roster:info(?MODULE, "Debug.Response:~p", [Response]), + ?LOG_INFO("Debug.Response:~p", [Response]), {reply, {bert, Response}, Req, State}; info(#'Star'{} = Star, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Star/unknown:~p", [ClientId, Star]), + ?LOG_INFO("~p:Star/unknown:~p", [ClientId, Star]), {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State}; info(#'Tag'{} = Tag, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Tag/unknown:~p", [ClientId, Tag]), + ?LOG_INFO("~p:Tag/unknown:~p", [ClientId, Tag]), {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State}. diff --git a/apps/roster/src/protocol/roster_friend.erl b/apps/roster/src/protocol/roster_friend.erl index 30ae11bf62866091a774bf6a7a0520d31c46f9c3..d9bd826e6214655c4043728ff7900f178c1a03de 100644 --- a/apps/roster/src/protocol/roster_friend.erl +++ b/apps/roster/src/protocol/roster_friend.erl @@ -1,4 +1,5 @@ -module(roster_friend). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include("micro.hrl"). -include_lib("n2o/include/n2o.hrl"). @@ -29,7 +30,7 @@ info(#'Friend'{phone_id = Me, friend_id = Friend, settings = FriendSettings, sta roster:send_ses(C, To, roster:presence(#'Contact'{reader = Rs} = roster:readmsgs(Mec, Friend))), roster:send_ses(C, From, roster:presence(Friendc#'Contact'{reader = lists:reverse(Rs)})), n2o_async:pid(system, ?MODULE) ! {send_push, Me, Friend, <<"request">>}, - roster:info(?MODULE, "~p:~p:Friend/request:~p", [Me, ClientId, Friend]), + ?LOG_INFO("~p:~p:Friend/request:~p", [Me, ClientId, Friend]), <<>> end end, @@ -38,22 +39,22 @@ info(#'Friend'{phone_id = Me, status = request} = F, Req, #cx{params = ClientId} PhoneId = case ClientId of <<"sys_", _/binary>> -> Me; <<"emqttd_", _/binary>> -> roster:phone_id(ClientId); - __________ -> roster:info(?MODULE, "invalid client id: ~p", [ClientId]), error + __________ -> ?LOG_INFO("invalid client id: ~p", [ClientId]), error end, case PhoneId of ?IS_UUID -> - roster:info(?MODULE, "invalid phone id: ~p", [PhoneId]), + ?LOG_INFO("invalid phone id: ~p", [PhoneId]), {reply, {bert, #io{code = {error, corrupted_db}}}, Req, State}; Me -> info(F#'Friend'{phone_id = Me}, {Req, handled}, State); _ -> - roster:info(?MODULE, "invalid phone id: ~p", [Me]), + ?LOG_INFO("invalid phone id: ~p", [Me]), {reply, {bert, #io{code = {error, permission_denied}}}, Req, State} end; info(#'Friend'{friend_id = Friend, status = ignore}, Req, #cx{params = ClientId, client_pid = C} = State) -> Me = roster:phone_id(ClientId), - roster:info(?MODULE, "~p:~p:Friend/ignore:~p", [Me, ClientId, Friend]), + ?LOG_INFO("~p:~p:Friend/ignore:~p", [Me, ClientId, Friend]), IO = case roster:get_contact(Me, Friend) of {error, _} = E -> #io{code = E}; #'Contact'{status = authorization} = Contact -> @@ -66,7 +67,7 @@ info(#'Friend'{friend_id = Friend, status = ignore}, Req, #cx{params = ClientId, info(#'Friend'{friend_id = Friend, phone_id = Sender, status = confirm}, Req, #cx{params = ClientId, client_pid = C} = State) -> Me = case ClientId of <<"sys_", _/binary>> -> Sender; <<"emqttd_", _/binary>> -> roster:phone_id(ClientId) end, - roster:info(?MODULE, "~p:Friend/confirm:~p:~p", [ClientId, Me, Friend]), + ?LOG_INFO("~p:Friend/confirm:~p:~p", [ClientId, Me, Friend]), [{ok, #'Roster'{phone = From} = FromR}, {ok, #'Roster'{phone = To} = ToR}] = [kvs:get('Roster', roster:roster_id(PId)) || PId <- [Me, Friend]], D = case {roster:get_contact(FromR, Friend), roster:get_contact(ToR, Me)} of @@ -87,8 +88,8 @@ info(#'Friend'{friend_id = Friend, phone_id = Sender, status = confirm}, Req, #c Remote = roster:update_contact(ToR, Mec#'Contact'{last_msg = [], created = Now = roster:now_msec()}), Local = roster:update_contact(FromR, Friendc#'Contact'{last_msg = [], created = Now}), TSs = [{ac_topic, subscribe}, {muc_topic, unsubscribe}], - [n2o_vnode:Sub(Client, roster:Topic(Me)) || Client <- Tos, {Topic, Sub} <- TSs], - [n2o_vnode:Sub(Client, roster:Topic(Friend)) || Client <- Froms, {Topic, Sub} <- TSs], + [n2o_vnode:Sub(Client, roster:TopicLocal(Me)) || Client <- Tos, {TopicLocal, Sub} <- TSs], + [n2o_vnode:Sub(Client, roster:TopicLocal(Friend)) || Client <- Froms, {TopicLocal, Sub} <- TSs], [n2o_vnode:subscribe(Client, Topic) || Client <- Tos ++ Froms], roster:send_ses(C, To, Mec2 = roster:presence(#'Contact'{reader = Rs} = roster:readmsgs(Mec#'Contact'{update = Now, created = Now, unread = 1}, Friend))), roster:send_ses(C, From, roster:presence(Friendc#'Contact'{update = Now, created = Now, unread = 1, reader = lists:reverse(Rs)})), @@ -101,10 +102,10 @@ info(#'Friend'{friend_id = Friend, phone_id = Sender, status = confirm}, Req, #c info(#'Friend'{friend_id = Friend, status = Status}, Req, #cx{params = ClientId, client_pid = C} = State) when Status == ban; Status == unban -> Me = roster:phone_id(ClientId), - roster:info(?MODULE, "~p:Friend/~p:~p:~p", [ClientId, Status, Me, Friend]), + ?LOG_INFO("~p:Friend/~p:~p:~p", [ClientId, Status, Me, Friend]), [From, FromId] = roster:parts_phone_id(Me), [To, ToId] = roster:parts_phone_id(Friend), - [Tos, Froms] = [emqttd_pubsub:subscribers(roster:ses_topic(P)) || P <- [To, From]], + [_Tos, Froms] = [emqttd_pubsub:subscribers(roster:ses_topic(P)) || P <- [To, From]], [{ok, FromR}, {ok, ToR}] = [kvs:get('Roster', Id) || Id <- [FromId, ToId]], Fc = #'Contact'{status = FrSt} = roster:get_contact(FromR, Friend), Mc = #'Contact'{status = MeSt} = roster:get_contact(ToR, Me), @@ -131,7 +132,7 @@ info(#'Friend'{friend_id = Friend, status = Status}, Req, [n2o_vnode:Unsub(Client, AcFrT) || Client <- Froms], [n2o_vnode:Unsub(Client, roster:p2p_topic(Me, Friend)) || Client <- Froms], case roster:is_shared_rooms(FromR#'Roster'.roomlist, - [R || #'Room'{status = Status} = R <- ToR#'Roster'.roomlist, Status /= removed]) of + [R || #'Room'{status = StatusLocal} = R <- ToR#'Roster'.roomlist, StatusLocal /= removed]) of true -> [n2o_vnode:Sub(Client, MucFrT) || Client <- Froms]; _ -> skip end, roster:send_ses(C, To, roster:presence(#'Contact'{reader = Rs} = roster:readmsgs(MeC, Friend))), @@ -141,12 +142,12 @@ info(#'Friend'{friend_id = Friend, status = Status}, Req, info(#'Friend'{phone_id = PhoneId, friend_id = Friend, settings = NewSets, status = update}, Req, #cx{client_pid = C, params = ClientId} = State) -> - roster:info(?MODULE, "~p:Friend/update:~p", [ClientId, Friend]), + ?LOG_INFO("~p:Friend/update:~p", [ClientId, Friend]), IO = case roster:phone_id(ClientId) of PhoneId -> case kvs:get('Roster', roster:roster_id(PhoneId)) of {ok, #'Roster'{userlist = Contacts} = Roster} -> case lists:keyfind(Friend, #'Contact'.phone_id, Contacts) of - #'Contact'{reader = RId, settings = Settings, status = friend} = Contact -> + #'Contact'{settings = Settings, status = friend} = Contact -> C2 = Contact#'Contact'{settings = lists:foldl( fun(#'Feature'{id = FId} = F, Acc) -> lists:keystore(FId, #'Feature'.id, Acc, F) end, @@ -164,12 +165,12 @@ info(#'Friend'{phone_id = PhoneId, friend_id = Friend, settings = NewSets, statu {reply, {bert, IO}, Req, State}; -info(#'Friend'{phone_id = Roster, friend_id = Friend} = F, Req, State) -> - roster:info(?MODULE, "~p:~p:Friend/unknown", [Roster, F]), +info(#'Friend'{phone_id = Roster} = F, Req, State) -> + ?LOG_INFO("~p:~p:Friend/unknown", [Roster, F]), {reply, {bert, <<>>}, Req, State}. proc(init, #handler{name = ?MODULE} = Async) -> - roster:info(?MODULE, "ASYNC", []), + ?LOG_INFO("ASYNC", []), {ok, Async}; proc({send_push, #'Contact'{names = FromName} = FromContactPayload, To, PushType}, #handler{} = H) -> diff --git a/apps/roster/src/protocol/roster_ftp.erl b/apps/roster/src/protocol/roster_ftp.erl index 8a498de0901bdf19417aaf59017fd9f4c9aa6d58..5cc449b3736923c22bda2fe610e972daa4733867 100644 --- a/apps/roster/src/protocol/roster_ftp.erl +++ b/apps/roster/src/protocol/roster_ftp.erl @@ -1,4 +1,5 @@ -module(roster_ftp). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). -include_lib("kernel/include/file.hrl"). @@ -10,18 +11,18 @@ % Callbacks -filename(#ftp{sid = Sid, filename = FileName}) -> FileName. %filename:join(nitro:to_list(Sid),FileName). +filename(#ftp{filename = FileName}) -> FileName. %filename:join(nitro:to_list(Sid),FileName). % File Transfer Protocol info(#ftp{status = {event, _}} = FTP, Req, State) -> - roster:info(?MODULE, "Event Message: ~p", [FTP#ftp{data = <<>>}]), + ?LOG_INFO("Event Message: ~p", [FTP#ftp{data = <<>>}]), Module = case State#cx.module of [] -> index; M -> M end, Reply = try Module:event(FTP) catch E:R -> Error = n2o:stack(E, R), - roster:info(?MODULE, "Catch: ~p:~p~n~p", Error), + ?LOG_INFO("Catch: ~p:~p~n~p", Error), Error end, {reply, n2o:format({io, n2o_nitro:render_actions(n2o:actions()), Reply}), Req, State}; @@ -29,12 +30,12 @@ info(#ftp{status = {event, _}} = FTP, Req, State) -> info(#ftp{id = Link, status = <<"init">>, block = Block, offset = Offset} = FTP, Req, State) -> Root = ?ROOT, RelPath = (application:get_env(roster, filename, roster_ftp)):filename(FTP), - roster:info(?MODULE, "RelPath ~p", [RelPath]), + ?LOG_INFO("RelPath ~p", [RelPath]), FilePath = filename:join(Root, RelPath), ok = filelib:ensure_dir(FilePath), FileSize = case file:read_file_info(FilePath) of {ok, Fi} -> Fi#file_info.size; {error, _} -> 0 end, - roster:info(?MODULE, "Info Init: ~p Offset: ~p Block: ~p", [FilePath, FileSize, Block]), + ?LOG_INFO("Info Init: ~p Offset: ~p Block: ~p", [FilePath, FileSize, Block]), Block2 = case Block of 0 -> ?STOP; _ -> ?NEXT end, Offset2 = case FileSize >= Offset of true -> FileSize; false -> 0 end, @@ -46,12 +47,12 @@ info(#ftp{id = Link, status = <<"init">>, block = Block, offset = Offset} = FTP, {reply, {bert, FTP2}, Req, State}; info(#ftp{id = Link, status = <<"send">>} = FTP, Req, State) -> - roster:info(?MODULE, "Info Send: ~p", [FTP#ftp{data = <<>>}]), + ?LOG_INFO("Info Send: ~p", [FTP#ftp{data = <<>>}]), Reply = try gen_server:call(n2o_async:pid({file, Link}), FTP) catch E:R -> skip, - roster:info(?MODULE, "Info Error call the sync: ~p", [{E, R}]), + ?LOG_INFO("Info Error call the sync: ~p", [{E, R}]), FTP#ftp{data = <<>>, block = ?STOP} end, - roster:info(?MODULE, "Send reply ~p", [Reply#ftp{data = <<>>}]), + ?LOG_INFO("Send reply ~p", [Reply#ftp{data = <<>>}]), {reply, {bert, Reply}, Req, State}; info(Message, Req, State) -> {unknown, Message, Req, State}. @@ -59,9 +60,9 @@ info(Message, Req, State) -> {unknown, Message, Req, State}. % n2o Handlers proc(init, #handler{state = #ftp{sid = Sid, meta = ClientId} = FTP} = Async) -> - roster:info(?MODULE, "Proc Init: ~p~n Sid: ~p ClientId: ~p", [FTP#ftp{data = <<>>}, Sid, ClientId]), + ?LOG_INFO("Proc Init: ~p~n Sid: ~p ClientId: ~p", [FTP#ftp{data = <<>>}, Sid, ClientId]), FTP2 = FTP#ftp{data = <<>>, status = {event, init}}, - roster:info(?MODULE, "~n~n~n~n FTP2 ~p ~n~n~n~n", [FTP2]), + ?LOG_INFO("~n~n~n~n FTP2 ~p ~n~n~n~n", [FTP2]), n2o_ring:send({publish, <<"events/1/index/anon/", ClientId/binary, "/", Sid/binary>>, term_to_binary(FTP2)}), {ok, Async}; @@ -71,7 +72,7 @@ proc(#ftp{id = Link, sid = Sid, data = Data, status = <<"send">>, block = Block, {S3_Status, S3_Data} = amazon_api:push_to_s3([], RelPath, Data, []), case S3_Status of ok -> - roster:info(?MODULE, "~n~n Uploaded to S3 file with link ~p ~n~n", [S3_Data]), + ?LOG_INFO("~n~n Uploaded to S3 file with link ~p ~n~n", [S3_Data]), FTP2 = FTP#ftp{data = <<>>, block = ?STOP}, FTP3 = FTP2#ftp{status = {event, stop}, filename = RelPath}, n2o_ring:send({publish, <<"events/1//index/anon/", ClientId/binary, "/", Sid/binary>>, term_to_binary(FTP3)}), @@ -84,14 +85,14 @@ proc(#ftp{id = Link, sid = Sid, data = Data, status = <<"send">>, block = Block, proc(#ftp{data = Data, block = Block} = FTP, #handler{state = #ftp{offset = Offset, filename = RelPath}} = Async) -> FTP2 = FTP#ftp{status = <<"send">>, offset = Offset + Block}, - roster:info(?MODULE, "Proc Process ~p", [FTP2#ftp{data = <<>>}]), + ?LOG_INFO("Proc Process ~p", [FTP2#ftp{data = <<>>}]), {S3_Status, S3_Data} = amazon_api:push_to_s3([], RelPath, Data, []), case S3_Status of ok -> - roster:info(?MODULE, "~n~n Uploaded to S3 file with link ~p ~n~n", [S3_Data]), + ?LOG_INFO("~n~n Uploaded to S3 file with link ~p ~n~n", [S3_Data]), {reply, FTP2#ftp{data = <<>>}, Async#handler{state = FTP2#ftp{filename = RelPath}}}; _ -> {reply, {error, S3_Data / binary}, Async} end; -proc(_, Async) -> {reply, #ftpack{}, Async}. \ No newline at end of file +proc(_, Async) -> {reply, #ftpack{}, Async}. diff --git a/apps/roster/src/protocol/roster_history.erl b/apps/roster/src/protocol/roster_history.erl index a5243b64978eaf4f2bd8ade95cb383f0cf7e9998..1b007d1bf1171d791c1b669a81c4159da9134852 100644 --- a/apps/roster/src/protocol/roster_history.erl +++ b/apps/roster/src/protocol/roster_history.erl @@ -1,4 +1,5 @@ -module(roster_history). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). -include_lib("kvs/include/kvs.hrl"). @@ -13,7 +14,7 @@ start() -> n2o_async:start(#handler{module = ?MODULE, class = system, group = ro info(#'History'{roster_id = RosterId, feed = #'StickerPack'{} = Feed, size = N, status = get} = Data, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:~p:History/get:~p", [RosterId, ClientId, Feed]), + ?LOG_INFO("~p:~p:History/get:~p", [RosterId, ClientId, Feed]), Stickers = case kvs:get('Roster', roster:roster_id(RosterId)) of {ok, #'Roster'{}} -> S = kvs:all('StickerPack'), case N of [] -> S; _ -> lists:sublist(S, N) end; %%TODO Temporary all sticker packs @@ -23,13 +24,13 @@ info(#'History'{roster_id = RosterId, feed = #'StickerPack'{} = Feed, size = N, info(#'History'{status = get_reply, entity_id = Id} = History, Req, #cx{params = ClientId} = State) when Id /= [] -> PhoneId = roster:phone_id(ClientId), - roster:info(?MODULE, "~p:~p:History/get_reply:~p", [PhoneId, ClientId, Id]), + ?LOG_INFO("~p:~p:History/get_reply:~p", [PhoneId, ClientId, Id]), Msgs = case kvs:get('Message', Id) of {ok, #'Message'{repliedby = [_|_] = Replyes}} -> [case kvs:get('Message', ReplId) of {ok, Msg} -> Msg; _ -> [] end || ReplId<-Replyes]; _ -> [] end, {reply, {bert, History#'History'{data = lists:flatten(Msgs)}}, Req, State}; -info(#'History'{status = double_get, feed = Feed, size = N, entity_id = MId, data = []} = History, Req, +info(#'History'{status = double_get, size = N, entity_id = MId, data = []} = History, Req, #cx{} = State) when N > 0, MId > 0 -> {reply, {bert, #'History'{data = Msgs1}}, _, _} = info(History#'History'{status = get, size = N}, Req, State), @@ -52,13 +53,13 @@ info(#'History'{status = get, roster_id = Roster0, feed = Feed, size = N, entity when N /= 0 andalso (is_record(Feed, p2p) orelse is_record(Feed, muc)) andalso (MsgData == [] andalso InitialStatus == get orelse MsgData /= []) -> PhoneId = roster:phone_id(ClientId), - roster:info(?MODULE, "~p:~p:History/get:~p:~p", [PhoneId, ClientId, Feed, History]), + ?LOG_INFO("~p:~p:History/get:~p:~p", [PhoneId, ClientId, Feed, History]), {UID, R, LastMsgId, _Unit} = case PhoneId of Roster0 -> roster:get_feed_data(Feed, PhoneId); _ -> {error, permission_denied, [],[]} end, IO = case UID of - error -> roster:info(?MODULE, "History/get.Error:~p", [R]), + error -> ?LOG_INFO("History/get.Error:~p", [R]), #io{code = #error{code = R}}; _ -> {N2, FId} = case {N, MsgData} of @@ -82,8 +83,8 @@ info(#'History'{status = get, roster_id = Roster0, feed = Feed, size = N, entity Filter = case N of [] -> msg_update; _ -> msg_filter end, %% select filtration function {InnerFilterFun, Mime2} = case MsgData of [#'Message'{files = [#'Desc'{mime = Mime = <<_:8, _/binary>>}]}] -> - {fun(#'Message'{files = Descs} = Msg, UID) -> %% filter by mime type - case roster:msg_filter(Msg, UID) of + {fun(#'Message'{files = Descs} = Msg, UIDLocal) -> %% filter by mime type + case roster:msg_filter(Msg, UIDLocal) of 1 -> roster:bool_to_int(lists:keyfind(Mime, #'Desc'.mime, Descs)); 0 -> 0 end end, Mime}; [] -> {fun roster:Filter/2, []}; @@ -97,8 +98,8 @@ info(#'History'{status = get, roster_id = Roster0, feed = Feed, size = N, entity AccFun = fun(_, #'Message'{id = Id}, Acc, Dir) when Dir < 0, Id > MaxReadId -> Acc; %% if from message is lower then reader (1, Msg, Acc, Dir) when Dir < 0 -> Acc++[roster:wrap_msg(Msg)]; - (0, Msg, Acc, Dir) -> Acc; - (1, Msg, Acc, Dir) -> [roster:wrap_msg(Msg)|Acc] end, + (0,_Msg, Acc,_Dir) -> Acc; + (1, Msg, Acc,_Dir) -> [roster:wrap_msg(Msg)|Acc] end, FilterFun = fun(Msg, {Acc, _} = A) when length(Acc) < abs(N2) -> @@ -118,7 +119,7 @@ info(#'History'{status = get, roster_id = Roster0, feed = Feed, size = N, entity lists:reverse(lists:ukeymerge(#'Message'.id, lists:reverse(Msgs), [EntityMsg#'Message'{files = []}])); {error, _} -> - roster:info(?MODULE, "message ~p not found", [MId]), + ?LOG_INFO("message ~p not found", [MId]), #error{code = invalid_data} end; _ -> Msgs @@ -140,7 +141,7 @@ info(#'History'{status = update, feed = Feed, entity_id = MId}, Req, #cx{client_pid = C, params = ClientId, state = verified} = State) -> PhoneId = roster:phone_id(ClientId), From = roster:phone(PhoneId), - roster:info(?MODULE, "~p:History/update:~p,~p", [From, Feed, MId]), + ?LOG_INFO("~p:History/update:~p,~p", [From, Feed, MId]), {UID, R, LastMsgId, Unit} = roster:get_feed_data(Feed, PhoneId), MId2 = case MId > LastMsgId of true -> LastMsgId; _ -> MId end, {Unread, UpdReader} = @@ -148,7 +149,7 @@ info(#'History'{status = update, feed = Feed, entity_id = MId}, Req, {ok, #reader{cache = []} = Rdr0} -> W = #writer{count = Count, first = #'Message'{}} = kvs_stream:load_writer(Feed), case Count of - 0 -> roster:info(?MODULE, "ThereAreNotMessages:~p", [W]), <<>>; + 0 -> ?LOG_INFO("ThereAreNotMessages:~p", [W]), <<>>; _ -> Rdr = kvs_stream:bot((Rdr0#reader{dir = 0})), Reader = kvs_stream:save(roster:mover(Rdr, {'Message', MId2})), {roster:nentries(kvs:get('Message', MId2), Count + 1, UID) - roster:nentries(kvs:get('Message', MId2), 0, UID), Reader} @@ -159,9 +160,9 @@ info(#'History'{status = update, feed = Feed, entity_id = MId}, Req, {ok, #reader{pos = 0, cache = {_, RId}} = Rdr} when MId2 >= RId -> Reader = kvs_stream:save((roster:mover(Rdr#reader{dir = roster:sign(MId2 - RId)}, {'Message', MId2}))#reader{dir = 0}), {roster:nentries2(kvs:get('Message', LastMsgId), MId2, UID), Reader}; - {ok, #reader{pos = Pos, cache = {_, RId}} = Rdr} when MId2 == RId -> + {ok, #reader{pos =_Pos, cache = {_, RId}} =_Rdr} when MId2 == RId -> {<<>>, <<>>}; - {ok, #reader{pos = Pos, cache = {_, RId}} = Rdr} -> + {ok, #reader{cache = {_, RId}} = Rdr} -> % #writer{count = Top} = kvs_stream:load_writer(Feed), Reader = #reader{cache = {_, NRId}} = kvs_stream:save((roster:mover(Rdr#reader{dir = roster:sign(MId2 - RId)}, {'Message', MId2}))#reader{dir = 0}), {roster:nentries2(kvs:get('Message', LastMsgId), NRId, UID), Reader}; @@ -200,7 +201,7 @@ info(#'History'{status = update, feed = Feed, entity_id = MId} = History, Req, # info(#'History'{feed = Feed, status = delete}, Req, #cx{client_pid = C, params = ClientId} = State) -> PhoneId = roster:phone_id(ClientId), - roster:info(?MODULE, "~p:History/delete:~p", [PhoneId, Feed]), + ?LOG_INFO("~p:History/delete:~p", [PhoneId, Feed]), IO = case clean_history(Feed, PhoneId) of #error{} = Res -> #io{code = Res}; {_Unread, #'Message'{}=Internal} -> @@ -212,7 +213,7 @@ info(#'History'{feed = Feed, status = delete}, Req, #cx{client_pid = C, params = {I,_} -> I end, {reply, {bert, IO}, Req, State}; -info(#'History'{roster_id = PhoneId, status = draft}, Req, #cx{client_pid = C, params = ClientId} = State) -> +info(#'History'{roster_id = PhoneId, status = draft}, Req, #cx{params = ClientId} = State) -> PhoneId = roster:phone_id(ClientId), IO = case kvs:get('Roster', roster:roster_id(PhoneId)) of {ok, #'Roster'{userlist = Conts, roomlist = Rooms}} -> @@ -220,12 +221,12 @@ info(#'History'{roster_id = PhoneId, status = draft}, Req, #cx{client_pid = C, p CMs=[M|| #'Contact'{last_msg = M}<-Conts], #'Draft'{data=lists:flatten(CMs++RMs), status = get}; E -> #io{code = E} end, - roster:info(?MODULE, "~p:History/draft:~p", [PhoneId, IO]), + ?LOG_INFO("~p:History/draft:~p", [PhoneId, IO]), {reply, {bert, IO}, Req, State}; info(#'History'{} = Data, Req, State) -> - roster:info(?MODULE, "History/unknown:~p", [Data]), + ?LOG_INFO("History/unknown:~p", [Data]), {reply, {bert, <<>>}, Req, State}. % Helper Section @@ -256,7 +257,7 @@ clean_history(Feed, From, To, Rs) -> kvs:put(Room#'Room'{readers = []}), {0, M}; {ok, #writer{count = Top, id = #p2p{}} = W} -> - NewW = #writer{cache = #'Message'{id = NewFirst, next = NextId} = M} = + #writer{cache = #'Message'{id = NewFirst, next = NextId} = M} = kvs_stream:save(kvs_stream:add(W#writer{args = Msg#'Message'{seenby = [To], created = roster:now_msec()}})), [begin case kvs:get('Message', NextId) of @@ -270,7 +271,7 @@ clean_history(Feed, From, To, Rs) -> end. proc(init, #handler{name = roster_history} = Async) -> - roster:info(?MODULE, "ASYNC:~p", [?MODULE]), + ?LOG_INFO("ASYNC:~p", [?MODULE]), {ok, Async}; proc({send_push, ToPhoneId, Feed, Action}, #handler{} = H) -> @@ -280,7 +281,7 @@ proc({send_push, ToPhoneId, Feed, Action}, #handler{} = H) -> try Room = #'Room'{} = roster:room(roster:roster_id(ToPhoneId), RoomId), case Action of ?HISTORY_UPDATE_ACTION -> Room#'Room'{last_msg = []}; _ -> Room end catch Err:Rea -> - roster:error(?MODULE, ":~p~n", [n2o:stack_trace(Err, Rea)]) + ?LOG_ERROR(":~p~n", [n2o:stack_trace(Err, Rea)]) end; #p2p{} -> Contact = roster:user(roster:roster_id(ToPhoneId), roster:friend(ToPhoneId, Feed)), @@ -289,4 +290,4 @@ proc({send_push, ToPhoneId, Feed, Action}, #handler{} = H) -> end, PushAlert = iolist_to_binary(["SyncPush:", Action]), [roster_push:send_push_notification(Ses, Payload, PushAlert, PushType) || #'Auth'{} = Ses <- kvs:index('Auth', user_id, ToPhoneId)], - {reply, [], H}. \ No newline at end of file + {reply, [], H}. diff --git a/apps/roster/src/protocol/roster_link.erl b/apps/roster/src/protocol/roster_link.erl index d358145bb54c8b967839f2d522f608b9d12536e9..4cb2ed86746d51acfa34d19c74f953245fc5d10b 100644 --- a/apps/roster/src/protocol/roster_link.erl +++ b/apps/roster/src/protocol/roster_link.erl @@ -1,4 +1,5 @@ -module(roster_link). +-include_lib("kernel/include/logger.hrl"). -include("../../include/roster.hrl"). -include_lib("roster/include/static/roster_text.hrl"). -include_lib("roster/include/static/roster_var.hrl"). @@ -11,8 +12,8 @@ % API % ====================================== info(#'Link'{id = LinkId, type = group, status = join = LStatus} = RequestData, - Req, #cx{params = ClientId, client_pid = C} = State) when LinkId /= [] -> - roster:info(?MODULE, "~p:Link/~p:~p", [ClientId, LStatus, RequestData]), + Req, #cx{params = ClientId} = State) when LinkId /= [] -> + ?LOG_INFO("~p:Link/~p:~p", [ClientId, LStatus, RequestData]), LD=kvs:get('Link',LinkId), Res = case get_entity(LD) of {ok, #'Room'{id = RoomId, last_msg = MsgId} = Room} -> @@ -28,12 +29,12 @@ info(#'Link'{id = LinkId, type = group, status = join = LStatus} = RequestData, NewRoom#'Room'{members = [roster:muc_member(PhoneId,RoomId) | Members], links=[Link], admins = Admins, last_msg = LastMsg, status=joined }; {error, not_found} = Err -> #io{code = Err} end, - roster:info(?MODULE, "Link/~p.Response:~p", [Res, LStatus]), + ?LOG_INFO("Link/~p.Response:~p", [Res, LStatus]), {reply, {bert, Res}, Req, State}; info(#'Link'{id = LinkId, status = get = LStatus} = RequestData, Req, #cx{params = ClientId} = State) when LinkId /= [] -> - roster:info(?MODULE, "~p:Link/~p:~p", [ClientId, LStatus, RequestData]), + ?LOG_INFO("~p:Link/~p:~p", [ClientId, LStatus, RequestData]), LD=kvs:get('Link',LinkId), Res = case get_entity(LD) of {ok, #'Room'{id = RoomId, last_msg = MsgId} = Room} -> @@ -46,14 +47,14 @@ info(#'Link'{id = LinkId, status = get = LStatus} = RequestData, Room#'Room'{members = Members, admins = Admins, links=[Link], last_msg = LastMsg, status = info}; {error, not_found} = Err -> #io{code = Err} end, - roster:info(?MODULE, "Link/~p.Response:~p", [Res, LStatus]), + ?LOG_INFO("Link/~p.Response:~p", [Res, LStatus]), {reply, {bert, Res}, Req, State}; %% NOTE - Not used at the moment, to be finished in next release % -info(#'Link'{id = LinkId, room_id = RoomId, type = Type, status = update = LStatus} = RequestData, +info(#'Link'{id = LinkId, room_id = RoomId, status = update = LStatus} = RequestData, Req, #cx{params = ClientId} = State) when RoomId /= [], LinkId /= [] -> - roster:info(?MODULE, "~p:Link/~p:~p", [ClientId, LStatus, RequestData]), + ?LOG_INFO("~p:Link/~p:~p", [ClientId, LStatus, RequestData]), Res = case roster:muc_member(ClientId, RoomId) of #'Member'{status = Status} when Status == owner; Status == admin -> case update_link(RequestData) of @@ -65,21 +66,21 @@ info(#'Link'{id = LinkId, room_id = RoomId, type = Type, status = update = LStat _ -> #io{code = #error{code = invalid_data}} end, - roster:info(?MODULE, "Link/~p.Response:~p", [Res, LStatus]), + ?LOG_INFO("Link/~p.Response:~p", [Res, LStatus]), {reply, {bert, Res}, Req, State}; %% NOTE - Not used at the moment, to be finished in next release % info(#'Link'{type = group, status = delete = LStatus} = RequestData, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Link/~p:~p", [ClientId, LStatus, RequestData]), + ?LOG_INFO("~p:Link/~p:~p", [ClientId, LStatus, RequestData]), % Res = delete_link(ClientId, RequestData), Res = #io{code = #error{code = permission_denied}}, - roster:info(?MODULE, "Link/~p.Response:~p", [Res, LStatus]), + ?LOG_INFO("Link/~p.Response:~p", [Res, LStatus]), {reply, {bert, Res}, Req, State}; info(#'Link'{} = RequestData, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Link/unknown:~p", [ClientId, RequestData]), + ?LOG_INFO("~p:Link/unknown:~p", [ClientId, RequestData]), {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State}. % ====================================== @@ -206,4 +207,4 @@ get_link({ok,#'Room'{id=EId}})-> E -> E end; get_link(ID) when is_binary(ID) -> get_link({ok,#'Room'{id=ID}}); -get_link(_)->{error, not_found}. \ No newline at end of file +get_link(_)->{error, not_found}. diff --git a/apps/roster/src/protocol/roster_message.erl b/apps/roster/src/protocol/roster_message.erl index a8d72c3e1619f9f1a7235d82266659f9a54c1bc2..0d918104baf58b7f3e7bdf2755e7ca9a4912d027 100644 --- a/apps/roster/src/protocol/roster_message.erl +++ b/apps/roster/src/protocol/roster_message.erl @@ -1,4 +1,5 @@ -module(roster_message). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). -include_lib("kvs/include/kvs.hrl"). @@ -14,11 +15,11 @@ start() -> n2o_async:start(#handler{module = ?MODULE, class = system, group = ro info(#'Typing'{phone_id = Phone, comments = Comments}, Req, #cx{} = State) -> - roster:info(?MODULE, "~p:Typing/:~p", [Phone, Comments]), + ?LOG_INFO("~p:Typing/:~p", [Phone, Comments]), {reply, {bert, <<>>}, Req, State}; info(#'Message'{msg_id=MsgID, files=Descs, status=Status} = M, Req, #cx{state=[]}=State) when MsgID /= [], Status/=update-> - roster:info(?MODULE, "~p:~p:Message/:~p", [MsgID, State, Status]), + ?LOG_INFO("~p:~p:Message/:~p", [MsgID, State, Status]), case {kvs:index('Message', msg_id, MsgID), has_flag(Descs, message_ack)} of {[#'Message'{id = ServerId, feed_id = Feed, next = Next}], true} -> {reply, {bert, #'MessageAck'{id = ServerId, @@ -123,7 +124,7 @@ info(#'Message'{status = [], id = [], feed_id = F, from=From0, to = To, kvs:put(LnkRes2), LnkRes2; _ -> LnkRes end, roster:send_feed(C, F, Msg2 = M#'Message'{link = LnkRes3}), - roster:info(?MODULE, "~p:Message/new:~p", [From, To]), + ?LOG_INFO("~p:Message/new:~p", [From, To]), %% have to skip push notifications for call bubbles case [lists:keyfind(BubbleContentType, #'Desc'.mime, Descs) || BubbleContentType <- [?CONTENT_TYPE_VIDEOCALL, ?CONTENT_TYPE_AUDIOCALL]] of [false, false] -> n2o_async:pid(system, ?MODULE) ! {send_push, From, To, Msg2, []}; @@ -151,10 +152,10 @@ info(#'Message'{status = [], id = [], feed_id = F, from=From0, to = To, {reply, {bert, IO}, Req, State}; info(#'Message'{status = edit, id = Id, msg_id = ClMID, feed_id = Feed, from = From, to = To, mentioned = Mentioned, - files = [#'Desc'{payload = Payload} | _] = Descs}, Req, + files = [#'Desc'{} | _] = Descs}, Req, #cx{params = ClientId, client_pid = C, state=ack} = State) -> PhoneId = roster:phone_id(ClientId, From), - roster:info(?MODULE, "~p:Message/edit:~p", [PhoneId, To]), + ?LOG_INFO("~p:Message/edit:~p", [PhoneId, To]), DV = length([D || D = #'Desc'{id = ID} <- Descs, is_binary(ID), ID /= <<>>]) == length(Descs), Data = case kvs:get('Message', Id) of @@ -166,7 +167,7 @@ info(#'Message'{status = edit, id = Id, msg_id = ClMID, feed_id = Feed, from = F _ -> #io{code = #error{code = not_found}} end; {ok, #'Message'{feed_id = #p2p{} = Feed, from = PhoneId, to = To} = Msg} when DV -> - % roster:info(?MODULE, "~p:~p:P2P/edit:~p",[Feed,From,Id]), + % ?LOG_INFO("~p:~p:P2P/edit:~p",[Feed,From,Id]), % Timeout=calendar:time_difference(roster:msToUT(T0),erlang:universaltime()), Cont = #'Contact'{reader = Rdr} = roster:get_contact(roster:roster_id(PhoneId), To), {Msg#'Message'{files = Descs}, Rdr, Cont}; @@ -199,7 +200,7 @@ info(#'Message'{status = edit, id = Id, msg_id = ClMID, feed_id = Feed, from = F info(#'Message'{id = Id, msg_id = ClMID, feed_id = Feed, from = From0, seenby = Seen, status = delete}, Req, #cx{params = ClientId, client_pid = C, state=ack} = State) when is_integer(Id) -> - roster:info(?MODULE, "~p:~p:Message/delete:~p", [Feed, From0, Id]), + ?LOG_INFO("~p:~p:Message/delete:~p", [Feed, From0, Id]), %%TODO for security From= PhoneId = roster:phone_id(ClientId, From0), D = case kvs:get('Message', Id) of @@ -212,7 +213,7 @@ info(#'Message'{id = Id, msg_id = ClMID, feed_id = Feed, from = From0, seenby = lists:usort(lists:subtract(Seen0, Seen) ++ lists:usort(Seen)) end, case roster:muc_member(PhoneId, Name) of #'Member'{reader = Rdr, status = admin} = Mem -> - %roster:info(?MODULE, "~p:~p:MUC/delete:~p",[Feed,From,Id]), + %?LOG_INFO("~p:~p:MUC/delete:~p",[Feed,From,Id]), {Msg, roster:room_topic(Name), Rdr, Mem, case {NSeen, Seen0} of {Seen0, Seen0} -> <<>>; @@ -233,7 +234,7 @@ info(#'Message'{id = Id, msg_id = ClMID, feed_id = Feed, from = From0, seenby = _ -> #io{code = #error{code = not_found}} end; {ok, #'Message'{feed_id = #p2p{} = Feed, from = From, to = To, created = T0, seenby = Seen0, status = []} = Msg}-> - % roster:info(?MODULE, "~p:~p:P2P/delete:~p",[Feed,From,Id]), + % ?LOG_INFO("~p:~p:P2P/delete:~p",[Feed,From,Id]), Timeout = calendar:time_difference(roster:msToUT(T0), erlang:universaltime()), Cont = #'Contact'{reader = Rdr} = roster:get_contact(roster:roster_id(PhoneId), case From == PhoneId of true -> To;false -> From end), @@ -280,7 +281,7 @@ info(#'Message'{id = Id, msg_id = ClMID, feed_id = Feed, from = From0, seenby = case {Type, Link} of {[reply], Link} when is_integer(Link) andalso NewSeen == [-1] -> case kvs:get('Message', Link) of - {error, not_found} -> roster:info(?MODULE, + {error, not_found} -> ?LOG_INFO( "link ~p for reply not found in ~p message", [Link, Message#'Message'.id]), <<>>; {ok, #'Message'{feed_id = Feed, repliedby = ReplBy} = LnkMessage} -> kvs:put(LnkMessage2 = LnkMessage#'Message'{repliedby = lists:delete(MsgId, ReplBy)}), @@ -294,7 +295,7 @@ info(#'Message'{status = update, type = [draft], feed_id = Feed0, from = From, t files = File}=M, Req, #cx{params = ClientId, client_pid = C, state=ack} = State) -> PhoneId = case ClientId of <<"sys_bpe">> -> From; <<"emqttd_", _/binary>> -> roster:phone_id(ClientId) end, - roster:info(?MODULE, "~p:Message/Draft:~p", [PhoneId, Feed0]), + ?LOG_INFO("~p:Message/Draft:~p", [PhoneId, Feed0]), {Feed,M1,D}=case File of [] -> {Feed0,[],#'Draft'{data =[M], status = delete}}; [#'Desc'{} | _] -> @@ -329,7 +330,7 @@ info(#'Message'{status = update, id = Id, files = [#'Desc'{mime = <<"transcribe" Pid = n2o_async:pid(system, roster_message), case kvs:get('Message', Id) of {ok, #'Message'{feed_id = Feed, from = From, to = To, files = [#'Desc'{mime = <<"audio">>, payload = Uri} | _]}} -> - roster:info(?MODULE, "enter ~p transcribe process with ~p", [Type, Uri]), + ?LOG_INFO("enter ~p transcribe process with ~p", [Type, Uri]), ErrMsg = #'Message'{id = Id, feed_id = Feed, from = From, to = To}, case Type of short -> @@ -345,7 +346,7 @@ info(#'Message'{status = update, id = Id, files = [#'Desc'{mime = <<"transcribe" end, [file:delete(filename:absname(File)) || File <- [FileIn, FileOut]], R; {error, ErrInfo} -> - roster:error(?MODULE, "invalid url for transcribe: ~p", [ErrInfo]), + ?LOG_ERROR("invalid url for transcribe: ~p", [ErrInfo]), #io{code = {error, invaid_data}, data = ErrMsg} end, Pid ! {Res, ClientId} end), {reply, {bert, #io{code = #ok{code = transcribe}, data = Id}}, Req, State}; @@ -361,22 +362,22 @@ info(#'Message'{status = update, id = Id, files = [#'Desc'{mime = <<"transcribe" Pid ! {Msg#'Message'{files = [Desc#'Desc'{payload = Text, data = Data2}]}, ClientId} end, google_api:transcribe(Type, GsUri, Lang, <<"ENCODING_UNSPECIFIED">>, Fun) end), {reply, {bert, #io{code = #ok{code = transcribe}, data = Id}}, Req, State}; - _ -> roster:error(?MODULE, "invalid transcribe type ~p", [Type]), + _ -> ?LOG_ERROR("invalid transcribe type ~p", [Type]), {reply, {bert, #io{code = #error{code = invalid_data}, data = ErrMsg}}, Req, State} end; {ok, InvalidMsg} -> - roster:info(?MODULE, "invalid audio transcribe for ~p", [InvalidMsg]), + ?LOG_INFO("invalid audio transcribe for ~p", [InvalidMsg]), {reply, {bert, #io{code = invalid_data, data = InvalidMsg}}, Req, State}; - {error, _} = E -> roster:error(?MODULE, "message ~p not found for transcribe", [Id]), + {error, _} = E -> ?LOG_ERROR("message ~p not found for transcribe", [Id]), {reply, {bert, #io{code = E}, #'Message'{id = Id}}, Req, State} end; -info(#'Message'{status = update, id = Id, feed_id = Feed, from = From, to = To, +info(#'Message'{status = update, id = Id, feed_id = Feed, from = From, files = [#'Desc'{id = ID, payload = Payload, data = Data, mime = DMime} = ND | _]}, Req, #cx{params = ClientId, client_pid = C, state=ack} = State) when is_integer(Id) -> PhoneId = case ClientId of <<"sys_bpe">> -> From; <<"emqttd_", _/binary>> -> roster:phone_id(ClientId) end, - roster:info(?MODULE, "~p:Message/update:~p", [PhoneId, Id]), + ?LOG_INFO("~p:Message/update:~p", [PhoneId, Id]), Lang = roster:get_data_val(?LANG_KEY, Data), IO = case kvs:get('Message', Id) of {ok, #'Message'{feed_id = Feed, files = Descs} = Msg} -> @@ -384,7 +385,7 @@ info(#'Message'{status = update, id = Id, feed_id = Feed, from = From, to = To, 1 -> Descs2 = lists:flatten(lists:foldr( fun (#'Desc'{data = NData, id = DescId} = D, [Acc, _]) when Lang == [], ID /= [] -> - FUsers = #'Feature'{value = I} = + #'Feature'{value = I} = case roster:get_data(?USERS_KEY, NData) of [] -> #'Feature'{key = ?USERS_KEY, value = []}; FU -> FU end, [[case DescId of @@ -425,11 +426,11 @@ info(#'Message'{status = update, id = Id, feed_id = Feed, from = From, to = To, info(#'Message'{from = From, to = To} = ReqData, Req, State) -> - roster:info(?MODULE, "~p:~p:Message/unknown:~p", [From, To, {ReqData, Req}]), + ?LOG_INFO("~p:~p:Message/unknown:~p", [From, To, {ReqData, Req}]), {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State}. proc(init, #handler{name = roster_message} = Async) -> - roster:info(?MODULE, "ASYNC:~p", [?MODULE]), + ?LOG_INFO("ASYNC:~p", [?MODULE]), {ok, C} = emqttc:start_link([{client_id, <<"roster_message">>}, {logger, {console, error}}, {reconnect, 5}]), {ok, Async#handler{state = C, seq = 0}}; @@ -442,27 +443,27 @@ proc({send_push, From, To, #'Message'{type = TypeList} = Msg, Action}, #handler{ true -> notify(From, To, Msg, Action); _ -> - roster:info(?MODULE, "ExcessivePush:~p", [From]) + ?LOG_INFO("ExcessivePush:~p", [From]) end; _ -> notify(From, To, Msg, Action) end, {reply, [], H}; proc({#io{} = IO, ClientId}, #handler{state = C} = H) -> - roster:error(?MODULE, "~p", [IO]), + ?LOG_ERROR("~p", [IO]), roster:send_action(C, ClientId, IO), {reply, [], H}; proc({#'Message'{status = update} = Msg, ClientId}, #handler{state = C} = H) -> - roster:info(?MODULE, "UPDATE TRANSCRIBE: ~p", [Msg]), + ?LOG_INFO("UPDATE TRANSCRIBE: ~p", [Msg]), try {reply, {bert, IO}, _, _} = info(Msg, {[], handled}, #cx{params = ClientId, client_pid = C, state = ack}), roster:send_action(C, ClientId, IO) catch Err:Rea -> - roster:error(?MODULE, ":~p~n", [n2o:stack_trace(Err, Rea)]) + ?LOG_ERROR(":~p~n", [n2o:stack_trace(Err, Rea)]) end, {reply, [], H}; proc({mqttc, C, connected}, State = #handler{state = C, seq = S}) -> {ok, State#handler{seq = S + 1}}; proc({mqttc, _C, disconnected}, State) -> {ok, State}; proc(Unknown, #handler{} = H) -> - roster:info(?MODULE, "invalid message data :~p", [Unknown]), + ?LOG_INFO("invalid message data :~p", [Unknown]), {reply, [], H}. diff --git a/apps/roster/src/protocol/roster_presence.erl b/apps/roster/src/protocol/roster_presence.erl index 9ecf2434cf49ffdc5770a463f37347de5830ee16..7bfe3810b55a7b57d1b5204d612b463f0a855e07 100644 --- a/apps/roster/src/protocol/roster_presence.erl +++ b/apps/roster/src/protocol/roster_presence.erl @@ -1,39 +1,35 @@ -module(roster_presence). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). -include_lib("rest_static.hrl"). -compile(export_all). -info(#'Presence'{uid=UID, status=Status} = RequestData, Req, #cx{params = ClientId, client_pid = C} = State) -> - roster:info(?MODULE, "~p:Presence/Status:~p", [ClientId, RequestData]), - {reply, {bert, send_presence(Status, roster:phone_id(ClientId), C)}, Req, State}; - - -info(#'Presence'{} = RequestData, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Presence/unknown:~p", [ClientId, RequestData]), - {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State}. +info(#'Presence'{status=Status} = RequestData, Req, #cx{params = ClientId, client_pid = C} = State) -> + ?LOG_INFO("~p:Presence/Status:~p", [ClientId, RequestData]), + {reply, {bert, send_presence(Status, roster:phone_id(ClientId), C)}, Req, State}. on_connect(Phone, ClientId, C)-> on_connect(Phone, ClientId, C, ?VERSION). on_connect(Phone, ClientId, C, Ver) -> - roster:info(?MODULE, "~p:~p:CONNECT with Version:~p", [Phone, ClientId,Ver]), + ?LOG_INFO("~p:~p:CONNECT with Version:~p", [Phone, ClientId,Ver]), catch n2o_vnode:unsubscribe(ClientId, iolist_to_binary(["actions/", "1", "/micro/", ClientId])), try case send_presence(online, Phone, C, ClientId) of - {error, profile_not_found} -> roster:info(?MODULE, "~p:~p:Connect:ProfileNotFound", [Phone, ClientId]), + {error, profile_not_found} -> ?LOG_INFO("~p:~p:Connect:ProfileNotFound", [Phone, ClientId]), roster:delete_sessions(Phone); - #'Profile'{} = P when Ver==?VERSION -> + #'Profile'{} when Ver==?VERSION -> roster:send_cache(ClientId, C); #'Profile'{} = P-> roster:send_profile(P#'Profile'{settings = amazon_settings(P), status = init}, [], 0, ClientId, C), roster:send_cache(ClientId, C) end catch Err:Rea -> - roster:info(?MODULE, "Catch:~p", [n2o:stack_trace(Err, Rea)]) + ?LOG_INFO("Catch:~p", [n2o:stack_trace(Err, Rea)]) end. on_disconnect(#'Auth'{type = logout, phone = Phone, client_id = ClientId, user_id = PhoneId}, C) -> - roster:info(?MODULE, "~p:~p:DISCONNECT:LOGOUT", [Phone, ClientId]), + ?LOG_INFO("~p:~p:DISCONNECT:LOGOUT", [Phone, ClientId]), send_presence(offline, Phone, C, ClientId), roster:unsubscribe_p2p(ClientId, roster:roster_id(PhoneId)), roster:unsubscribe_room(ClientId), @@ -41,7 +37,7 @@ on_disconnect(#'Auth'{type = logout, phone = Phone, client_id = ClientId, user_i roster:final_disconnect(ClientId); on_disconnect(#'Auth'{type = disconnect, phone = Phone, client_id = ClientId, user_id = PhoneId}, C) -> - roster:info(?MODULE, "~p:~p:DISCONNECT", [Phone, ClientId]), + ?LOG_INFO("~p:~p:DISCONNECT", [Phone, ClientId]), send_presence(offline, Phone, C, ClientId), roster:unsubscribe_p2p(ClientId, roster:roster_id(PhoneId)), roster:unsubscribe_room(ClientId), @@ -52,7 +48,7 @@ on_disconnect(#'Auth'{type = disconnect, phone = Phone, client_id = ClientId, us roster:final_disconnect(ClientId); on_disconnect(#'Auth'{phone = Phone, client_id = ClientId, type=Type}, C) -> - roster:info(?MODULE, "~p:~p:DISCONNECT:~p", [Phone, ClientId, Type]), + ?LOG_INFO("~p:~p:DISCONNECT:~p", [Phone, ClientId, Type]), send_presence(offline, Phone, C, ClientId). on_verify(ClientId, PhoneId) -> roster:sub_client(subscribe, ClientId, PhoneId). @@ -82,7 +78,7 @@ send_presence(Status, Phone, C, ClientId) when Status == online; Status == offli end || AccId <- Rosters], P2; _ -> - roster:info(?MODULE, "~p:~p:~p",[Phone, ClientId,P]), + ?LOG_INFO("~p:~p:~p",[Phone, ClientId,P]), P end; _ -> {error, profile_not_found} @@ -92,7 +88,7 @@ send_presence(Status, Phone, C, ClientId) when Status == online; Status == offli send_presence(Status, PhoneId, C) -> case kvs:get('Profile', roster:phone(PhoneId)) of {ok, #'Profile'{rosters = Rosters} = P} -> - kvs:put(P#'Profile'{presence = Status, update = Now = roster:now_msec()}), + kvs:put(P#'Profile'{presence = Status, update = roster:now_msec()}), [case kvs:get('Roster', AccId) of {ok, #'Roster'{phone = Phone}} -> PId = roster:phone_id(Phone, AccId), @@ -107,4 +103,4 @@ amazon_settings(#'Profile'{phone = ProfileId, settings = Settings}) -> StaticFeatures = lists:foldl(fun({Key, Value}, Acc) -> Acc ++ [#'Feature'{id = iolist_to_binary([ProfileId, Key]), key = Key, value = Value, group = <<"SPECIAL_ANDROID_KEYS">>}] end, [], [{<<"ACCESS_KEY">>, ?AWS_ACCESS_KEY_ID}, {<<"SECRET_KEY">>, ?AWS_SECRET_ACCESS_KEY}]), - Settings ++ StaticFeatures. \ No newline at end of file + Settings ++ StaticFeatures. diff --git a/apps/roster/src/protocol/roster_profile.erl b/apps/roster/src/protocol/roster_profile.erl index b9f85cbe99db137926b685e07e3b7fd407990760..66d270a124b96292c1ab3b199d4e69da6e3353ed 100644 --- a/apps/roster/src/protocol/roster_profile.erl +++ b/apps/roster/src/protocol/roster_profile.erl @@ -1,4 +1,5 @@ -module(roster_profile). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). -compile(export_all). @@ -16,7 +17,7 @@ start() -> n2o_async:start(#handler{ %%info(#'Profile'{phone=Phone, rosters=Rosters, status=set} = Data, Req, %% #cx{params = ClientId} = State) -> -%% roster:info(?MODULE, "~p:~p:Profile/set:~s", [Phone,ClientId,io_lib:format("~p",[Data])]), +%% ?LOG_INFO("~p:~p:Profile/set:~s", [Phone,ClientId,io_lib:format("~p",[Data])]), %% D=case roster:parts_phone_id(roster:phone_id(ClientId)) of %% [Phone, RosterId]-> kvs:put(Data2 = Data#'Profile'{rosters=[RosterId],update = roster:now_msec()}), Data2; %% _ -> #io{code=#error{code=invalid_data}} end, @@ -24,7 +25,7 @@ start() -> n2o_async:start(#handler{ info(#'Profile'{status=patch, phone=Phone, balance = Balance} = Data, Req, #cx{params = ClientId} = State) when Balance >= 0 -> - roster:info(?MODULE, "~p:~p:Profile/patch", [Phone,ClientId]), + ?LOG_INFO("~p:~p:Profile/patch", [Phone,ClientId]), Res = case kvs:get('Profile', Phone) of {ok, #'Profile'{}=Profile} -> P = roster:patch_profile(Profile, Data), @@ -35,7 +36,7 @@ info(#'Profile'{status=patch, phone=Phone, balance = Balance} = Data, Req, info(#'Profile'{status = delete, phone=UID, rosters = [_|_]=Rs }=Data, Req, #cx{params = ClientId, client_pid = C} = State) -> - roster:info(?MODULE, "~p:~p:Profile/remove:~s", [UID,ClientId,io_lib:format("~p",[Data])]), + ?LOG_INFO("~p:~p:Profile/remove:~s", [UID,ClientId,io_lib:format("~p",[Data])]), case kvs:get('Profile',UID) of {ok,#'Profile'{rosters=Rosters}=P}-> NewRosters=(Rosters--Rs), @@ -57,7 +58,7 @@ info(#'Profile'{status = delete, phone=UID, rosters = [_|_]=Rs }=Data, Req, info(#'Profile'{status = remove, phone = Phone}=Data, Req, #cx{params = ClientId, client_pid = C} = State) -> - roster:info(?MODULE, "~p:~p:Profile/remove:~s", [Phone,ClientId,io_lib:format("~p",[Data])]), + ?LOG_INFO("~p:~p:Profile/remove:~s", [Phone,ClientId,io_lib:format("~p",[Data])]), case kvs:get('Profile',Phone) of {ok,#'Profile'{rosters=Rosters}} -> [ begin PhoneId=roster:phone_id(Phone,X), [begin [PhoneF, _]=roster:parts_phone_id(Friend), @@ -80,9 +81,9 @@ info(#'Profile'{ params = ClientId, client_pid = ClientPid } = State) -> - roster:info(?MODULE, "~p:Profile/get.Request/enter:~p", [ClientId, P2]), + ?LOG_INFO("~p:Profile/get.Request/enter:~p", [ClientId, P2]), Phone = roster:phone(ClientId), - roster:info(?MODULE, "~p:Profile/get.Request:~p", [ClientId, Phone]), + ?LOG_INFO("~p:Profile/get.Request:~p", [ClientId, Phone]), R = case kvs:get('Profile', Phone) of {ok, #'Profile'{} = P} -> Size = case {LastSync, catch binary_to_integer(roster:get_data_val(<<"size">>, Settings))} of @@ -102,7 +103,7 @@ info(#'Profile'{ ClientId, ClientPid ]), - roster:info(?MODULE, "timer_send_profile in microseconds ~p", [Timer]), + ?LOG_INFO("timer_send_profile in microseconds ~p", [Timer]), <<>>; {error, Reason} -> #io{code = #error{code = Reason}} @@ -117,11 +118,11 @@ info(#'Profile'{} = Data, Req, State) -> {reply, {bert, Data}, Req, State}. proc(init,#handler{name=?MODULE} = Async) -> - roster:info(?MODULE, "ASYNC",[]), + ?LOG_INFO("ASYNC",[]), {ok,Async}; -proc({restart, M}, #handler{state = {C, Proc}, seq = S} = H) -> - roster:info(?MODULE, "BPE PROC restarted", []), +proc({restart, M}, #handler{state = {_C, _Proc}} = H) -> + ?LOG_INFO("BPE PROC restarted", []), roster:restart_module(M), {reply, [], H}. @@ -145,10 +146,10 @@ proc({restart, M}, #handler{state = {C, Proc}, seq = S} = H) -> %% #io{code = #ok{code = aws}, data = #'Service'{id = AccessKeyId, type = aws, login = SecretAccessKey, %% password = SessionToken, expiration = Expiration, status = verified}} %% end, -%% roster:info(?MODULE, "Debug.AWSProcResponse:~p", [Res]), +%% ?LOG_INFO("Debug.AWSProcResponse:~p", [Res]), %% case emqttd_cm:lookup(ClientId) of %% undefined -> -%% roster:info(?MODULE, "CannotFindConnection:~p", [ClientId]); +%% ?LOG_INFO("CannotFindConnection:~p", [ClientId]); %% _-> roster:send_action(C, ClientId, Res) %% end, %% {reply, [], H}. diff --git a/apps/roster/src/protocol/roster_push.erl b/apps/roster/src/protocol/roster_push.erl index 961ea4961c1d28ae5f7be2ad918e3bdb8bea1581..b6e67162c5aee7831287e778536091a670e3a239 100644 --- a/apps/roster/src/protocol/roster_push.erl +++ b/apps/roster/src/protocol/roster_push.erl @@ -1,4 +1,5 @@ -module(roster_push). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). -include_lib("kvs/include/kvs.hrl"). @@ -7,7 +8,7 @@ start() -> n2o_async:start(#handler{module = ?MODULE, class = system, group = roster, name = ?MODULE, state = []}). proc(init, #handler{name = ?MODULE} = Async) -> - roster:info(?MODULE, "ASYNC", []), + ?LOG_INFO("ASYNC", []), {ok, Async}; proc({async_push, Session, Payload, PushAlert, PushType}, #handler{} = H) -> @@ -19,7 +20,7 @@ send_push_notification(#'Auth'{os = OS, push = PushToken, user_id = PhoneId, set case PushToken of [] -> skip; _ -> - roster:info(?MODULE, "~p:~p:~pPushAlert:~p", + ?LOG_INFO("~p:~p:~pPushAlert:~p", [PhoneId, OS, binary:part(PushToken, 0, erlang:min(25, size(PushToken))), PushAlert]), send_push_notification(OS, PushToken, Payload, PushAlert, PushType, AuthSettings) end. diff --git a/apps/roster/src/protocol/roster_room.erl b/apps/roster/src/protocol/roster_room.erl index c61549e5b68f4106fe05fa482ff2d8e8c78bc9a4..16f6254b50456862be985b65b6ef9fb29be8c1e9 100644 --- a/apps/roster/src/protocol/roster_room.erl +++ b/apps/roster/src/protocol/roster_room.erl @@ -1,4 +1,5 @@ -module(roster_room). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). -include_lib("roster/include/static/roster_text.hrl"). @@ -20,24 +21,24 @@ info(#'Room'{status = create, id = Room, admins = [Admin|_], type=Type} = R, Req ok = kvs:put(Link), info(R#'Room'{status = add, id = Room}, Req, State#cx{state = create}); -info(#'Room'{status = create, id = <>, name = Name, admins = [Owner]= Admins, members = [], data = Data} = R, Req, +info(#'Room'{status = create, id = <>, name = Name, admins = [_Owner], members = []} = R, Req, #cx{params = <<"emqttd_", _/binary>>=ClientId} = State) -> Length = length(unicode:characters_to_list(Name)), ExistingRoom = element(1, kvs:get('Room', Room)) == error, - roster:info(?MODULE, "~p:Room/create single room:~p", [ClientId, Name]), + ?LOG_INFO("~p:Room/create single room:~p", [ClientId, Name]), if not(Length >= ?MIN_ROOM_LENGTH) -> {reply, {bert, #io{code = #error{code = room_name_too_short}}}, Req, State}; not(Length =< ?MAX_ROOM_LENGTH) -> {reply, {bert, #io{code = #error{code = room_name_too_long}}}, Req, State}; not(ExistingRoom) -> {reply, {bert, #io{code = #error{code = room_already_exists}}}, Req, State}; - true -> #'Member'{phone_id = OwnPId} = Adm = #'Member'{phone_id = roster:phone_id(ClientId)}, + true -> #'Member'{phone_id =_OwnPId} = Adm = #'Member'{phone_id = roster:phone_id(ClientId)}, Admin = Adm#'Member'{id = [], status = admin, feed_id = #muc{name = Room}, update = roster:now_msec()}, info(R#'Room'{admins = [Admin], members = []}, Req, State#cx{state = verified}) end; -info(#'Room'{status = create, id = <>, name = Name, admins = [Owner|TA]= Admins, members = Members, data = Data} = R, Req, +info(#'Room'{status = create, id = <>, name = Name, admins = [Owner|TA]= Admins, members = Members} = R, Req, #cx{params = ClientId} = State) -> Length = length(unicode:characters_to_list(Name)), - roster:info(?MODULE, "~p:Room/create:~p", [ClientId, Name]), + ?LOG_INFO("~p:Room/create:~p", [ClientId, Name]), Prefix = hd(binary:split(ClientId, <<"_">>)), case Length >= ?MIN_ROOM_LENGTH andalso Length =< ?MAX_ROOM_LENGTH andalso element(1, kvs:get('Room', Room)) == error @@ -59,7 +60,7 @@ info(#'Room'{status = create, id = <>, name = Name, admins = [Owner _ -> {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State} end; info(#'Room'{status = patch, id = Room, name = Name, data = AvatarDesc} = R, Req, #cx{params = ClientId, client_pid = C} = State) when Room /= [] -> - roster:info(?MODULE, "~p:Room/patch:~p", [ClientId, R]), + ?LOG_INFO("~p:Room/patch:~p", [ClientId, R]), roster:verify_descs(AvatarDesc), {reply, {bert, case kvs:get('Room', Room) of @@ -115,7 +116,7 @@ info(#'Room'{status = patch, id = Room, name = Name, data = AvatarDesc} = R, Req end}, Req, State}; info(#'Member'{status = patch, id = Id} = Member, Req, #cx{params = ClientId, from = From} = State) -> - roster:info(?MODULE, "~p:Member/patch:~p", [ClientId, Member]), + ?LOG_INFO("~p:Member/patch:~p", [ClientId, Member]), PhoneId = roster:phone_id(ClientId), {IO, Topic} = case kvs:get('Member', Id) of @@ -130,7 +131,7 @@ info(#'Room'{status = St, id = Room, members = Members, admins = Admins, readers #cx{params = ClientId, client_pid = C, state = RStatus} = State) when (St == add orelse (St == join andalso RStatus /= create)) andalso (Members /= [] orelse Admins /= []) -> - roster:info(?MODULE, "~p:Room/~p:~p", [ClientId, St, Room]), + ?LOG_INFO("~p:Room/~p:~p", [ClientId, St, Room]), APId = case Prefix = hd(binary:split(ClientId, <<"_">>)) of <<"sys">> when St == add andalso Admins /= [] orelse St == join -> (hd(case St of join -> Members; _ -> Admins end))#'Member'.phone_id; @@ -163,7 +164,7 @@ info(#'Room'{status = St, id = Room, members = Members, admins = Admins, readers {M2, UpdRoom} end, case MmbrRoom of error -> - roster:info(?MODULE, "ERROR:add_member:RosterNotFound:~p", [PhoneId]), + ?LOG_INFO("ERROR:add_member:RosterNotFound:~p", [PhoneId]), MAcc; {ignore, M5} -> {Ms ++ [M5#'Member'{presence = roster:is_online(PhoneId)}], Alss, NewMs, StoredRoom}; @@ -220,7 +221,7 @@ info(#'Room'{status = St, id = Room, members = Members, admins = Admins, readers info(#'Room'{status = remove, members = Members, admins = Admins0, id = Room}, Req, #cx{params = ClientId, client_pid = C} = State) -> - roster:info(?MODULE, "~p:Room/remove:~p", [ClientId, Room]), + ?LOG_INFO("~p:Room/remove:~p", [ClientId, Room]), {APId, Admins} = case hd(binary:split(ClientId, <<"_">>)) of <<"sys">> -> {(hd(Admins0))#'Member'.phone_id, []}; <<"emqttd">> -> {roster:phone_id(ClientId), Admins0} @@ -264,7 +265,7 @@ info(#'Room'{status = remove, members = Members, admins = Admins0, id = Room}, R info(#'Room'{status = delete, members = [], admins = [], id = Room} = R, Req, #cx{params = <<"sys_", _/binary>> = ClientId, client_pid = C} = State) -> - roster:info(?MODULE, "~p:Room/delete:~p", [ClientId, Room]), + ?LOG_INFO("~p:Room/delete:~p", [ClientId, Room]), case kvs_stream:load_writer(Feed = #muc{name = Room}) of #writer{first = #'Message'{id = FirstMsgId}, cache = #'Message'{id = LastMsgId}} -> FilterFun = fun(#'Message'{type = [sys|_]}, _Acc) -> true; (_, _Acc) -> false end, @@ -291,7 +292,7 @@ info(#'Room'{status = delete, members = [], admins = [], id = Room} = R, Req, {reply, {bert, <<>>}, Req, State}; info(#'Room'{status = leave, id = RoomId}, Req, #cx{params = ClientId, client_pid = C} = State) -> - roster:info(?MODULE, "~p:Room/leave:~p", [ClientId, RoomId]), + ?LOG_INFO("~p:Room/leave:~p", [ClientId, RoomId]), {reply, {bert, case kvs:get('Room', RoomId) of {ok, #'Room'{} = Room0} -> @@ -335,7 +336,7 @@ info(#'Room'{status = leave, id = RoomId}, Req, #cx{params = ClientId, client_pi end}, Req, State}; info(#'Room'{status = get, id = Room}, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Room/get:~p", [ClientId, Room]), + ?LOG_INFO("~p:Room/get:~p", [ClientId, Room]), {reply, {bert, case kvs:get('Room', Room) of {ok, #'Room'{} = R} -> case roster:muc_member(ClientId, Room) of @@ -351,7 +352,7 @@ info(#'Room'{status = get, id = Room}, Req, #cx{params = ClientId} = State) -> info(#'Room'{status = Mute, id = Room}, Req, #cx{params = ClientId, client_pid = C} = State) when Mute == mute;Mute == unmute -> - roster:info(?MODULE, "~p:Room/~p:~p", [ClientId, Mute, Room]), + ?LOG_INFO("~p:Room/~p:~p", [ClientId, Mute, Room]), Mute2 = case Mute of mute -> Mute;_ -> [] end, {reply, {bert, case roster:muc_member(ClientId, Room) of #'Member'{phone_id = PhoneId} -> @@ -366,15 +367,15 @@ info(#'Room'{status = Mute, id = Room}, Req, #cx{params = ClientId, client_pid = _ -> #io{code = #error{code = not_member}} end}, Req, State}; info(#'Room'{} = Room, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Room/unknown:~p", [ClientId, Room]), + ?LOG_INFO("~p:Room/unknown:~p", [ClientId, Room]), {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State}; info(#'Member'{} = Member, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:Member/unknown:~p", [ClientId, Member]), + ?LOG_INFO("~p:Member/unknown:~p", [ClientId, Member]), {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State}. proc(init, #handler{name = roster_room} = Async) -> - roster:info(?MODULE, "ASYNC ROOM PROC started", []), + ?LOG_INFO("ASYNC ROOM PROC started", []), {ok, Async}; %% TODO use roster_push instead proc({send_push, NewMembers, Msg, Type}, #handler{} = H) -> @@ -428,4 +429,4 @@ proc({send_push, NewMembers, Msg, Type}, #handler{} = H) -> end, roster_push:send_push_notification(Ses, Payload, PushAlert, PushType) end || {MemPhoneId, _} = Mem <- ToRoomMembers, Ses <- kvs:index('Auth', user_id, MemPhoneId) - ], {reply, [], H}. \ No newline at end of file + ], {reply, [], H}. diff --git a/apps/roster/src/protocol/roster_roster.erl b/apps/roster/src/protocol/roster_roster.erl index 6afa73b09d499d5dc9d22d8721d65c650a071659..01542644ed0b220e480416f0fc6770ca4fea205b 100644 --- a/apps/roster/src/protocol/roster_roster.erl +++ b/apps/roster/src/protocol/roster_roster.erl @@ -1,4 +1,5 @@ -module(roster_roster). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). -compile(export_all). @@ -14,7 +15,7 @@ info(#'Roster'{status = patch, avatar = Avatar} = Data, Req, state = {verified, #'Roster'{id = RosterId0, phone = Phone, avatar = OldAvatar, roomlist = Rooms, status = Status} = Roster}} = State) -> RosterId = case ClientId of <<"sys_", _/binary>> -> RosterId0; _ -> roster:roster_id(ClientId) end, - roster:info(?MODULE, "~p:~p:Roster/patch:~s", [RosterId, ClientId, io_lib:format("~p", [Data])]), + ?LOG_INFO("~p:~p:Roster/patch:~s", [RosterId, ClientId, io_lib:format("~p", [Data])]), IO = case PreUpdRoster = roster:patch_roster(Roster, Data) of #error{code = invalid_data} = Err -> #io{code = Err}; _ -> @@ -70,13 +71,13 @@ info(#'Roster'{id = RosterId, nick = NickToBeValidated, status = nick} = Data, R ActedRosterId = case ClientId of <<"sys_", _/binary>> -> RosterId; _ -> roster:roster_id(ClientId) end, - roster:info(?MODULE, "~p:~p:Roster/nick.Request:~p", [ClientId, ActedRosterId, Data]), + ?LOG_INFO("~p:~p:Roster/nick.Request:~p", [ClientId, ActedRosterId, Data]), R = case kvs:get('Roster', ActedRosterId) of {error, Reason} -> #io{code = #error{code = Reason}}; {ok, Roster = #'Roster'{phone = Phone, nick = OldNick, status = Status}} -> case regexp_api:validate_string(NickToBeValidated) of {error, NickToBeValidated} -> - roster:error(?MODULE, "Debug.GotInvalidNickFormatError: ~p", [NickToBeValidated]), + ?LOG_ERROR("Debug.GotInvalidNickFormatError: ~p", [NickToBeValidated]), #io{code = #error{code = invalid_nick}}; {ok, Nick} -> % OldNickLow = OldNick, @@ -110,16 +111,16 @@ info(#'Roster'{id = RosterId, nick = NickToBeValidated, status = nick} = Data, R n2o_async:send(system, ?MODULE, {update_ac, UpdNewC}), <<>>; true -> - roster:info(?MODULE, "Debug.GotNickAlreadyUsedError", []), + ?LOG_INFO("Debug.GotNickAlreadyUsedError", []), #io{code = #error{code = nick}} end end end, - roster:info(?MODULE, "~p:~p:Roster/nick.Response:~p", [ClientId, ActedRosterId, R]), + ?LOG_INFO("~p:~p:Roster/nick.Response:~p", [ClientId, ActedRosterId, R]), {reply, {bert, R}, Req, State}; info(#'Roster'{id=RosterId, status=get},Req, #cx{params = ClientId }=State) -> - roster:info(?MODULE, "~p:~p:Roster/get",[RosterId,ClientId]), + ?LOG_INFO("~p:~p:Roster/get",[RosterId,ClientId]), {reply,{bert, case kvs:get('Roster', RosterId) of {ok,#'Roster'{id = Id, phone = Phone, userlist = Contacts}=Roster} -> Roster#'Roster'{userlist = @@ -130,47 +131,47 @@ info(#'Roster'{id=RosterId, status=get},Req, #cx{params = ClientId }=State) -> %%TODO It will be updated in bring with multiaccounts %info(#'Roster'{phone=Phone, id=RosterId, status=remove},Req, % #cx{params = ClientId }=State) -> -% roster:info(?MODULE, "~p:~p:Roster/remove:~p",[Phone,ClientId,RosterId]), +% ?LOG_INFO("~p:~p:Roster/remove:~p",[Phone,ClientId,RosterId]), % Result = roster:remove_roster(Phone,RosterId), % {reply, {bert, {io,Result,<<>>}}, Req, State}; %% TODO delete it later as excessive method %% info(#'Roster'{phone = Phone, status = create}, Req, #cx{params = ClientId} = State) -> -%% roster:info(?MODULE, "~p:~p:Roster/create", [Phone, ClientId]), +%% ?LOG_INFO("~p:~p:Roster/create", [Phone, ClientId]), %% {roster, X} = roster:add_roster(Phone), %% {ok, Roster} = kvs:get('Roster', X), %% {reply, {bert, Roster}, Req, State}; -info(#'Roster'{id = RosterId, userlist = List, status = del}, Req, #cx{params = ClientId, session = Token} = State) -> - roster:info(?MODULE, "~p:~p:Roster/del:~p", [RosterId, ClientId, length(List)]), +info(#'Roster'{id = RosterId, userlist = List, status = del}, Req, #cx{params = ClientId} = State) -> + ?LOG_INFO("~p:~p:Roster/del:~p", [RosterId, ClientId, length(List)]), {reply, {bert, #io{code = case roster:remove_contacts(RosterId, List, ClientId) of #ok2{} = O -> O; E -> E end}}, Req, State}; -info(#'Roster'{id = RosterId, userlist = RosterList, status = add}, Req, #cx{params = ClientId, session = Token} = State) -> - roster:info(?MODULE, "~p:~p:Roster/add:~p", [RosterId, ClientId, length(RosterList)]), +info(#'Roster'{id = RosterId, userlist = RosterList, status = add}, Req, #cx{params = ClientId} = State) -> + ?LOG_INFO("~p:~p:Roster/add:~p", [RosterId, ClientId, length(RosterList)]), List = roster:to_contact(RosterList, request), {reply, {bert, #io{code = roster:add_contacts(RosterId, List)}}, Req, State}; info(#'Roster'{phone = Phone, status = list}, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:~p:Roster/list", [Phone, ClientId]), + ?LOG_INFO("~p:~p:Roster/list", [Phone, ClientId]), {reply, {bert, {io, roster:list_rosters(Phone), <<>>}}, Req, State}; info(#'Roster'{id = RosterId, status = Status}, Req, #cx{params = ClientId} = State) -> - roster:info(?MODULE, "~p:~p:Roster/unknown:~p", [RosterId, ClientId, Status]), + ?LOG_INFO("~p:~p:Roster/unknown:~p", [RosterId, ClientId, Status]), {reply, {bert, #io{code = #error{code = invalid_data}}}, Req, State}. proc(init, #handler{name = ?MODULE} = Async) -> - roster:info(?MODULE, "ASYNC", []), + ?LOG_INFO("ASYNC", []), {ok, Async}; proc({update_ac, #'Contact'{phone_id = PhoneId} = C}, #handler{} = H) -> - [Phone, RosterId] = roster:parts_phone_id(PhoneId), - [begin [P, FriendId] = roster:parts_phone_id(PId), - Res = case roster:get_contact(FriendId, PhoneId) of - #'Contact'{reader = R, update = UPT} -> - roster:update_contact(FriendId, C#'Contact'{reader = R, presence = [], update = UPT}); - E -> E end end - || #'Contact'{phone_id = PId} <- roster:get_on_status(RosterId, friend)], + [_Phone, RosterId] = roster:parts_phone_id(PhoneId), + [begin [_P, FriendId] = roster:parts_phone_id(PId), + case roster:get_contact(FriendId, PhoneId) of + #'Contact'{reader = R, update = UPT} -> + roster:update_contact(FriendId, C#'Contact'{reader = R, presence = [], update = UPT}); + E -> E end end + || #'Contact'{phone_id = PId} <- roster:get_on_status(RosterId, friend)], {reply, [], H}; proc({update_index, #'Roster'{id = RosterId, nick = Nick}}, #handler{} = H) -> @@ -179,6 +180,6 @@ proc({update_index, #'Roster'{id = RosterId, nick = Nick}}, #handler{} = H) -> {reply, [], H}; %% TODO refactor me -proc({delete_index, #'Roster'{id = RosterId, nick = Nick}}, #handler{} = H) -> +proc({delete_index, #'Roster'{nick = Nick}}, #handler{} = H) -> kvs:delete('Index', {nick, Nick}), - {reply, [], H}. \ No newline at end of file + {reply, [], H}. diff --git a/apps/roster/src/protocol/roster_search.erl b/apps/roster/src/protocol/roster_search.erl index 3e2c55c49326f20e37f90d493d5e0031b0d34af6..9ef5658037cf995200cb6d0f34f431377fede8d2 100644 --- a/apps/roster/src/protocol/roster_search.erl +++ b/apps/roster/src/protocol/roster_search.erl @@ -1,4 +1,5 @@ -module(roster_search). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). -compile(export_all). @@ -7,26 +8,26 @@ info(#'Search'{id = FromRosterId, ref = Ref, field = <<"link">>, type = '==', value = SearchVal, status = room}, Req, #cx{params = <<"emqttd_",_/binary>> = ClientId} = State) -> - roster:info(?MODULE, "~p:~p:Search/RoomByLink:~p", [FromRosterId, ClientId, SearchVal]), + ?LOG_INFO("~p:~p:Search/RoomByLink:~p", [FromRosterId, ClientId, SearchVal]), ResRoom = case roster_channel_helper:get_channel_by_link(SearchVal) of [] -> []; R -> [R] end, Res = #'Roster'{id = FromRosterId, roomlist = ResRoom, status = search}, - roster:info(?MODULE, "Res.Search/RoomByLink:~p", [Res]), + ?LOG_INFO("Res.Search/RoomByLink:~p", [Res]), {reply, {bert, {io, {ok, Ref}, Res}}, Req, State}; info(#'Search'{id = FromRosterId, ref = Ref, field = <<"phone_id">>, type = '==', value = [UUID], status = contact}, Req, #cx{params = <<"emqttd_",_/binary>> = ClientId} = State) -> - roster:info(?MODULE, "~p:~p:Search/UUIDk:~p", [FromRosterId, ClientId, UUID]), + ?LOG_INFO("~p:~p:Search/UUIDk:~p", [FromRosterId, ClientId, UUID]), Res = #'Contact'{phone_id = micro:uuid_to_id('LinkRoster', UUID)}, - roster:info(?MODULE, "Res.Search/UUID:~p", [Res]), + ?LOG_INFO("Res.Search/UUID:~p", [Res]), {reply, {bert, {io, {ok, Ref}, Res}}, Req, State}; info(#'Search'{id = From, ref = Ref, field = <<"nick">>, type = '==', value = Q, status = contact}, Req, #cx{params = <<"emqttd_", _/binary>> = ClientId} = State) when Ref /=[] -> - roster:info(?MODULE, "~p:~p:Search/contact:~p", [From, ClientId, Q]), + ?LOG_INFO("~p:~p:Search/contact:~p", [From, ClientId, Q]), PhoneId = case ClientId of % <<"sys_bpe">> -> From0; <<"emqttd_", _/binary>> -> roster:phone_id(ClientId); @@ -41,12 +42,12 @@ info(#'Search'{id = From, ref = Ref, field = <<"nick">>, type = '==', value = Q, Cont#'Contact'{reader = []}; _ -> [] end end || RosterId <- Rs] end || V <- Q] end, Res = #'Roster'{id = From, userlist = lists:flatten(L), status = contact}, - roster:info(?MODULE, "Res.Search/contact:~p", [Res]), + ?LOG_INFO("Res.Search/contact:~p", [Res]), {reply, {bert, {io, {ok, Ref}, Res}}, Req, State}; info(#'Search'{id = From, ref = Ref, field = <<"phone">>, type = '==', value = Phones, status = contact}, Req, #cx{params = <<"emqttd_", _/binary>> = ClientId} = State) when is_list(Phones), Ref /=[] -> - %roster:info(?MODULE, "~p:~p:Search/phone:~p", [From, ClientId, Phones]), + %?LOG_INFO("~p:~p:Search/phone:~p", [From, ClientId, Phones]), PhoneId = case ClientId of % <<"sys_bpe">> -> From0; <<"emqttd_", _/binary>> -> roster:phone_id(ClientId); @@ -62,25 +63,25 @@ info(#'Search'{id = From, ref = Ref, field = <<"phone">>, type = '==', value = P end end || Phone <- lists:usort(Phones), #'Roster'{id=FriendId, phone=_ToPhone}=R <- kvs:index('Roster',phone, Phone)] end, Res = #'Roster'{id = From, userlist = lists:flatten(Result), status = contact}, - roster:info(?MODULE, ":~P", [Res, 30]), - %roster:info(?MODULE, "Res.Search/phone:~p", [Res]), + ?LOG_INFO(":~P", [Res, 30]), + %?LOG_INFO("Res.Search/phone:~p", [Res]), {reply, {bert, {io, {ok, Ref}, Res}}, Req, State}; %% TBD is this method neccesery? info(#'Search'{id = From, ref = Ref, field = K, type = 'like', value = V, status = roster}, Req, #cx{params = <<"emqttd_", _/binary>> = ClientId} = State) when is_atom(K), Ref /=[] -> - roster:info(?MODULE, "~p:~p:Search/names:~p", [From, ClientId, V]), + ?LOG_INFO("~p:~p:Search/names:~p", [From, ClientId, V]), L = [binary_to_list(string:lowercase(V)) ++ F || F <- ets:match(K, {binary_to_list(string:lowercase(V)) ++ '$1'})], {reply, {bert, {io, {ok, Ref}, L}}, Req, State}; info(#'Search'{} = Data, Req, State) -> - roster:info(?MODULE, "Search/unknown:~p", [Data]), + ?LOG_INFO("Search/unknown:~p", [Data]), {reply, {bert, {io, {error,invalid_data}}}, Req, State}. %% TODO search by prefix %%proc(init, #handler{name = roster_search} = Async) -> -%% roster:info(?MODULE, "Search ASYNC started", []), +%% ?LOG_INFO("Search ASYNC started", []), %% case mnesia:wait_for_tables(['Index'], 1000000) of %% ok -> index('Index', nick); %% E -> E diff --git a/apps/roster/src/rest/admin_whitelist.erl b/apps/roster/src/rest/admin_whitelist.erl deleted file mode 100644 index 6df46ca442acfb29bf77175cf03925aba1617d9a..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/admin_whitelist.erl +++ /dev/null @@ -1,10 +0,0 @@ --module(admin_whitelist). --include("roster.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --compile({parse_transform, lager_transform}). --export([handle_request/3]). - -handle_request('GET', _, Req) -> - roster:info(?MODULE, "~p:GET:~p", [Req:get(path), Req:parse_qs()]), - {ok, HTMLOutput} = phone_numbers_tpl:render([{title, <<"Whitelist">>}]), - Req:respond({?HTTP_CODE_200, [{"Content-Type", "text/html"}], HTMLOutput}). \ No newline at end of file diff --git a/apps/roster/src/rest/helpers/rest_auth_helper.erl b/apps/roster/src/rest/helpers/rest_auth_helper.erl deleted file mode 100644 index deb56d0e4a1750c997304456a7166f15c9674689..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/helpers/rest_auth_helper.erl +++ /dev/null @@ -1,62 +0,0 @@ --module(rest_auth_helper). --include("roster.hrl"). --include_lib("kvs/include/metainfo.hrl"). - --export([ - description/0, - authorized/1 -]). - --define(AUTH_USERNAME, proplists:get_value(username, application:get_env(rest, basic_auth, []))). --define(AUTH_PASSWORD, proplists:get_value(password, application:get_env(rest, basic_auth, []))). - -description() -> "Rest Basic Authorization Helper". - -authorized(Req) -> - [[], RootPathElem | _] = string:split(Req:get(path), "/", all), - case RootPathElem of - "api" -> %% If the root path of url is 'api' look for Bearer Authentication - case Req:get_header_value("Authorization") of - "Bearer " ++ BearerAuth -> - try - [Token, CId] = string:split(BearerAuth, "/"), - AuthToken = list_to_binary(Token), - ClientId = list_to_binary(CId), - case micro_auth:validate_token(AuthToken) of %% Check if AuthToken is JWT - {ok, UUID} -> - PhoneId = micro:uuid_to_id('LinkRoster', UUID), - case kvs:get('Auth', ClientId) of - {ok, #'Auth'{user_id = PhoneId}} -> {true, api}; - _ -> {error, invalid_token} - end; - error -> %% Seems like token is not JWT - case roster:parse_token(AuthToken) of %% Try using the old Auth - {error, _} = Err -> Err; - _ -> - case kvs:get('Auth', ClientId) of - {ok, #'Auth'{token = DBToken}} when DBToken == AuthToken -> {true, api}; - {error, not_found} -> {error, invalid_token} - end - end - end - catch - _:_ -> {error, invalid_token} - end; - _ -> {error, invalid_token} - end; - _ -> %% If the root path is not 'api' look for Basic Authentication - case Req:get_header_value("Authorization") of - "Basic " ++ BasicAuth -> - case rest_main_helper:is_base64(BasicAuth) of - true -> - case list_to_tuple(binary:split(base64:decode(BasicAuth), <<":">>)) of - {Username, Password} -> - case (nitro:to_binary(Username) == nitro:to_binary(?AUTH_USERNAME) andalso nitro:to_binary(Password) == nitro:to_binary(?AUTH_PASSWORD)) of - true -> true; - _ -> - roster:info(?MODULE, "BasicAuthFailure:Username=~s,Password=~p", [Username, Password]), - false end; - _ -> false end; - _ -> false end; - _ -> false end - end. \ No newline at end of file diff --git a/apps/roster/src/rest/helpers/rest_gw.erl b/apps/roster/src/rest/helpers/rest_gw.erl index 1be6a77f2b17de192a7697cfe318bb2356d1b74f..466a59c3e9036a183b9436237c3e07ae5ae4bb3f 100644 --- a/apps/roster/src/rest/helpers/rest_gw.erl +++ b/apps/roster/src/rest/helpers/rest_gw.erl @@ -1,8 +1,5 @@ -module(rest_gw). -include("roster.hrl"). --include_lib("n2o/include/n2o.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --include_lib("kvs/include/metainfo.hrl"). -compile(export_all). add_as_friend(SenderType, SenderPhoneId, ReceiverPhoneId) -> @@ -48,4 +45,4 @@ send_p2p_message(SenderPhoneId, PhoneId, Text) -> case rest_main_helper:send_to_mqtt(sync, Message) of {reply, {bert, <<>>}, _, _} -> ok; _ -> {error, send_message} - end. \ No newline at end of file + end. diff --git a/apps/roster/src/rest/helpers/rest_log_helper.erl b/apps/roster/src/rest/helpers/rest_log_helper.erl deleted file mode 100644 index 357b8414e8a8b86fa4d77dfc83bf246c76568fe6..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/helpers/rest_log_helper.erl +++ /dev/null @@ -1,10 +0,0 @@ --module(rest_log_helper). --export([ - checker_error_logs/3, - checker_error_logs/4 -]). - -checker_error_logs(Req, Error, Status) -> - checker_error_logs(Req, Error, Status, ?MODULE). -checker_error_logs(Req, Error, Status, ModuleName) -> - roster:info(ModuleName, "~p:~p:~p:~p(Query:~p;Body:~p)", [Req:get(method), Req:get(path), Status, Error, Req:parse_qs(), mochiweb_request:recv_body(Req)]). diff --git a/apps/roster/src/rest/helpers/rest_main_helper.erl b/apps/roster/src/rest/helpers/rest_main_helper.erl index 33ba9258ab3ce801da4b13ee512826a2347b1f47..94b4b5570a3d5a3b28ccc4fee1176507949e81c2 100644 --- a/apps/roster/src/rest/helpers/rest_main_helper.erl +++ b/apps/roster/src/rest/helpers/rest_main_helper.erl @@ -1,8 +1,8 @@ -module(rest_main_helper). +-include_lib("kernel/include/logger.hrl"). -include("rest_static.hrl"). -include("roster.hrl"). -include_lib("roster/include/static/rest_text.hrl"). --include_lib("roster/include/static/rest_var.hrl"). -include_lib("n2o/include/n2o.hrl"). -export([ description/0, @@ -101,7 +101,7 @@ validate_msg_to_publish(Data) -> try binary_to_list(base64:decode(Data)) catch E:R -> - roster:info(?MODULE, "~p:~pBase64 Decoding Error! ~p", [E, R, Data]), + ?LOG_INFO("~p:~pBase64 Decoding Error! ~p", [E, R, Data]), false end. @@ -110,7 +110,7 @@ is_base64(Data) -> base64:decode(Data), true catch E:R -> - roster:info(?MODULE, "~p:~pBase64DecodingError! ~p", [E, R, Data]), + ?LOG_INFO("~p:~pBase64DecodingError! ~p", [E, R, Data]), false end. @@ -141,4 +141,4 @@ get_client_phone_id(Req) -> end; _ -> throw(authentication_error) - end. \ No newline at end of file + end. diff --git a/apps/roster/src/rest/helpers/rest_response_helper.erl b/apps/roster/src/rest/helpers/rest_response_helper.erl index 20a3365e32df238a176971b293c542ae210562ab..f0019431968a559140dbe88aca16d3414907ac1c 100644 --- a/apps/roster/src/rest/helpers/rest_response_helper.erl +++ b/apps/roster/src/rest/helpers/rest_response_helper.erl @@ -1,6 +1,6 @@ -module(rest_response_helper). +-include_lib("kernel/include/logger.hrl"). -include_lib("roster/include/static/rest_text.hrl"). --include_lib("roster/include/static/rest_var.hrl"). -export([ description/0, @@ -12,6 +12,13 @@ success_200/0 ]). +-define(HTTP_CODE_200, 200). +-define(HTTP_CODE_400, 400). +-define(HTTP_CODE_401, 401). +-define(HTTP_CODE_403, 403). +-define(HTTP_CODE_404, 404). +-define(HTTP_CODE_405, 405). + description() -> "Rest Responses Helper". response(Req, Status, Body) -> @@ -58,16 +65,16 @@ error_response_api(Req, Status, Message) -> 'POST' -> Req:parse_post(); _ -> [] end, - roster:info(?MODULE, "NotFound:~p:~p:~p, Error: ~p", [Req:get(method), Req:get(path), Params, Message]), + ?LOG_INFO("NotFound:~p:~p:~p, Error: ~p", [Req:get(method), Req:get(path), Params, Message]), Body = remove_symbol_screening(jsx:encode([{<<"message">>, nitro:to_binary(Message)}])), Req:respond({Status, [{"Content-Type", "application/json"}], Body}). error_400_response(Req) -> - roster:info(?MODULE, "BadRequest:~p:~p:~p", [Req:get(method), Req:get(path), Req:parse_post()]), + ?LOG_INFO("BadRequest:~p:~p:~p", [Req:get(method), Req:get(path), Req:parse_post()]), response(Req, ?HTTP_CODE_400, rest_response_helper:error_400()). error_405_response(Req) -> - roster:info(?MODULE, "MethodNotAllowed:~p:~p", [Req:get(method), Req:get(path)]), + ?LOG_INFO("MethodNotAllowed:~p:~p", [Req:get(method), Req:get(path)]), response(Req, ?HTTP_CODE_405, rest_response_helper:error_405()). %% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -108,4 +115,4 @@ remove_symbol_screening(Data) -> "\"}{\"", "\"},{\"", [{return, list}, global]), "\"}]\"", "\"}]", [{return, list}, global]), "]\"}}", "]}}", [{return, list}, global]) - ). \ No newline at end of file + ). diff --git a/apps/roster/src/rest/rest_chat_csv.erl b/apps/roster/src/rest/rest_chat_csv.erl index c49a7ac09a7cc3816aa76da72b237ded6efaac2e..85dbd194c936c34862d47f444a8d8e0b09026f96 100644 --- a/apps/roster/src/rest/rest_chat_csv.erl +++ b/apps/roster/src/rest/rest_chat_csv.erl @@ -9,7 +9,6 @@ %%%----------------------------------------------------------------------------- -module(rest_chat_csv). -include("roster.hrl"). --include_lib("roster/include/static/rest_var.hrl"). -include_lib("kvs/include/metainfo.hrl"). -define(MIN_DATE, 0). @@ -21,23 +20,22 @@ -define(EVERYONE, [-1]). -export([ - handle_request/3 + get_user_chat_csv/3, + get_room_chat_csv/3 ]). %% Custom data types -type type_chat_element() :: {MsgDate::integer(), Username::string(), Message::string()}. -type type_chat() :: [type_chat_element()]. --type type_chat_history() :: list(#'Message'{}). -type type_user_data() :: {binary(), string()}. %%%============================================================================= %%% API %%%============================================================================= -handle_request('GET', #{user := ToUserId}, Req) -> - {FromDate, ToDate, TimeZone, AllowedMessageTypes} = parse_get(Req:parse_qs()), + +get_user_chat_csv(QSList, FromPhoneId, ToPhoneId) -> + {FromDate, ToDate, TimeZone, AllowedMessageTypes} = parse_get(QSList), try - ToPhoneId = list_to_binary(ToUserId), - FromPhoneId = rest_main_helper:get_client_phone_id(Req), ChatHistory = get_chat_history({p2p, FromPhoneId, ToPhoneId}), FromUsername = get_username_by_id(extract_user_id(FromPhoneId)), ToUsername = get_username_by_id(extract_user_id(ToPhoneId)), @@ -45,55 +43,45 @@ handle_request('GET', #{user := ToUserId}, Req) -> ChatData = extract_chat_data(ChatHistory, Members, FromDate, ToDate, AllowedMessageTypes, FromPhoneId), CsvText = conversation_to_csv(ChatData, ToUsername, FromDate, ToDate, TimeZone, AllowedMessageTypes), Filename = ["NynjaPrivateChat-", FromUsername, "-", ToUsername, ?CSV_FILE_EXTENSION], - send_response(CsvText, Filename, Req) + {ok, CsvText, Filename} catch - throw:{no_chat_history, {p2p, User1, User2}} -> - rest_response_helper:error_response_api(Req, ?HTTP_CODE_404, io_lib:format("No chat history for: ~s/~s", [User1, User2])); - throw:{no_chat_history, get_chat_time_span} -> - rest_response_helper:error_response_api(Req, ?HTTP_CODE_404, "No chat history for specified period" ); + throw:{no_chat_history, {p2p, User1, User2}} -> + Msg = io_lib:format("No chat history for: ~s/~s", [User1, User2]), + {error, Msg}; + throw:{no_chat_history, get_chat_time_span} -> + {error, "No chat history for specified period"}; throw:{unknown_timezone, TZ} -> - rest_response_helper:error_response_api(Req, ?HTTP_CODE_404, io_lib:format("Unknown timezone: ~s", [TZ])); + {error, io_lib:format("Unknown timezone: ~s", [TZ])}; _:Err -> - roster:info(?MODULE, "~p:~p -UNEXPECTED_ERROR-: ~p:~p~n", [Req:get(method), Req:get(path), Err, erlang:get_stacktrace()]), - rest_response_helper:error_response_api(Req, ?HTTP_CODE_404, "Unexpected error") - end; + {unexpected_error, Err} + end. -handle_request('GET', #{room := GroupChatId}, Req) -> - {FromDate, ToDate, TimeZone, AllowedMessageTypes} = parse_get(Req:parse_qs()), - RoomId = list_to_binary(GroupChatId), - try +get_room_chat_csv(QSList, FromPhoneID, RoomId) -> + {FromDate, ToDate, TimeZone, AllowedMessageTypes} = parse_get(QSList), + try ChatHistory = get_chat_history({muc, RoomId}), Members = get_group_chat_members(RoomId), GroupChatName = get_group_chat_name(RoomId), - #'Member'{id = MemberId} = roster:muc_member(rest_main_helper:get_client_phone_id(Req), RoomId), + #'Member'{id = MemberId} = roster:muc_member(FromPhoneID, RoomId), ChatData = extract_chat_data(ChatHistory, Members, FromDate, ToDate, AllowedMessageTypes, MemberId), CsvText = conversation_to_csv(ChatData, GroupChatName, FromDate, ToDate, TimeZone, AllowedMessageTypes), Filename = ["NynjaGroupChat-", GroupChatName, ?CSV_FILE_EXTENSION], - send_response(CsvText, Filename, Req) - catch - throw:{no_chat_history, {muc, RoomId}} -> - rest_response_helper:error_response_api(Req, ?HTTP_CODE_404, io_lib:format("No chat history for: ~s", [RoomId])); - throw:{no_chat_history, get_chat_time_span} -> - rest_response_helper:error_response_api(Req, ?HTTP_CODE_404, "No chat history for specified period" ); + {ok, CsvText, Filename} + catch + throw:{no_chat_history, {muc, RoomId}} -> + {error, io_lib:format("No chat history for: ~s", [RoomId])}; + throw:{no_chat_history, get_chat_time_span} -> + {error, "No chat history for specified period"}; throw:{unknown_timezone, TZ} -> - rest_response_helper:error_response_api(Req, ?HTTP_CODE_404, io_lib:format("Unknown timezone: ~s", [TZ])); + {error, io_lib:format("Unknown timezone: ~s", [TZ])}; _:Err -> - roster:info(?MODULE, "~p:~p -UNEXPECTED_ERROR-: ~p:~p~n", [Req:get(method), Req:get(path), Err, erlang:get_stacktrace()]), - rest_response_helper:error_response_api(Req, ?HTTP_CODE_404, "Unexpected error") - end; - -handle_request(_, _, Req) -> - rest_response_helper:error_response_api(Req, ?HTTP_CODE_405, "Method not allowed"). + {unexpected_error, Err} + end. %%%============================================================================= %%% Internal functions %%%============================================================================= -send_response(CsvText, Filename, Req) -> - {ResponseStatus, ResponseData} = {?HTTP_CODE_200, CsvText}, - ResponseHeader = [{"Content-Type", "text/csv"}, {"Content-Disposition", "attachment; filename=" ++ Filename}], - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - Req:respond({ResponseStatus, ResponseHeader, ResponseData}). parse_get(ReqParams) -> ReqParams2 = proplists:delete("saveAsLink", ReqParams), @@ -203,7 +191,7 @@ get_chat_history(Feed) -> end. -spec get_chat_time_span(type_chat_element(), integer(), integer()) -> {integer(), integer()}. -get_chat_time_span([], FromDate, ToDate) -> throw({no_chat_history, get_chat_time_span}); +get_chat_time_span([],_FromDate,_ToDate) -> throw({no_chat_history, get_chat_time_span}); get_chat_time_span(Chat, FromDate, ToDate) -> From = case FromDate of @@ -236,4 +224,4 @@ format_timezone({{_, _, _}, {_, _, _}} = Date, TimeZone) -> {error, unknown_tz} -> throw({unknown_timezone, TimeZone}); LocalDate -> LocalDate end; -format_timezone(Timestamp, _) -> Timestamp. \ No newline at end of file +format_timezone(Timestamp, _) -> Timestamp. diff --git a/apps/roster/src/rest/rest_cowboy_chat_handler.erl b/apps/roster/src/rest/rest_cowboy_chat_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..daa4b843b9fc083eb809d68d397be8b1f78911fa --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_chat_handler.erl @@ -0,0 +1,144 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /chat/:roster_uuid/message endpoint +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_chat_handler). +-include_lib("kernel/include/logger.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_provided/2 + , init/2 + , is_authorized/2 + , malformed_request/2 + ]). + +%% Custom callbacks +-export([ to_json/2 + ]). + + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := chat} = State) -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, State) -> + {[<<"GET">>], Req, State}. + +-define(bad_query_string, + <<"Bad query params. Please, add it in format:" + " sender=...,sender_type=...,text=...">>). + +malformed_request(Req, State) -> + try cowboy_req:match_qs([sender, sender_type, text], Req) of + QMap -> + {false, Req, State#{qmap => QMap}} + catch + _:_ -> + Req1 = set_error_resp(<<"error">>, + <<"Missing query parameters">>, Req), + {true, Req1, State} + end. + +content_types_provided(Req, State) -> + {[{<<"application/json">>, to_json} + ], + Req, State}. + +%% TODO: GET as state altering operation is not best practice. +to_json(Req, #{qmap := QMap} = State) -> + ReceiverRosterUUID = cowboy_req:binding(roster_uuid, Req), + try {get_user(ReceiverRosterUUID), get_user(maps:get(sender, QMap))} of + {ReceiverPhoneId, SenderPhoneId} -> + State1 = State#{ sender_phone => SenderPhoneId + , receiver_phone => ReceiverPhoneId + }, + to_json1(Req, State1) + catch + throw:{get_user_error, Reason} -> + Status = <<"error">>, + Resp = rest_response_helper:error_response(Status, Reason), + Req1 = cowboy_req:reply(400, #{}, Resp, Req), + {stop, Req1, State} + end. + + +to_json1(Req, #{ sender_phone := SenderPhoneId + , receiver_phone := ReceiverPhoneId + , qmap := #{ sender_type := SenderType + , text := Text }} = State) -> + Receiver = cowboy_req:binding(roster_uuid, Req), + case rest_gw:add_as_friend(SenderType, SenderPhoneId, ReceiverPhoneId) of + {error, What} -> + {Status, Msg} = add_as_friend_error(What, Receiver), + Resp = error_resp(Status, Msg), + {stop, cowboy_req:reply(400, #{}, Resp, Req), State}; + friend_request -> + Status = <<"ok_request_friend">>, + Msg = <<"Friend request send to: ", Receiver/binary>>, + Resp = ok_resp(Status, Msg), + {Resp, Req, State}; + ok -> + case rest_gw:send_p2p_message(SenderPhoneId, ReceiverPhoneId, Text) of + ok -> + Status = <<"ok_message_sent">>, + Msg = <<"Message send to: ", Receiver/binary>>, + Resp = ok_resp(Status, Msg), + {Resp, Req, State}; + {error, send_message} -> + Status = <<"error_sending_message">>, + Msg = <<"Error sending Message to: ", Receiver/binary>>, + Resp = error_resp(Status, Msg), + {stop, cowboy_req:reply(400, #{}, Resp, Req), State} + end + end. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +ok_resp(Status, Reason) -> + rest_response_helper:success_response(Status, Reason). + +error_resp(Status, Reason) -> + rest_response_helper:error_response(Status, Reason). + +set_error_resp(Status, Reason, Req) -> + Resp = rest_response_helper:error_response(Status, Reason), + cowboy_req:set_resp_body(Resp, Req). + +get_user(RosterUUID) -> + try uuid:is_valid(binary_to_list(RosterUUID)) of + true -> + case micro:uuid_to_id('LinkRoster', RosterUUID) of + [] -> throw({get_user_error, <<"User not found: ", RosterUUID/binary>>}); + RosterId -> roster:phone_id(RosterId) + end; + false -> + throw({get_user_error, <<"User not found: ", RosterUUID/binary>>}) + catch _:_ -> + throw({get_user_error, <<"Bad User UUID: ", RosterUUID/binary>>}) + end. + +add_as_friend_error(request_not_accepted, Receiver) -> + {<<"error_request_not_accepted">>, + <<"Friend request not accepted by: ", Receiver/binary>>}; +add_as_friend_error(send_friend_request, Receiver) -> + {<<"error_send_friend_request">>, + <<"Error Request Friend/request with: ", Receiver/binary>>}; +add_as_friend_error(confirm_friend_request, Receiver) -> + {<<"error_confirm_friend_request">>, + <<"Error Request Friends/confirm with: ", Receiver/binary>>}; +add_as_friend_error(unexpected_error, Receiver) -> + {<<"error_unexpected">>, + <<"Unexpected Error with: ", Receiver/binary>>}. + diff --git a/apps/roster/src/rest/rest_cowboy_cri_handler.erl b/apps/roster/src/rest/rest_cowboy_cri_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..ef02dfaebac22488ee3c2f69dd3c7db18ad1b434 --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_cri_handler.erl @@ -0,0 +1,374 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /cri/rooms +%%% /cri/rooms/members +%%% /cri/rooms/type +%%% /cri/bubbles +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_cri_handler). + +-include_lib("kernel/include/logger.hrl"). +-include("roster.hrl"). +-include("rest_static.hrl"). +-include_lib("roster/include/static/rest_text.hrl"). +-include_lib("roster/include/static/roster_text.hrl"). +-include_lib("roster/include/static/roster_var.hrl"). +-include_lib("roster/include/static/main_var.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_accepted/2 + , content_types_provided/2 + , delete_resource/2 + , init/2 + , is_authorized/2 + , forbidden/2 + , malformed_request/2 + ]). + +%% Custom callbacks +-export([ bubble_from_json/2 + , new_room_from_json/2 + , new_room_member_from_json/2 + , room_type_to_json/2 + , room_members_to_json/2 + ]). + +-define(cri_room, cri_room). +-define(cri_room_members, cri_room_members). +-define(cri_room_type, cri_room_type). +-define(cri_bubble, cri_bubble). + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := Endpoint} = State) when Endpoint =:= ?cri_room; + Endpoint =:= ?cri_room_members; + Endpoint =:= ?cri_room_type; + Endpoint =:= ?cri_bubble -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + case rest_cowboy_handler:is_authorized(Req, State) of + {{false,_} ,_Req1,_State1} = Ret -> + Ret; + {true, Req1, State1} -> + %% Perform rest of initialization started in + %% malformed_request + try + _ = check_profile(State1), + {true, Req1, init_room(State1)} + catch + throw:{init_error, What} -> + Resp = rest_response_helper:error_response(400, What), + {stop, cowboy_req:reply(400, #{}, Resp, Req1), State1} + end + end. + +allowed_methods(Req, #{endpoint := Endpoint} = State) -> + case Endpoint of + ?cri_room -> {[<<"POST">>, <<"DELETE">>], Req, State}; + ?cri_room_members -> {[<<"GET">>, <<"POST">>, <<"DELETE">>], Req, State}; + ?cri_room_type -> {[<<"GET">>], Req, State}; + ?cri_bubble -> {[<<"POST">>], Req, State} + end. + +%%%=================================================================== +%%% Malformed request + +malformed_request(Req, State) -> + try + State1 = init_phone_header(Req, State), + State2 = init_required_qs_params(Req, State1), + {false, Req, State2} + catch + throw:{init_error, What} -> + {true, set_error_resp(What, Req), State} + end. + +init_phone_header(Req, State) -> + case cowboy_req:header(<<"phoneid">>, Req, undefined) of + undefined -> throw({init_error, ?ERROR_MISSING_HEADER}); + PhoneHeader -> State#{phone_id => rest_main_helper:get_phone_number(PhoneHeader)} + end. + +%% NOTE: Exposes information about DB, so it must be handled after authorization +check_profile(#{ phone_id := PhoneId}) -> + case rest_main_helper:get_profile_by_phone(PhoneId) of + {error, _} -> throw({init_error, ""}); %% TODO: Mimicking existing response. + {ok, _} -> ok + end. + +init_required_qs_params(Req, #{endpoint := Endpoint} = State) -> + Method = cowboy_req:method(Req), + Required = required_qs_param(Endpoint, Method), + try cowboy_req:match_qs(Required, Req) of + #{} = QMap -> maps:merge(State, QMap) + catch _:_ -> throw({init_error, ?ERROR_MISSING_PARAM}) + end. + +%% NOTE: Exposes information about DB, so it must be handled after authorization +init_room(State) -> + case maps:get(room_id, State, undefined) of + undefined -> + State; + RoomId -> + case kvs:get('Room', RoomId) of + {ok, #'Room'{status = Status} = Room} when Status /= delete -> + State#{room_object => Room, room_id => RoomId}; + _ -> + throw({init_error, ?ERROR_ROOM_NOT_FOUND}) + end + end. + +%%%=================================================================== +%%% Forbidden + +forbidden(#{method := <<"DELETE">>} = Req, + #{endpoint := Endpoint} = State) when Endpoint =:= ?cri_room_members; + Endpoint =:= ?cri_room -> + case is_call_room(State) andalso is_admin_user(State) of + true -> {false, Req, State}; + false -> {true, set_error_resp(403, ?ERROR_PERMISSION_DENIED, Req), State} + end; +forbidden(Req, State) -> + {false, Req, State}. + +is_call_room(#{room_object := #'Room'{type = ?CALL_ROOM}}) -> + true; +is_call_room(#{}) -> + false. + +is_admin_user(#{ phone_id := PhoneId, room_id := RoomId }) -> + case roster:muc_member(PhoneId, RoomId) of + #'Member'{status = admin} -> true; + _ -> false + end. + +%%%=================================================================== + +content_types_provided(Req, #{endpoint := Endpoint} = State) -> + {[{<<"application/json">>, endpoint_GET_callback(Endpoint)}], + Req, State}. + +endpoint_GET_callback(?cri_room_type) -> room_type_to_json; +endpoint_GET_callback(?cri_room_members) -> room_members_to_json; +endpoint_GET_callback(_) -> should_not_be_used. + +%%%=================================================================== + +content_types_accepted(Req, #{endpoint := Endpoint} = State) -> + {[{<<"application/json">>, endpoint_POST_callback(Endpoint)}], + Req, State}. + +endpoint_POST_callback(?cri_room) -> new_room_from_json; +endpoint_POST_callback(?cri_room_members) -> new_room_member_from_json; +endpoint_POST_callback(?cri_bubble) -> bubble_from_json. + +%%%=================================================================== + +delete_resource(Req, #{endpoint := ?cri_room, room_id := RoomId} = State) -> + rest_main_helper:send_to_mqtt(#'Room'{id = RoomId, + status = ?DELETE_STATUS, + type = ?CALL_ROOM}), + Json = rest_response_helper:success_response(), + {true, cowboy_req:set_resp_body(Json, Req), State}; +delete_resource(Req, #{endpoint := ?cri_room_members, + room_id := RoomId, + phone_id := AdminPhoneId, + phone_ids := PhoneIdsRaw} = State) -> + {ok, AdminMember} = roster:to_member(AdminPhoneId), + PhoneIdList = re:split(PhoneIdsRaw, ","), + Remove = #'Room'{id = RoomId, + status = ?REMOVE_STATUS, + admins = [AdminMember]}, + {ResponseStatus, ResponseData} = rest_cri:request_with_failed_status(PhoneIdList, Remove), + Req1 = cowboy_req:set_resp_body(ResponseData, Req), + case ResponseStatus of + ok -> + {false, Req1, State}; + error -> + {true, Req1, State} + end. + +%%%=================================================================== + +room_type_to_json(Req, #{room_object := Room} = State) -> + #'Room'{type = Type} = Room, + Json = success_json(jsx_tuples_to_json([{?ROOM_TYPE_VALUE, Type}])), + {Json, Req, State}. + +room_members_to_json(Req, State) -> + #{phone_ids := PhoneIdListRaw, room_id := RoomId} = State, + PhoneIdList = re:split(PhoneIdListRaw, ","), + Json = success_json([jsx_tuples_to_json(member_tuples(PhoneId, RoomId)) + || PhoneId <- PhoneIdList]), + {Json, Req, State}. + +member_tuples(PhoneId, RoomId) -> + case roster:muc_member(nitro:to_binary(PhoneId), RoomId) of + #'Member'{} -> + [{<<"phone_id">>, PhoneId}, + {<<"is_member">>, true}]; + [] -> + PhoneNumber = rest_main_helper:get_phone_number(PhoneId), + case kvs:get('Profile', nitro:to_binary(PhoneNumber)) of + {error, _} -> + [{<<"phone_id">>, PhoneId}, + {<<"is_member">>, false}, + {error, ?ERROR_USER_404}]; + _ -> + [{<<"phone_id">>, PhoneId}, + {<<"is_member">>, false}] + end + end. + +%%%=================================================================== + +new_room_from_json(Req, #{phone_id := PhoneID} = State) -> + RoomId = new_room_id(), + {ok, #'Member'{alias = AdminNick, names = AdminName} = AdminMember} = + roster:to_member(nitro:to_binary(PhoneID)), + Create = #'Room'{id = RoomId, + status = ?CREATE_STATUS, + type = ?CALL_ROOM, + name = new_room_name(AdminNick, AdminName), + admins = [AdminMember]}, + rest_main_helper:send_to_mqtt(sync, Create), + Json = success_json(jsx_tuples_to_json([{<<"room_id">>, RoomId}])), + {true, cowboy_req:set_resp_body(Json, Req), State}. + +new_room_id() -> + TS = nitro:to_binary(roster:now_msec()), + NextId = nitro:to_binary(kvs:next_id('Room', 1)), + iolist_to_binary(["conference_", TS, "_", NextId]). + +new_room_name(AdminNick, AdminName) -> + case AdminNick =:= [] of + true -> iolist_to_binary(["Call by @", AdminName]); + false -> iolist_to_binary(["Call by @", AdminNick]) + end. + +%%%=================================================================== + +new_room_member_from_json(Req, #{phone_id := AdminPhoneId} = State) -> + {ok, Body, Req1} = cowboy_req:read_body(Req), + case parse_new_room_members_body(Body) of + {error, What} -> + {false, set_error_resp(What, Req1), State}; + {ok, #{ room_id := RoomId + , join := true}} -> + {ok, JoinMember} = roster:to_member(AdminPhoneId), + JoinReq = #'Room'{id = RoomId, + status = ?JOIN_STATUS, + type = ?CALL_ROOM, + members = [JoinMember]}, + rest_main_helper:send_to_mqtt(JoinReq), + Json = rest_response_helper:success_response(), + {true, cowboy_req:set_resp_body(Json, Req1), State}; + {ok, #{ room_id := RoomId + , join := true + , phone_ids := PhoneIDs}} -> + case roster:muc_member(AdminPhoneId, RoomId) of + #'Member'{status = admin} = AdminMember -> + PhoneIDList = re:split(PhoneIDs, ","), + Room = #'Room'{id = RoomId, status = ?ADD_STATUS, admins = [AdminMember]}, + case rest_cri:request_with_failed_status(PhoneIDList, Room) of + {ok, Json} -> + Req2 = cowboy_req:set_resp_body(Json, Req1), + {true, Req2, State}; + {error, Json} -> + Req2 = cowboy_req:set_resp_body(Json, Req1), + {false, Req2, State} + end; + _ -> + %% Not an admin + Status = 403, + Error = ?ERROR_PERMISSION_DENIED, + Json = rest_response_helper:error_response(Status, Error), + {stop, cowboy_req:reply(403, #{}, Json, Req1), State} + end + end. + +parse_new_room_members_body(Body) -> + %% room_id, join - always required + %% if join == false - phone_ids required + case rest_main_helper:get_body_value(Body, <<"room_id">>) of + {ok, RoomId} -> + case rest_main_helper:get_body_value(Body, <<"join">>) of + {ok, <<"true">>} -> + {ok , #{ room_id => RoomId + , join => true + }}; + {ok, <<"false">>} -> + case rest_main_helper:get_body_value(Body, <<"phone_id">>) of + {ok, PhoneIds} -> + #{ room_id => RoomId + , join => false + , phone_ids => PhoneIds + }; + {error, _} = Err -> + Err + end; + {error, _} = Err -> + Err + end; + {error, _} = Err -> + Err + end. + +%%%=================================================================== + +bubble_from_json(Req, #{phone_id := FromPhoneId} = State) -> + {ok, Body, Req1} = cowboy_req:read_body(Req), + CallBubbleJSON = 'CallBubbleJSONRest':from_json(jsx:decode(Body), #'CallBubbleJSON'{}), + case rest_cri:validate_bubble_data(CallBubbleJSON) of + error -> + Resp = rest_response_helper:error_response(), + {false, cowboy_req:set_resp_body(Resp, Req), State}; + ok -> + case rest_cri:validate_permission(FromPhoneId, CallBubbleJSON) of + error -> + Status = 403, + Error = ?ERROR_PERMISSION_DENIED, + Json = rest_response_helper:error_response(Status, Error), + {stop, cowboy_req:reply(403, #{}, Json, Req1), State}; + ok -> + Msg = rest_cri:create_message(FromPhoneId, CallBubbleJSON), + rest_main_helper:send_to_mqtt(Msg), + MsgBin = binary_to_list(term_to_binary(Msg)), + Resp = rest_response_helper:success_response(MsgBin), + {true, cowboy_req:set_resp_body(Resp, Req1), State} + end + end. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +set_error_resp(Status, What, Req) -> + Resp = rest_response_helper:error_response(Status, What), + cowboy_req:set_resp_body(Resp, Req). + +set_error_resp(What, Req) -> + Resp = rest_response_helper:error_response(What), + cowboy_req:set_resp_body(Resp, Req). + +success_json(Data) -> + rest_response_helper:success_response(Data). + +jsx_tuples_to_json(Data) -> + Fun = fun({K, V}) -> {nitro:to_binary(K), nitro:to_binary(V)}end, + jsx:encode(lists:map(Fun, Data)). + +required_qs_param(?cri_room_type , <<"GET">> ) -> [room_id]; +required_qs_param(?cri_room , <<"DELETE">>) -> [room_id]; +required_qs_param(?cri_room_members , <<"GET">> ) -> [room_id, phone_ids]; +required_qs_param(?cri_room_members , <<"DELETE">>) -> [room_id, phone_ids]; +required_qs_param(_Endpoint , _Method ) -> []. + + diff --git a/apps/roster/src/rest/rest_cowboy_csv_handler.erl b/apps/roster/src/rest/rest_cowboy_csv_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..1e1a72265851b8703eb9ff49ee6fe74708d8d1fb --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_csv_handler.erl @@ -0,0 +1,87 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /api/v1/chat/messages/csv +%%% /api/v1/groups/messages/csv +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_csv_handler). + +-include_lib("kernel/include/logger.hrl"). +-include("roster.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_provided/2 + , init/2 + , is_authorized/2 + ]). + +%% Custom callbacks +-export([ chat_to_csv/2 + , group_chat_to_csv/2 + ]). + +-define(groups_csv, groups_csv). +-define(chats_csv, chats_csv). + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := Endpoint} = State) when Endpoint =:= ?groups_csv; + Endpoint =:= ?chats_csv -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, State) -> + {[<<"GET">>], Req, State}. + +%%%=================================================================== + +content_types_provided(Req, #{endpoint := Endpoint} = State) -> + {[{<<"text/csv">>, endpoint_GET_callback(Endpoint)}], + Req, State}. + +endpoint_GET_callback(?groups_csv) -> group_chat_to_csv; +endpoint_GET_callback(?chats_csv) -> chat_to_csv. + +group_chat_to_csv(Req, #{token_phone_id := FromPhoneID} = State) -> + QSList0 = cowboy_req:parse_qs(Req), + RoomID = cowboy_req:binding(room_id, Req), + %% For now, the old handler code needs the content as strings. + QSList = [{binary_to_list(K), binary_to_list(V)} || {K, V} <- QSList0], + case rest_chat_csv:get_room_chat_csv(QSList, FromPhoneID, RoomID) of + {ok, CSVFile, Filename} -> + Req1 = cowboy_req:set_resp_header(<<"Content-Disposition">>, + "attachment; filename=" ++ Filename), + {CSVFile, Req1, State}; + {error, Msg} -> + respond_error(iolist_to_binary(Msg), Req, State) + end. + +chat_to_csv(Req, #{token_phone_id := FromPhoneID} = State) -> + QSList0 = cowboy_req:parse_qs(Req), + ToPhoneID = cowboy_req:binding(phone_id, Req), + %% For now, the old handler code needs the content as strings. + QSList = [{binary_to_list(K), binary_to_list(V)} || {K, V} <- QSList0], + case rest_chat_csv:get_user_chat_csv(QSList, FromPhoneID, ToPhoneID) of + {ok, CSVFile, Filename} -> + Req1 = cowboy_req:set_resp_header(<<"Content-Disposition">>, + "attachment; filename=" ++ Filename), + {CSVFile, Req1, State}; + {error, Msg} -> + respond_error(iolist_to_binary(Msg), Req, State) + end. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +respond_error(What, Req, State) when is_binary(What) -> + Msg = jsx:encode([{<<"message">>, What}]), + Headers = #{<<"Content-Type">> => <<"application/json">>}, + {stop, cowboy_req:reply(404, Headers, Msg, Req), State}. diff --git a/apps/roster/src/rest/rest_cowboy_fake_numbers_handler.erl b/apps/roster/src/rest/rest_cowboy_fake_numbers_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..7e3e8591f72707b92e8795be055b94edc8ab1370 --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_fake_numbers_handler.erl @@ -0,0 +1,128 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /fake_numbers and /fn (alias) endpoints +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_fake_numbers_handler). +-include_lib("kernel/include/logger.hrl"). +-include("roster.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_accepted/2 + , content_types_provided/2 + , delete_resource/2 + , init/2 + , is_authorized/2 + , malformed_request/2 + , resource_exists/2 + ]). + +%% Custom callbacks +-export([ to_json/2 + , to_html/2 + , from_json/2 + ]). + + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := Endpoint} = State) when Endpoint =:= fn; + Endpoint =:= fake_numbers -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +resource_exists(Req, State) -> + {application:get_env(roster, fake_numbers_check, false), Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, #{endpoint := fake_numbers} = State) -> + {[<<"GET">>], Req, State}; +allowed_methods(Req, #{endpoint := fn} = State) -> + {[<<"GET">>, <<"PUT">>, <<"DELETE">>], Req, State}. + +-define(phone_not_found, <<"Cannot find phones in query params. Please, add " + "it in format: phone=PhoneNumber1,PhoneNumber2">>). +-define(phone_not_found_body, + <<"Empty Body section. Please, add numbers in format: {\"phone\": " + "[PhoneNumber1,PhoneNumber2]}">>). + +malformed_request(#{method := <<"GET">>} = Req, State) -> + {false, Req, State}; +malformed_request(#{method := <<"DELETE">>} = Req, State) -> + QSList = cowboy_req:parse_qs(Req), + case proplists:get_value(<<"phone">>, QSList) of + undefined -> + {true, set_error_resp(?phone_not_found, Req), State}; + Phone -> + {false, Req, State#{phone => Phone}} + end; +malformed_request(#{method := <<"PUT">>} = Req, State) -> + %% Nothing to check here outside the body, which shouldn't be read here. + {false, Req, State}. + +set_error_resp(Reason, Req) -> + Resp = jsx:encode([{<<"Status">>, <<"Error">>}, + {<<"Reason">>, Reason}]), + cowboy_req:set_resp_body(Resp, Req). + +content_types_provided(Req, #{endpoint := fake_numbers} = State) -> + {[{<<"text/html">>, to_html} + ], + Req, State}; +content_types_provided(Req, #{endpoint := fn} = State) -> + {[{<<"application/json">>, to_json} + ], + Req, State}. + +content_types_accepted(Req, State) -> + {[{<<"application/json">>, from_json} + ], + Req, State}. + +delete_resource(Req, #{phone := Phone} = State) -> + lists:foreach(fun(P) -> + kvs:delete('FakeNumbers', P) + end, re:split(Phone, ",")), + Resp = jsx:encode([{<<"Status">>, <<"Success">>}]), + Req1 = cowboy_req:set_resp_body(Resp, Req), + {true, Req1, State}. + +to_html(#{method := <<"GET">>} = Req, #{endpoint := fake_numbers} = State) -> + {ok, HTMLOutput} = phone_numbers_tpl:render([{title, <<"Fake Numbers">>}]), + {HTMLOutput, Req, State}. + +to_json(#{method := <<"GET">>} = Req, #{endpoint := fn} = State) -> + Data = [#{<<"phone">> => binary_to_integer(P), + <<"created">> => list_to_binary(roster:timestamp_to_datetime((T)))} + || #'FakeNumbers'{created = T, phone = P} + <- kvs:all('FakeNumbers')], + Json = jsx:encode(#{<<"Status">> => <<"Success">>, + <<"Data">> => Data}), + {Json, Req, State}. + +from_json(#{method := <<"PUT">>} = Req, #{endpoint := fn} = State) -> + case cowboy_req:read_body(Req) of + {ok, <<>>, Req1} -> + {false, set_error_resp(?phone_not_found_body, Req1), State}; + {ok, Data, Req1} -> + try jsx:decode(Data, [return_maps]) of + #{<<"phone">> := [_|_] = Phones} -> + lists:foreach(fun add_fake_number/1, Phones), + Res = jsx:encode([{<<"Status">>, <<"Success">>}]), + Req2 = cowboy_req:set_resp_body(Res, Req1), + {true, Req2, State}; + #{} -> + {false, set_error_resp(?phone_not_found_body, Req1), State} + catch _:_ -> + {false, set_error_resp(?phone_not_found_body, Req1), State} + end + end. + +add_fake_number(P) -> + roster:add_fake_number(nitro:to_binary(P)). diff --git a/apps/roster/src/rest/rest_cowboy_handler.erl b/apps/roster/src/rest/rest_cowboy_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..3abd803047ef3af79e73fdbef5cbe40a718a24cb --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_handler.erl @@ -0,0 +1,176 @@ +%%%------------------------------------------------------------------- +%%% @doc Handle the rest interface using cowboy +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_handler). + +-include_lib("kernel/include/logger.hrl"). +-include("roster.hrl"). + + +-export([ start/0 + , compile_template_files/0 + , compile_template_files/1 + , compile_template_files/2 + , is_authorized/2 + ]). +%%%=================================================================== +%%% API +%%%=================================================================== + +start() -> + start(application:get_env(rest, start_cowboy, false)). + +start(false) -> + ?LOG_INFO("Cowboy REST handler NOT started", []); +start(true) -> + ok = compile_template_files(), + Port = application:get_env(rest, port, 8888), + Opts = [ {port, Port} + ], + HostName = '_', + Env = #{env => #{dispatch => cowboy_router:compile([{HostName, routes()}])}}, + {ok, _} = cowboy:start_clear(roster_cowboy_http, Opts, Env), + ?LOG_INFO("Cowboy REST handler started on port: ~p", [Port]), + ok. + +routes() -> + [ {"/assets/[...]", cowboy_static, {dir, static_assets_dir()}} + , {"/chat/:roster_uuid/message", rest_cowboy_chat_handler, basic_state(chat)} + , {"/sessions", rest_cowboy_session_handler, basic_state(sessions)} + , {"/whitelist", rest_cowboy_whitelist_handler, basic_state(whitelist)} + , {"/admin_whitelist", rest_cowboy_whitelist_handler, basic_state(admin_whitelist)} + , {"/fake_numbers", rest_cowboy_fake_numbers_handler, basic_state(fake_numbers)} + , {"/fn", rest_cowboy_fake_numbers_handler, basic_state(fn)} + , {"/metrics", rest_cowboy_metric_handler, basic_state(metrics)} + , {"/push/message", rest_cowboy_push_handler, basic_state(push_message)} + , {"/room", rest_cowboy_room_handler, basic_state(room)} + , {"/publish", rest_cowboy_publish_handler, basic_state(publish)} + , {"/users", rest_cowboy_users_handler, basic_state(users)} + , {"/cri/rooms/type", rest_cowboy_cri_handler, basic_state(cri_room_type)} + , {"/cri/rooms", rest_cowboy_cri_handler, basic_state(cri_room)} + , {"/cri/rooms/members", rest_cowboy_cri_handler, basic_state(cri_room_members)} + , {"/cri/bubbles", rest_cowboy_cri_handler, basic_state(cri_bubble)} + , {"/test/", rest_cowboy_test_handler, basic_state(test_api)} + , {"/link/:link_id/room_id", rest_cowboy_link_handler, basic_state(link_room)} + , {"/api/v1/groups/:room_id/messages/csv", + rest_cowboy_csv_handler, token_state(groups_csv)} + , {"/api/v1/chats/:phone_id/messages/csv", + rest_cowboy_csv_handler, token_state(chats_csv)} + , {"/api/v1/groups/:room_id/messages/:message_id/transcribe", + rest_cowboy_transcribe_handler, token_state(groups_transcribe)} + , {"/api/v1/chats/:phone_id/messages/:message_id/transcribe", + rest_cowboy_transcribe_handler, token_state(chats_transcribe)} + ]. + +token_state(Endpoint) -> + #{ auth => token + , endpoint => Endpoint}. + +basic_state(Endpoint) -> + #{ auth => basic + , endpoint => Endpoint}. + +static_assets_dir() -> + filename:join([code:priv_dir(roster), "www", "assets"]). + +%% Compile template (dtl) files +compile_template_files() -> + compile_template_files([]). + +compile_template_files(Opts) -> + Files = filelib:wildcard(filename:join([code:priv_dir(roster), "www", "*.dtl"])), + compile_template_files(Files, Opts). + +compile_template_files([], _Opts) -> + ok; +compile_template_files([File | Files], Opts) -> + Out = re:replace(filename:basename(File), ".dtl", "_tpl", + [global, {return, list}]), + ok = erlydtl:compile(File, Out, Opts), + compile_template_files(Files, Opts). + + +%%%=================================================================== +%%% Authorization + +is_authorized(#{path := Path} = Req, #{auth := AuthType} = State) -> + case lists:member(Path, application:get_env(rest, open_api_list, [])) of + true -> {true, Req, State}; + false -> + case cowboy_req:header(<<"authorization">>, Req) of + undefined when AuthType =:= basic -> + {{false, <<"Basic">>}, Req, State}; + undefined when AuthType =:= token -> + Req1 = cowboy_req:set_resp_body(<<"Token is invalid">>, Req), + {{false, <<"Bearer">>}, Req1, State}; + AuthHeader when AuthType =:= token -> + token_authentication(AuthHeader, Req, State); + AuthHeader when AuthType =:= basic -> + case basic_auth(AuthHeader) of + true -> {true, Req, State}; + false -> {{false, <<"Basic">>}, Req, State} + end + end + end. + +token_authentication(Auth, Req, State) -> + case token_auth_with_phone_id(Auth) of + {ok, PhoneId} -> + {true, Req, State#{token_phone_id => iolist_to_binary(PhoneId)}}; + {error, invalid_token} -> + Req1 = cowboy_req:set_resp_body(<<"Token is invalid">>, Req), + {{false, <<"Bearer">>}, Req1, State}; + {error, token_expired} -> + Req1 = cowboy_req:set_resp_body(<<"Token is expired">>, Req), + {{false, <<"Bearer">>}, Req1, State} + end. + +-define(AUTH_USERNAME, proplists:get_value(username, application:get_env(rest, basic_auth, []))). +-define(AUTH_PASSWORD, proplists:get_value(password, application:get_env(rest, basic_auth, []))). + +basic_auth(<<"Basic", BasicAuth/binary>>) -> + try binary:split(base64:decode(BasicAuth), <<":">>) of + [Username, Password] -> + (iolist_to_binary(?AUTH_USERNAME) =:= Username) + andalso (iolist_to_binary(?AUTH_PASSWORD) =:= Password); + _ -> false + catch + _:_ -> false + end; +basic_auth(Bin) when is_binary(Bin) -> + false. + +token_auth_with_phone_id(<<"Bearer ", BearerAuth/binary>>) -> + try + [AuthToken, ClientId] = re:split(BearerAuth, "/"), + %% Check if AuthToken is JWT + case micro_auth:validate_token(AuthToken) of + {ok, UUID} -> + PhoneId = micro:uuid_to_id('LinkRoster', UUID), + case kvs:get('Auth', ClientId) of + {ok, #'Auth'{user_id = PhoneId}} -> + {ok, PhoneId}; + _ -> + {error, invalid_token} + end; + error -> + %% Seems like token is not JWT + %% Try using the old Auth + case roster:parse_token(AuthToken) of + {error, _} = Err -> + Err; + _ -> + case kvs:get('Auth', ClientId) of + {ok, #'Auth'{token = DBToken}} when DBToken == AuthToken -> ok; + {error, not_found} -> {error, invalid_token} + end + end + end + catch + _:_ -> {error, invalid_token} + end; +token_auth_with_phone_id(Bin) when is_binary(Bin) -> + {error, invalid_token}. + diff --git a/apps/roster/src/rest/rest_cowboy_link_handler.erl b/apps/roster/src/rest/rest_cowboy_link_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..82216edc8d41ac287b1d022a932cfaebcaa2699b --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_link_handler.erl @@ -0,0 +1,66 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /link/:link_id/room_id +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_link_handler). + +-include_lib("kernel/include/logger.hrl"). +-include("roster.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_provided/2 + , init/2 + , is_authorized/2 + , resource_exists/2 + ]). + +%% Custom callbacks +-export([ room_to_json/2 + ]). + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := link_room} = State) -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, #{} = State) -> + {[<<"GET">>], Req, State}. + +content_types_provided(Req, State) -> + {[{<<"application/json">>, room_to_json}], + Req, State}. + +resource_exists(Req, State) -> + LinkId = cowboy_req:binding(link_id, Req), + case kvs:get('Link', LinkId) of + {ok, #'Link'{room_id = RoomId}} -> + case kvs:get('Room', RoomId) of + {ok, #'Room'{name = RoomName}} -> + {true, Req, State#{ room_id => RoomId + , room_name => RoomName}}; + _ -> + Json = jsx:encode([ {room_id, list_to_binary(RoomId)} + , {error, <<"not_found">>}]), + Req1 = cowboy_req:set_resp_body(Json, Req), + {false, Req1, State} + end; + {error, not_found} -> + Json = jsx:encode([ {link_id, LinkId} + , {error, <<"not_found">>}]), + Req1 = cowboy_req:set_resp_body(Json, Req), + {false, Req1, State} + end. + +room_to_json(Req, #{room_id := RoomId, room_name := RoomName} = State) -> + Json = jsx:encode([ { room_id, RoomId} + , {room_name, RoomName}]), + {Json, Req, State}. diff --git a/apps/roster/src/rest/rest_cowboy_metric_handler.erl b/apps/roster/src/rest/rest_cowboy_metric_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..2b88370df1985b9c6604bc8a1d7df3a9784b5fb5 --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_metric_handler.erl @@ -0,0 +1,56 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /metrics endpoint +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_metric_handler). + +-include_lib("kernel/include/logger.hrl"). +-include_lib("roster/include/static/prometheus_var.hrl"). + +%% Cowboy callbacks +-export([ init/2 + , is_authorized/2 + , allowed_methods/2 + , content_types_provided/2 + ]). + +%% Custom callbacks +-export([ to_text/2 + ]). + + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method} = Req, #{endpoint := metrics} = State) -> + ?LOG_INFO("~s:~s", [Method, Path]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, State) -> + {[<<"GET">>], Req, State}. + +content_types_provided(Req, State) -> + {[{<<"text/plain">>, to_text} + ], + Req, State}. + +to_text(Req, State) -> + update_gauges(), + Resp = prometheus_text_format:format(), + {Resp, Req, State}. + +update_gauges() -> + prometheus_gauge:set(?METRIC_CHATS_TOTAL, [?METRIC_LABEL_P2P_CHAT], roster_db:p2p_stats()), + prometheus_gauge:set(?METRIC_CHATS_TOTAL, [?METRIC_LABEL_GROUP_CHAT], roster_db:room_stats()), + [prometheus_gauge:set(?METRIC_USERS_PER_COUNTRY, [Country], Value) || {Country, Value} <- roster_db:country_user_stats()], + %% [prometheus_gauge:set(?METRIC_USERS_PER_COUNTRY_ONLINE, [Country], Value) || {Country, Value} <- roster_db:country_user_stats(online)], + [prometheus_gauge:set(?METRIC_MSGS_BY_TYPE_NMBR, [Mime], Value) || {Mime, Value} <- roster_db:msg_stats()], + [prometheus_gauge:set(?METRIC_SESSIONS_PER_COUNTRY_ONLINE, [Country], Value) || {Country, Value} <- roster_db:country_session_stats(online)], + ok. + + diff --git a/apps/roster/src/rest/rest_cowboy_publish_handler.erl b/apps/roster/src/rest/rest_cowboy_publish_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..a030da52bbd817765841f3c4d5ede4d611cba7ff --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_publish_handler.erl @@ -0,0 +1,65 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /publish +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_publish_handler). +-include_lib("kernel/include/logger.hrl"). +-include("roster.hrl"). +-include_lib("roster/include/static/rest_text.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_accepted/2 + , init/2 + , is_authorized/2 + ]). + +%% Custom callbacks +-export([ from_json/2 + ]). + + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := publish} = State) -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, State) -> + {[<<"POST">>], Req, State}. + +content_types_accepted(Req, State) -> + {[{<<"application/json">>, from_json} + ], + Req, State}. + +from_json(Req, State) -> + {ok, Body, Req1} = cowboy_req:read_body(Req), + try + [throw({validation, ?ERROR_INVALID_JSON}) || not jsx:is_json(Body)], + PS = 'PublishServiceRest':from_json(jsx:decode(Body), #'PublishService'{}), + #'PublishService'{message = Msg, topic = Topic, qos = Qos} = PS, + [throw({validation, ?ERROR_MISSING_PARAM}) || Msg == [] orelse Topic == []], + [throw({validation, ?ERROR_INVALID_QOS_PARAM}) || Qos < 0 orelse Qos > 2], + case rest_main_helper:validate_msg_to_publish(Msg) of + false -> + throw({validation, ?ERROR_INVALID_MESSAGE_PARAM}); + DecodedMsg -> + Pid = rest_main_helper:rest_pid(), + MsgBin = term_to_binary(DecodedMsg), + n2o_vnode:send(Pid, Topic, MsgBin, [{qos, Qos}]), + Json = rest_response_helper:success_200(), + Req2 = cowboy_req:set_resp_body(Json, Req1), + {true, Req2, State} + end + catch throw:{validation, What} -> + ErrJson = rest_response_helper:error_response(What), + {false, cowboy_req:set_resp_body(ErrJson, Req), State} + end. diff --git a/apps/roster/src/rest/rest_cowboy_push_handler.erl b/apps/roster/src/rest/rest_cowboy_push_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..5b603bd61c7ecaf88cc8a8e11c2afe821f6cdaea --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_push_handler.erl @@ -0,0 +1,64 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /push/message +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_push_handler). +-include_lib("kernel/include/logger.hrl"). +-include("roster.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_accepted/2 + , init/2 + , is_authorized/2 + ]). + +%% Custom callbacks +-export([ from_json/2 + ]). + + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := push_message} = State) -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, State) -> + {[<<"POST">>], Req, State}. + +content_types_accepted(Req, State) -> + {[{<<"application/json">>, from_json} + ], + Req, State}. + +from_json(Req, State) -> + {ok, Body, Req1} = cowboy_req:read_body(Req), + case jsx:is_json(Body) of + false -> + ?LOG_INFO("Error Invalid Json", []), + {false, Req1, State}; + true -> + PS = 'PushServiceRest':from_json(jsx:decode(Body), #'PushService'{}), + Payload = list_to_binary(PS#'PushService'.payload), + Recipients = PS#'PushService'.recipients, + PushAlert = PushType = list_to_binary(PS#'PushService'.module), + Pid = n2o_async:pid(system, roster_push), + lists:foreach( + fun(PhoneId0) -> + PhoneId = iolist_to_binary(PhoneId0), + AuthList = kvs:index('Auth', user_id, PhoneId), + lists:foreach( + fun(Auth) -> + Pid ! {async_push, Auth, Payload, PushAlert, PushType} + end, AuthList) + end, Recipients), + {true, Req1, State} + end. diff --git a/apps/roster/src/rest/rest_cowboy_room_handler.erl b/apps/roster/src/rest/rest_cowboy_room_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..f8fba266c6b0d8103c1fcc836868f825fe8b011e --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_room_handler.erl @@ -0,0 +1,74 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /room +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_room_handler). +-include_lib("kernel/include/logger.hrl"). +-include("roster.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_provided/2 + , init/2 + , is_authorized/2 + , malformed_request/2 + , resource_exists/2 + ]). + +%% Custom callbacks +-export([ to_json/2 + ]). + + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := room} = State) -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, State) -> + {[<<"GET">>], Req, State}. + +malformed_request(Req, State) -> + QSList = cowboy_req:parse_qs(Req), + case proplists:get_value(<<"link">>, QSList) of + undefined -> + Resp = resp_json(<<"Query Parameter Not Found, Check your request">>), + Req1 = cowboy_req:set_resp_body(Resp, Req), + {true, Req1, State}; + Link -> + {false, Req, State#{link => Link}} + end. + +resource_exists(Req, #{link := Link} = State) -> + case roster_channel_helper:get_channel_by_link(Link) of + [] -> + Resp = resp_json(<<"Channel not found">>), + Req1 = cowboy_req:set_resp_body(Resp, Req), + {false, Req1, State}; + Room -> + {true, Req, State#{room => Room}} + end. + +content_types_provided(Req, State) -> + {[{<<"application/json">>, to_json} + ], + Req, State}. + +to_json(Req, #{room := Room} = State) -> + Avatar = roster_channel_helper:get_room_avatar(Room), + Json = resp_json([ {<<"id">>, Room#'Room'.id} + , {<<"name">>, Room#'Room'.name} + , {<<"description">>, Room#'Room'.description} + , {<<"avatar">>, Avatar}]), + {Json, Req, State}. + +resp_json(Data) -> + jsx:encode([{<<"data">>, Data}]). diff --git a/apps/roster/src/rest/rest_cowboy_session_handler.erl b/apps/roster/src/rest/rest_cowboy_session_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..1d4b27941e3b368404d3bb67a8577f6a5285b081 --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_session_handler.erl @@ -0,0 +1,108 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /sessions endpoint +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_session_handler). + +-include_lib("kernel/include/logger.hrl"). +-include("roster.hrl"). + +%% Cowboy callbacks +-export([ init/2 + , is_authorized/2 + , allowed_methods/2 + , content_types_provided/2 + , malformed_request/2 + , resource_exists/2 + ]). + +%% Custom callbacks +-export([ to_json/2 + ]). + + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := sessions} = State) -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +resource_exists(Req, State) -> + {application:get_env(roster, get_sessions_api, false), Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, State) -> + {[<<"GET">>], Req, State}. + + +-define(empty_qs, <<"Empty query parameter. Please, add it in format:" + " phone=RequestPhoneNumber">>). +-define(invalid_qs, <<"Invalid query parameter. Please, add it in format:" + " phone=RequestPhoneNumber">>). +-define(no_sessions, <<"Cannot find sessions for requested number. " + "Please, check input query parameters">>). + +malformed_request(Req, State) -> + case cowboy_req:parse_qs(Req) of + [] -> + {stop, reply_plain(400, ?empty_qs, Req), State}; + QSList -> + case proplists:get_value(<<"phone">>, QSList) of + undefined -> + {stop, reply_plain(400, ?invalid_qs, Req), State}; + Phone -> + {false, Req, State#{phone => Phone}} + end + end. + +content_types_provided(Req, State) -> + {[{<<"application/json">>, to_json} + ], + Req, State}. + +to_json(Req, #{phone := Phone} = State) -> + case kvs:index('Auth', phone, Phone) of + [] -> + {stop, reply_plain(400, ?no_sessions, Req), State}; + Sessions -> + Json = sessions_to_json(Sessions), + {Json, Req, State} + end. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +reply_plain(Status, Msg, Req) -> + Headers = #{<<"content-type">> => <<"text/plain">>}, + cowboy_req:reply(Status, Headers, Msg, Req). + +sessions_to_json(Sessions) -> + %% TODO: Flatten this out to not need the replace + Res0 = + [begin + FL0 = [jsx:encode('FeatureRest':to_json(Feature)) + || Feature <- FeaturesList], + FL1 = roster:binary_join(lists:flatten(FL0), <<",">>), + S1 = S#'Auth'{settings = iolist_to_binary([<<"[">>, FL1, <<"]">>])}, + jsx:encode('AuthRest':to_json(S1)) + end + || #'Auth'{settings = FeaturesList} = S <- Sessions], + Res1 = iolist_to_binary( + [<<"[">>, roster:binary_join(lists:flatten(Res0), <<",">>), <<"]">>]), + re:replace( + re:replace( + re:replace( + re:replace( + re:replace( + Res1, "\\\\\\\"", "\\\"", [{return, list}, global]), + "\\]\\\"", "\\]", [{return, list}, global]), + "\\\"\\[", "\\[", [{return, list}, global]), + "\\\"\\{", "\\{", [{return, list}, global]), + "\\}\\\"", "\\}", [{return, list}, global]). diff --git a/apps/roster/src/rest/rest_cowboy_test_handler.erl b/apps/roster/src/rest/rest_cowboy_test_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..3ffeb38081eff6b1e4773f064afae17e0d7a0bbf --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_test_handler.erl @@ -0,0 +1,44 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /test endpoint +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_test_handler). +-include_lib("kernel/include/logger.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_provided/2 + , init/2 + , is_authorized/2 + ]). + +%% Custom callbacks +-export([ to_json/2 + ]). + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := test_api} = State) -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, State) -> + {[<<"GET">>], Req, State}. + +content_types_provided(Req, State) -> + {[{<<"application/json">>, to_json} + ], + Req, State}. + +%% TODO: GET as state altering operation is not best practice. +to_json(Req, State) -> + TS = roster:timestamp_to_datetime(roster:now_msec()), + JSon = jsx:encode([{<<"Current time">>, list_to_binary(TS)}]), + {JSon, Req, State}. diff --git a/apps/roster/src/rest/rest_cowboy_transcribe_handler.erl b/apps/roster/src/rest/rest_cowboy_transcribe_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..fc633efeea3c7ca303e99687a74ce5b87753a712 --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_transcribe_handler.erl @@ -0,0 +1,73 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /api/v1/chat/messages/transcribe +%%% /api/v1/groups/messages/transcribe +%%% +%%% @end +%%%------------------------------------------------------------------- + +%% TODO: This doesn't seem ready at all. Included in migration anyway +%% in the off chance that someone relies on it. + +-module(rest_cowboy_transcribe_handler). +-include_lib("kernel/include/logger.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_provided/2 + , init/2 + , is_authorized/2 + , resource_exists/2 + , malformed_request/2 + ]). + +%% Custom callbacks +-export([ dummy_to_json/2 + ]). + +-define(groups_transcribe, groups_transcribe). +-define(chats_transcribe, chats_transcribe). + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := Endpoint} = State) when Endpoint =:= ?groups_transcribe; + Endpoint =:= ?chats_transcribe -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, #{} = State) -> + {[<<"GET">>], Req, State}. + +malformed_request(Req, State) -> + try cowboy_req:match_qs([lang], Req) of + #{lang := Lang} -> {false, Req, State#{lang => Lang}} + catch + _:_ -> + Json = rest_response_helper:error_400(), + {true, cowboy_req:set_resp_body(Json, Req), State} + end. + +resource_exists(Req, #{endpoint := ?groups_transcribe} = State) -> + _RoomID = cowboy_req:binding(room_id, Req), + MsgId = cowboy_req:binding(message_id, Req), + case kvs:get('Message', MsgId) of + {ok,_Msg} -> {true, Req, State}; + {error, not_found} -> {false, Req, State} + end. + +content_types_provided(Req, #{endpoint := Endpoint} = State) -> + {[{<<"application/json">>, endpoint_GET_callback(Endpoint)}], + Req, State}. + +endpoint_GET_callback(?groups_transcribe) -> dummy_to_json; +endpoint_GET_callback(?chats_transcribe) -> dummy_to_json. + +dummy_to_json(Req, State) -> + Resp = jsx:encode([{<<"transcription">>, + <<"hardcoded transcription of text">>}]), + {Resp, Req, State}. diff --git a/apps/roster/src/rest/rest_cowboy_users_handler.erl b/apps/roster/src/rest/rest_cowboy_users_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..138620201a2efc02a738e509ceec5e5b00a24720 --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_users_handler.erl @@ -0,0 +1,80 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /users +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_users_handler). + +-include_lib("kernel/include/logger.hrl"). +-include("roster.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_provided/2 + , init/2 + , is_authorized/2 + , malformed_request/2 + ]). + +%% Custom callbacks +-export([ to_json/2 + ]). + + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := users} = State) -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, State) -> + {[<<"GET">>], Req, State}. + +malformed_request(Req, State) -> + try cowboy_req:match_qs([ {order, [], <<"desc">>} + , {page_size, int, 50} + , {page, int, 1} + ], + Req) of + QMap -> + {false, Req, State#{qmap => QMap}} + catch _:_ -> + {true, Req, State} + end. + +content_types_provided(Req, State) -> + {[{<<"application/json">>, to_json} + ], + Req, State}. + +to_json(Req, #{qmap := QMap} = State) -> + #{ order := Order + , page_size := PageSize + , page := PageNumber} = QMap, + {Users, UsersCount, PagesCount} = get_users(Order, PageSize, PageNumber), + Json = jsx:encode([{page_size, PageSize}, + {page, PageNumber}, + {pages_total, PagesCount}, + {users_total, UsersCount}, + {users, Users}]), + {Json, Req, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +get_users(Ordering, PageSize, PageNumber) -> + Users = lists:keysort(#'Roster'.id, kvs:all('Roster')), + SortedUsers = if Ordering == <<"desc">> -> lists:reverse(Users); true -> Users end, + CuttedUsers = [[{id, UID}, {phone_number, UPhone}] || #'Roster'{id = UID, phone = UPhone} <- SortedUsers], + Offset = (PageNumber - 1) * PageSize, + TotalUsersNumber = length(CuttedUsers), + PaginatedUsers = if Offset >= TotalUsersNumber -> []; true -> roster_channel_helper:paginator(CuttedUsers, PageSize, Offset) end, + TotalPageNumbers = round(TotalUsersNumber / PageSize), + {PaginatedUsers, TotalUsersNumber, TotalPageNumbers}. diff --git a/apps/roster/src/rest/rest_cowboy_whitelist_handler.erl b/apps/roster/src/rest/rest_cowboy_whitelist_handler.erl new file mode 100644 index 0000000000000000000000000000000000000000..0597cc95d8048f4cd27f00c9aae962a78098c03c --- /dev/null +++ b/apps/roster/src/rest/rest_cowboy_whitelist_handler.erl @@ -0,0 +1,113 @@ +%%%------------------------------------------------------------------- +%%% @doc Cowboy handler for /whitelist endpoint +%%% +%%% @end +%%%------------------------------------------------------------------- +-module(rest_cowboy_whitelist_handler). +-include_lib("kernel/include/logger.hrl"). + +%% Cowboy callbacks +-export([ allowed_methods/2 + , content_types_accepted/2 + , content_types_provided/2 + , delete_resource/2 + , init/2 + , is_authorized/2 + , malformed_request/2 + , resource_exists/2 + ]). + +%% Custom callbacks +-export([ to_json/2 + , to_html/2 + , from_json/2 + ]). + + +%%%=================================================================== +%%% API +%%%=================================================================== + +init(#{path := Path, method := Method, qs := QS} = Req, + #{endpoint := Endpoint} = State) when Endpoint =:= whitelist; + Endpoint =:= admin_whitelist + -> + ?LOG_INFO("~s:~s ~p", [Method, Path, QS]), + {cowboy_rest, Req, State}. + +resource_exists(Req, State) -> + {application:get_env(roster, whitelist_check, false), Req, State}. + +is_authorized(Req, State) -> + rest_cowboy_handler:is_authorized(Req, State). + +allowed_methods(Req, #{endpoint := admin_whitelist} = State) -> + {[<<"GET">>], Req, State}; +allowed_methods(Req, #{endpoint := whitelist} = State) -> + {[<<"GET">>, <<"PUT">>, <<"DELETE">>], Req, State}. + +-define(phone_not_found, <<"Cannot find phones in query params. Please, add " + "it in format: phone=PhoneNumber1,PhoneNumber2">>). + +malformed_request(#{method := <<"GET">>} = Req, State) -> + {false, Req, State}; +malformed_request(#{method := <<"DELETE">>} = Req, State) -> + QSList = cowboy_req:parse_qs(Req), + case proplists:get_value(<<"phone">>, QSList) of + undefined -> + {true, set_error_resp(?phone_not_found, Req), State}; + Phone -> + {false, Req, State#{phone => Phone}} + end; +malformed_request(#{method := <<"PUT">>} = Req, State) -> + %% Nothing to check here outside the body, which shouldn't be read here. + {false, Req, State}. + +set_error_resp(Reason, Req) -> + Resp = jsx:encode([{<<"Status">>, <<"Error">>}, + {<<"Reason">>, Reason}]), + cowboy_req:set_resp_body(Resp, Req). + +content_types_provided(Req, #{endpoint := admin_whitelist} = State) -> + {[{<<"text/html">>, to_html} + ], + Req, State}; +content_types_provided(Req, #{endpoint := whitelist} = State) -> + {[{<<"application/json">>, to_json} + ], + Req, State}. + +content_types_accepted(Req, State) -> + {[{<<"application/json">>, from_json} + ], + Req, State}. + +delete_resource(Req, #{phone := Phone} = State) -> + Numbers = re:split(Phone, ","), + Data = rest_whitelist:delete_whitelist(Numbers), + Resp = jsx:encode([{<<"Status">>, <<"Success">>}, {<<"Data">>, Data}]), + Req1 = cowboy_req:set_resp_body(Resp, Req), + {true, Req1, State}. + +to_html(#{method := <<"GET">>} = Req, State) -> + {ok, HTML} = phone_numbers_tpl:render([{title, <<"Whitelist">>}]), + {HTML, Req, State}. + +to_json(#{method := <<"GET">>} = Req, State) -> + Json = rest_whitelist:whitelist_json(), + {Json, Req, State}. + +from_json(#{method := <<"PUT">>} = Req, State) -> + {ok, Data, Req1} = cowboy_req:read_body(Req), + case rest_whitelist:check_put_body(Data) of + {ok, Phones} -> + ok = rest_whitelist:update_whitelist(Phones), + Res = jsx:encode([{<<"Status">>, <<"Success">>}]), + Req2 = cowboy_req:set_resp_body(Res, Req1), + {true, Req2, State}; + {error, Reason} -> + Res = jsx:encode([{<<"Status">>, <<"Error">>}, + {<<"Reason">>, Reason}]), + Req2 = cowboy_req:set_resp_body(Res, Req1), + {false, Req2, State} + end. diff --git a/apps/roster/src/rest/rest_cri.erl b/apps/roster/src/rest/rest_cri.erl index 92345ae854d1baeb84dc934952b739747f447c13..466d4603336c2f915692d4c52e53c0f6a3593bad 100644 --- a/apps/roster/src/rest/rest_cri.erl +++ b/apps/roster/src/rest/rest_cri.erl @@ -1,241 +1,26 @@ -module(rest_cri). --compile({parse_transform, lager_transform}). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include("rest_static.hrl"). --include_lib("roster/include/static/rest_var.hrl"). -include_lib("roster/include/static/rest_text.hrl"). -include_lib("roster/include/static/roster_text.hrl"). -include_lib("roster/include/static/roster_var.hrl"). -include_lib("roster/include/static/main_var.hrl"). -export([ - description/0, - handle_request/1, create_message/2 -]). + ]). -description() -> "Call Room Interface Module". - -handle_request(Req)-> - check_headers_missing(Req, [?PHONE_ID_HEADER]). - -%% Room management - -handle_request('GET', ?RCI_ROOM_TYPE_ENDPOINT, Req) -> - QueryData = Req:parse_qs(), - roster:info(?MODULE, "~p:Request:~p:~p", [Req:get(method), Req:get(path), QueryData]), - RoomId = nitro:to_binary(proplists:get_value(nitro:to_binary(?ROOM_ID_PARAM), rest_main_helper:get_query_values(QueryData, [?ROOM_ID_PARAM]))), - {ok, #'Room'{type = Type}} = kvs:get('Room', RoomId), - ResponseData = rest_response_helper:success_response(jsx_tuple_to_json([{?ROOM_TYPE_VALUE, Type}])), - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - Req:respond({?HTTP_CODE_200, [], ResponseData}); - -handle_request('POST', ?RCI_ROOM_ENDPOINT, Req) -> - roster:info(?MODULE, "~p:Request:~p", [Req:get(method), Req:get(path)]), - RoomId = iolist_to_binary(["conference_", nitro:to_binary(roster:now_msec()), "_", nitro:to_binary(kvs:next_id('Room', 1))]), - AdminPhoneId = Req:get_header_value(?PHONE_ID_HEADER), - {ok, #'Member'{alias = AdminNick, names = AdminName} = AdminMember} = roster:to_member(nitro:to_binary(AdminPhoneId)), -%% create call room name - RoomName = iolist_to_binary(["Call by @", case AdminNick of [] -> AdminName; _ -> AdminNick end]), - rest_main_helper:send_to_mqtt(sync, #'Room'{id = RoomId, status = ?CREATE_STATUS, type = ?CALL_ROOM, name = RoomName, admins = [AdminMember]}), - ResponseData = rest_response_helper:success_response(jsx_tuple_to_json([{?ROOM_ID_PARAM, RoomId}])), - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - Req:respond({?HTTP_CODE_200, [], ResponseData}); - -handle_request('DELETE', ?RCI_ROOM_ENDPOINT, Req) -> - QueryData = Req:parse_qs(), - roster:info(?MODULE, "~p:Request:~p:~p", [Req:get(method), Req:get(path), QueryData]), - RoomId = nitro:to_binary(proplists:get_value(nitro:to_binary(?ROOM_ID_PARAM), rest_main_helper:get_query_values(QueryData, [?ROOM_ID_PARAM]))), - rest_main_helper:send_to_mqtt(#'Room'{id = RoomId, status = ?DELETE_STATUS, type = ?CALL_ROOM}), - ResponseData = rest_response_helper:success_response(), - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - Req:respond({?HTTP_CODE_200, [], ResponseData}); - -%% Members management - -handle_request('GET', ?RCI_ROOM_MEMBERS_ENDPOINT, Req) -> - QueryData = Req:parse_qs(), - roster:info(?MODULE, "~p:Request:~p:~p", [Req:get(method), Req:get(path), QueryData]), - ReqParamValues = rest_main_helper:get_query_values(QueryData, [?ROOM_ID_PARAM, ?PHONE_IDS_PARAM]), - RoomId = nitro:to_binary(proplists:get_value(nitro:to_binary(?ROOM_ID_PARAM), ReqParamValues)), - PhoneIdList = proplists:get_value(nitro:to_binary(?PHONE_IDS_PARAM), ReqParamValues), -%% check membership for each phone number - VerifiedPhones = [ - case roster:muc_member(nitro:to_binary(PhoneId), RoomId) of - #'Member'{} -> jsx_tuple_to_json([{?PHONE_ID_PARAM, PhoneId}, {?IS_MEMBER_FLAG, true}]); - [] -> - case rest_main_helper:get_profile_by_phone(rest_main_helper:get_phone_number(PhoneId)) of - {error, _} -> jsx_tuple_to_json([{?PHONE_ID_PARAM, PhoneId}, {?IS_MEMBER_FLAG, false}, {error, ?ERROR_USER_404}]); - _ -> jsx_tuple_to_json([{?PHONE_ID_PARAM, PhoneId}, {?IS_MEMBER_FLAG, false}]) - end - end || PhoneId <- string:tokens(PhoneIdList, ",")], - ResponseData = rest_response_helper:success_response(VerifiedPhones), - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - Req:respond({?HTTP_CODE_200, [], ResponseData}); - -handle_request('DELETE', ?RCI_ROOM_MEMBERS_ENDPOINT, Req) -> - QueryData = Req:parse_qs(), - roster:info(?MODULE, "~p:Request:~p:~p", [Req:get(method), Req:get(path), QueryData]), - ReqParamValues = rest_main_helper:get_query_values(QueryData, [?ROOM_ID_PARAM, ?PHONE_IDS_PARAM]), - RoomId = nitro:to_binary(proplists:get_value(nitro:to_binary(?ROOM_ID_PARAM), ReqParamValues)), - PhoneIdList = proplists:get_value(nitro:to_binary(?PHONE_IDS_PARAM), ReqParamValues), - AdminPhoneId = Req:get_header_value(?PHONE_ID_HEADER), - {ok, AdminMember} = roster:to_member(nitro:to_binary(AdminPhoneId)), - {ResponseStatus, ResponseData} = request_with_failed_status(string:tokens(PhoneIdList, ","), #'Room'{id = RoomId, status = ?REMOVE_STATUS, admins = [AdminMember]}), - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - Req:respond({ResponseStatus, [], ResponseData}); - -%% room_id, join - always required -%% if join == false - phone_ids required -handle_request('POST', ?RCI_ROOM_MEMBERS_ENDPOINT, Req) -> - ReqBody = mochiweb_request:recv_body(Req), - roster:info(?MODULE, "~p:Request:~p:~p", [Req:get(method), Req:get(path), ReqBody]), - ReqParamValues = rest_main_helper:get_body_values(ReqBody, [?ROOM_ID_PARAM, ?PHONE_IDS_PARAM, ?JOIN_FLAG_PARAM]), - RoomId = nitro:to_binary(proplists:get_value(nitro:to_binary(?ROOM_ID_PARAM), ReqParamValues)), - IsJoin = nitro:to_binary(proplists:get_value(nitro:to_binary(?JOIN_FLAG_PARAM), ReqParamValues)), - {ResponseStatus, ResponseData} = -%% validate join value - if - IsJoin == <<"true">> -> - AdminPhoneId = Req:get_header_value(?PHONE_ID_HEADER), - {ok, JoinMember} = roster:to_member(nitro:to_binary(AdminPhoneId)), - rest_main_helper:send_to_mqtt(#'Room'{id = RoomId, status = ?JOIN_STATUS, type = ?CALL_ROOM, members = [JoinMember]}), - {?HTTP_CODE_200, rest_response_helper:success_response()}; - IsJoin == <<"false">> -> - case rest_main_helper:get_body_value(ReqBody, ?PHONE_IDS_PARAM) of - {error, PhonesIdParamError} -> {?HTTP_CODE_400, rest_response_helper:error_response(PhonesIdParamError)}; - {ok, PhoneIdList} -> - AdminPhoneId = nitro:to_binary(Req:get_header_value(?PHONE_ID_HEADER)), - case is_admin(AdminPhoneId, RoomId) of - {error, _} -> {?HTTP_CODE_403, rest_response_helper:error_response(?HTTP_CODE_403, ?ERROR_PERMISSION_DENIED)}; - {ok, AdminMember} -> request_with_failed_status(PhoneIdList, #'Room'{id = RoomId, status = ?ADD_STATUS, admins = [AdminMember]}) - end - end; - true -> {?HTTP_CODE_400, rest_response_helper:error_response(?ERROR_INVALID_REQUEST_PARAM)} - end, - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - Req:respond({ResponseStatus, [], ResponseData}); - -%% Call Bubbles management - -handle_request('POST', ?RCI_BUBBLE_ENDPOINT, Req) -> - ReqBody = mochiweb_request:recv_body(Req), - roster:info(?MODULE, "~p:Request:~p:~p", [Req:get(method), Req:get(path), ReqBody]), - CallBubbleJSON = 'CallBubbleJSONRest':from_json(jsx:decode(ReqBody), #'CallBubbleJSON'{}), - {ResponseStatus, ResponseData} = -%% validate input data - case validate_bubble_data(CallBubbleJSON) of - error -> {?HTTP_CODE_400, rest_response_helper:error_response()}; - ok -> - FromPhoneId = nitro:to_binary(Req:get_header_value(?PHONE_ID_HEADER)), - case validate_permission(FromPhoneId, CallBubbleJSON) of - error -> {?HTTP_CODE_403, rest_response_helper:error_response(?HTTP_CODE_403, ?ERROR_PERMISSION_DENIED)}; - ok -> - Msg = create_message(FromPhoneId, CallBubbleJSON), - rest_main_helper:send_to_mqtt(Msg), - {?HTTP_CODE_200, rest_response_helper:success_response(binary_to_list(term_to_binary(Msg)))} - end - end, - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - Req:respond({ResponseStatus, [], ResponseData}); - -handle_request(_, _, Req) -> - rest_response_helper:error_405_response(Req). -%% roster:info(?MODULE, "MethodNotAllowed:~p:~p", [Method, Path]), -%% Req:respond({?HTTP_CODE_405, [], []}). +%% For Cowboy handler +-export([request_with_failed_status/2, + validate_bubble_data/1, + validate_permission/2 + ]). %% ------------------------------------------------------------------ %% CRI Local Helpers %% ------------------------------------------------------------------ -checker_error_logs(Req, Error, Status) -> - roster:info(?MODULE, "~p:~p:~p:~p(Query:~p;Body:~p)", [Req:get(method), Req:get(path), Status, Error, Req:parse_qs(), mochiweb_request:recv_body(Req)]). - -check_headers_missing(Req, HeadersList) -> - case rest_main_helper:check_params_missing(HeadersList, rest_main_helper:get_request_headers(Req, HeadersList)) of - {error, _} -> - roster:error(?MODULE, "RequestHeadersProtectedAction", []), - checker_error_logs(Req, Er = ?ERROR_MISSING_HEADER, St = ?HTTP_CODE_400), - Req:respond({St, [], rest_response_helper:error_response(Er)}); - {ok, Headers} -> - roster:info(?MODULE, "Headers:~p", [Headers]), - check_phone_id_header(Req, Headers) - end. - -check_phone_id_header(Req, Headers) -> - case rest_main_helper:get_profile_by_phone(rest_main_helper:get_phone_number(nitro:to_binary(proplists:get_value(nitro:to_binary(?PHONE_ID_HEADER), Headers)))) of - {error, _} -> - roster:error(?MODULE, "ActedUserExistsProtectedAction", []), - checker_error_logs(Req, [], St = ?HTTP_CODE_400), - Req:respond({St, [], rest_response_helper:error_response()}); - {ok, _} -> - Method = Req:get(method), - Path = Req:get(path), - if - (Path == ?RCI_ROOM_TYPE_ENDPOINT andalso Method == 'GET') orelse (Path == ?RCI_ROOM_ENDPOINT andalso Method == 'DELETE') -> check_params_missing(Req, [?ROOM_ID_PARAM], query); - Path == ?RCI_ROOM_MEMBERS_ENDPOINT andalso (Method == 'GET' orelse Method == 'DELETE') -> check_params_missing(Req, [?ROOM_ID_PARAM, ?PHONE_IDS_PARAM], query); - Path == ?RCI_ROOM_MEMBERS_ENDPOINT andalso Method == 'POST' -> check_params_missing(Req, [?ROOM_ID_PARAM, ?JOIN_FLAG_PARAM], body); - true -> handle_request(Req:get(method), Req:get(path), Req) - end - end. - -check_params_missing(Req, ParamsList, query) -> - check_params_missing(Req, ParamsList, Req:parse_qs(), fun rest_main_helper:get_query_values/2); -check_params_missing(Req, ParamsList, body) -> - check_params_missing(Req, ParamsList, mochiweb_request:recv_body(Req), fun rest_main_helper:get_body_values/2). - -check_params_missing(Req, ParamsList, ReqData, Fun) -> - case rest_main_helper:check_params_missing(ParamsList, Fun(ReqData, ParamsList)) of - {error, Er} -> - roster:error(?MODULE, "ParamsExistsProtectedAction", []), - checker_error_logs(Req, Er, St = ?HTTP_CODE_400), - Req:respond({St, [], rest_response_helper:error_response(Er)}); - {ok, Params} -> - check_room_exists(Req, Params) - end. - -check_room_exists(Req, ReqParams) -> - case kvs:get('Room', nitro:to_binary(proplists:get_value(nitro:to_binary(?ROOM_ID_PARAM), ReqParams))) of - {ok, #'Room'{status = Status}} when Status /= delete -> - Method = Req:get(method), - Path = Req:get(path), - if - (Path == ?RCI_ROOM_MEMBERS_ENDPOINT andalso Method == 'DELETE') orelse (Path == ?RCI_ROOM_MEMBERS_ENDPOINT andalso Method == 'POST') orelse (Path == ?RCI_ROOM_ENDPOINT andalso Method == 'DELETE') -> check_room_type(Req, ReqParams); - true -> handle_request(Req:get(method), Req:get(path), Req) - end; - _ -> - roster:error(?MODULE, "RoomExistsProtectedAction", []), - checker_error_logs(Req, Er = ?ERROR_ROOM_NOT_FOUND, St = ?HTTP_CODE_400), - Req:respond({St, [], rest_response_helper:error_response(Er)}) - end. - -%% Check is it regular group or call -check_room_type(Req, ReqParams) -> - case kvs:get('Room', nitro:to_binary(proplists:get_value(nitro:to_binary(?ROOM_ID_PARAM), ReqParams))) of - {ok, #'Room'{type = ?CALL_ROOM}} -> - Method = Req:get(method), - Path = Req:get(path), - if - (Path == ?RCI_ROOM_MEMBERS_ENDPOINT andalso Method == 'DELETE') orelse (Path == ?RCI_ROOM_ENDPOINT andalso Method == 'DELETE') -> check_admin_action(Req, ReqParams); - true -> handle_request(Req:get(method), Req:get(path), Req) - end; - _ -> - roster:error(?MODULE, "RoomTypeProtectedAction", []), - checker_error_logs(Req, Er = ?ERROR_PERMISSION_DENIED, St = ?HTTP_CODE_403), - Req:respond({St, [], rest_response_helper:error_response(Er)}) - end. - -check_admin_action(Req, ReqParams) -> - AdminPhoneId = nitro:to_binary(Req:get_header_value(?PHONE_ID_HEADER)), - RoomId = nitro:to_binary(proplists:get_value(nitro:to_binary(?ROOM_ID_PARAM), ReqParams)), - case is_admin(AdminPhoneId, RoomId) of - {error, _} -> - roster:error(?MODULE, "AdminProtectedAction", []), - checker_error_logs(Req, Er = ?ERROR_PERMISSION_DENIED, St = ?HTTP_CODE_403), - Req:respond({St, [], rest_response_helper:error_response(St, Er)}); - {ok, _} -> handle_request(Req:get(method), Req:get(path), Req) - end. - jsx_tuple_to_json(Data) -> jsx:encode([{nitro:to_binary(K), nitro:to_binary(V)} || {K, V} <- Data]). @@ -253,7 +38,7 @@ failed_json(PhoneList) -> {failed, iolist_to_binary( ["[", string:join( - [binary_to_list(jsx_tuple_to_json([{?PHONE_ID_PARAM, FN}, {error, ?ERROR_USER_404}])) || FN <- PhoneList], + [binary_to_list(jsx_tuple_to_json([{<<"phone_id">>, FN}, {error, ?ERROR_USER_404}])) || FN <- PhoneList], ","), "]"] )} @@ -264,52 +49,44 @@ request_with_failed_status(PhoneIdList, Room) -> %% if Members list is empty - return error and don't do mqtt call %% if exists at least OK member - return 200 and errors list case Members of - [] -> {?HTTP_CODE_400, rest_response_helper:error_response(failed_json(FailedNumbers))}; + [] -> {error, rest_response_helper:error_response(failed_json(FailedNumbers))}; _ -> rest_main_helper:send_to_mqtt(Room#'Room'{type = ?CALL_ROOM, members = Members}), - {?HTTP_CODE_200, rest_response_helper:success_response(failed_json(FailedNumbers))} - end. - -is_admin(PhoneId, RoomId) -> - case roster:muc_member(PhoneId, RoomId) of - Mmbr when is_record(Mmbr, 'Member') andalso Mmbr#'Member'.status == admin -> - {ok, Mmbr}; - _ -> - {error, []} + {ok, rest_response_helper:success_response(failed_json(FailedNumbers))} end. validate_bubble_data(#'CallBubbleJSON'{content_type = ContentType} = Data) -> %% check content type value case lists:member(nitro:to_binary(ContentType), [?CONTENT_TYPE_VIDEOCALL, ?CONTENT_TYPE_AUDIOCALL]) of - false -> roster:error(?MODULE, "InvalidCallBubbleContentType:~p:In:~p", [ContentType, Data]), error; + false -> ?LOG_ERROR("InvalidCallBubbleContentType:~p:In:~p", [ContentType, Data]), error; _ -> validate_type(Data) end. validate_type(#'CallBubbleJSON'{type = CallType} = Data) -> %% check call type value case lists:member(nitro:to_binary(CallType), [?CALL_TYPE_P2P, ?CALL_TYPE_CONFERENCE]) of - false -> roster:error(?MODULE, "InvalidCallBubbleType:~p:In:~p", [CallType, Data]), error; + false -> ?LOG_ERROR("InvalidCallBubbleType:~p:In:~p", [CallType, Data]), error; _ -> validate_feed(nitro:to_binary(CallType), Data) end. validate_feed(?CALL_TYPE_P2P, #'CallBubbleJSON'{feed_id = FeedId} = Data) -> case string:tokens(FeedId, "/") of [From, To] -> validate_feed(#p2p{from = nitro:to_binary(From), to = nitro:to_binary(To)}, Data); - _ -> roster:error(?MODULE, "InvalidCallBubbleFeedId:~p:In:~p", [FeedId, Data]), error + _ -> ?LOG_ERROR("InvalidCallBubbleFeedId:~p:In:~p", [FeedId, Data]), error end; validate_feed(?CALL_TYPE_CONFERENCE, #'CallBubbleJSON'{feed_id = FeedId} = Data) -> validate_feed(#muc{name = nitro:to_binary(FeedId)}, Data); validate_feed(Feed, #'CallBubbleJSON'{type = Type} = Data) -> case kvs:get('writer', Feed) of {ok, #'writer'{}} -> validate_recipients(nitro:to_binary(Type), Data); - _ -> roster:error(?MODULE, "InvalidCallBubbleFeed:~p:In:~p", [Feed, Data]), error + _ -> ?LOG_ERROR("InvalidCallBubbleFeed:~p:In:~p", [Feed, Data]), error end. validate_recipients(?CALL_TYPE_P2P, _) -> ok; validate_recipients(?CALL_TYPE_CONFERENCE, #'CallBubbleJSON'{recipients = Recipients} = Data) -> %% check that recipients is a list of phone_ids case strict_list(Recipients) of - false -> roster:error(?MODULE, "InvalidCallBubbleRecipient:~p:In:~p", [Recipients, Data]), error; + false -> ?LOG_ERROR("InvalidCallBubbleRecipient:~p:In:~p", [Recipients, Data]), error; _ -> ok end. @@ -318,12 +95,12 @@ validate_permission(PhoneId, #'CallBubbleJSON'{type = CallType, feed_id = FeedId validate_permission(PhoneId, #'p2p'{from = U1, to = U2}, Data) -> if PhoneId == U1 orelse PhoneId == U2 -> ok; - true -> roster:error(?MODULE, "PermissionDeniedForUser:~p:In:~p", [PhoneId, Data]), error + true -> ?LOG_ERROR("PermissionDeniedForUser:~p:In:~p", [PhoneId, Data]), error end; validate_permission(PhoneId, #'muc'{name = RoomId}, Data) -> case roster:muc_member(PhoneId, RoomId) of #'Member'{} -> ok; - _ -> roster:error(?MODULE, "PermissionDeniedForUser:~p:In:~p", [PhoneId, Data]), error + _ -> ?LOG_ERROR("PermissionDeniedForUser:~p:In:~p", [PhoneId, Data]), error end. get_feed(?CALL_TYPE_CONFERENCE, FeedId) -> #muc{name = nitro:to_binary(FeedId)}; @@ -352,4 +129,4 @@ create_message(FromPhoneId, #'CallBubbleJSON'{feed_id = FeedId, call_id = Confer files = [#'Desc'{id = DescId, mime = nitro:to_binary(ContentType), payload = nitro:to_binary(Status), data = Features}]}. strict_list(Data) -> - case is_list(Data) of false -> false; _ -> case io_lib:printable_list(Data) of true -> false; _ -> true end end. \ No newline at end of file + case is_list(Data) of false -> false; _ -> case io_lib:printable_list(Data) of true -> false; _ -> true end end. diff --git a/apps/roster/src/rest/rest_deeplink.erl b/apps/roster/src/rest/rest_deeplink.erl deleted file mode 100644 index 9037234e5391eefb1aa109f7fc1d9fc2f5621ca0..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/rest_deeplink.erl +++ /dev/null @@ -1,36 +0,0 @@ --module(rest_deeplink). --compile({parse_transform, lager_transform}). --include("roster.hrl"). --include("rest_static.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --export([handle_request/3]). - - -%% Request /room?link=LinkValue -%% Response: -%% Success: {"data": {"id": RoomIdString, "name": RoomNameString, "avatar": LinkToRoomAvatarString, "description": RoomDescriptionString}} -%% Failure {"data": "Channel Not Found"} -handle_request('GET', _, Req) -> - QueryData = Req:parse_qs(), - roster:info(?MODULE, "Request:~p:~p:~p", [Req:get(method), Req:get(path), QueryData]), - {ResponseStatus, ResponseData} = case rest_main_helper:get_query_value(QueryData, ?ROOM_LINK_KEY) of - {error, QueryParamError} -> {?HTTP_CODE_400, QueryParamError}; - {ok, LinkValue} -> - case roster_channel_helper:get_channel_by_link(LinkValue) of - [] -> {?HTTP_CODE_404, ?CHANNEL_NOT_FOUND}; - #'Room'{id = Id, name = Name, description = Description} = FoundRoom -> - RoomResponse = [ - {?FOUND_ROOM_ID, Id}, - {?FOUND_ROOM_NAME, Name}, - {?FOUND_ROOM_AVATAR, roster_channel_helper:get_room_avatar(FoundRoom)}, - {?FOUND_ROOM_DESCRIPTION, Description}], - {?HTTP_CODE_200, RoomResponse} - end - end, - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - Req:respond({ResponseStatus, [], jsx:encode([{<<"data">>, ResponseData}])}); - -handle_request(_, _, Req) -> - rest_response_helper:error_405_response(Req). -%% roster:info(?MODULE, "MethodNotAllowed:~p:~p", [Method, Path]), -%% Req:respond({?HTTP_CODE_405, [], []}). \ No newline at end of file diff --git a/apps/roster/src/rest/rest_fake_numbers.erl b/apps/roster/src/rest/rest_fake_numbers.erl deleted file mode 100644 index cc47233235f0a57ee278c11fe108659c750b1c2e..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/rest_fake_numbers.erl +++ /dev/null @@ -1,53 +0,0 @@ --module(rest_fake_numbers). --include("roster.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --compile({parse_transform, lager_transform}). --export([handle_request/3]). - -handle_request('GET', "/fake_numbers", Req) -> - roster:info(?MODULE, "~p:GET:~p", [Req:get(path), Req:parse_qs()]), - {ok, HTMLOutput} = phone_numbers_tpl:render([{title, <<"Fake Numbers">>}]), - Req:respond({?HTTP_CODE_200, [{"Content-Type", "text/html"}], HTMLOutput}); - -handle_request('GET', "/fn", Req) -> - roster:info(?MODULE, "~p:GET:~p", [Req:get(path), Req:parse_qs()]), - Res = jsx:encode( - #{<<"Status">> => <<"Success">>, - <<"Data">> => [#{<<"phone">> => binary_to_integer(P), - <<"created">> => list_to_binary(roster:timestamp_to_datetime(CreatedTimestamp))} - || #'FakeNumbers'{created = CreatedTimestamp, phone = P} <- kvs:all('FakeNumbers')]}), -%% roster:info(?MODULE, "Res:~p", [Res]), - Req:respond({?HTTP_CODE_200, [], Res}); - -handle_request('PUT', "/fn", Req) -> - ReqBody = mochiweb_request:recv_body(Req), - roster:info(?MODULE, "~p:PUT:~p", [Req:get(path), ReqBody]), - Error = #{<<"Status">> => <<"Error">>, <<"Reason">> => - <<"Empty Body section. Please, add numbers in format: {\"phone\": [PhoneNumber1,PhoneNumber2]}">>}, - {HttpCode, RawRes} = case catch jsx:decode(ReqBody, [return_maps]) of - #{<<"phone">> := [_|_] = Phones} -> - [roster:add_fake_number(nitro:to_binary(P)) || P <- Phones], - {?HTTP_CODE_200, #{<<"Status">> => <<"Success">>}}; - _ -> {?HTTP_CODE_400, Error} - end, - Req:respond({HttpCode, [], jsx:encode(RawRes)}); - -handle_request('DELETE', "/fn", Req) -> - QueryParams = Req:parse_qs(), - roster:info(?MODULE, "~p:DELETE:~p", [Req:get(path), QueryParams]), - PhoneQueryParams = lists:keyfind("phone", 1, QueryParams), - {HttpCode, RawRes} = - case lists:keyfind("phone", 1, QueryParams) of - false -> - {?HTTP_CODE_400, - #{<<"Status">> => <<"Error">>, - <<"Reason">> => <<"Cannot find phones in query params. Please, add it in format: phone=PhoneNumber1,PhoneNumber2">>}}; - _ -> [kvs:delete('FakeNumbers', nitro:to_binary(P)) || P <- string:tokens(element(2, PhoneQueryParams), ",")], - {?HTTP_CODE_200, #{<<"Status">> => <<"Success">>}} - end, - roster:info(?MODULE, "Res:~p", [RawRes]), - Req:respond({HttpCode, [], jsx:encode(RawRes)}); - -handle_request(Method, Path, Req) -> - roster:info(?MODULE, "MethodNotAllowed:~p:~p", [Method, Path]), - rest_response_helper:response(Req, ?HTTP_CODE_405, rest_response_helper:error_405()). \ No newline at end of file diff --git a/apps/roster/src/rest/rest_handler.erl b/apps/roster/src/rest/rest_handler.erl deleted file mode 100644 index 6f85c2bc176f463b6f4d15bfc8ca6bf6a8c5ca8c..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/rest_handler.erl +++ /dev/null @@ -1,120 +0,0 @@ --module(rest_handler). --compile({parse_transform, lager_transform}). --include_lib("roster/include/static/rest_text.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --export([init/0, handle_request/1, c_tpl/0, c_tpl/1, c_tpl/2, docroot/0]). - -%% TODO fix with wait() --define(REST_PORT, application:get_env(rest, port, 8888)). --define(OPEN_API_LIST, application:get_env(rest, open_api_list, [])). - -%% Environment variables --define(CHECK_SESSIONS_API, application:get_env(roster, get_sessions_api, false)). --define(CHECK_WHITELIST, application:get_env(roster, whitelist_check, false)). --define(CHECK_FAKE_NUMBERS, application:get_env(roster, fake_numbers_check, false)). - -init() -> - mochiweb:start_http('mqtt:http', ?REST_PORT, [], {rest_handler, handle_request, []}), - roster:info(?MODULE, "REST_PID:~p", [rest_main_helper:rest_pid()]), - c_tpl(). - -%% ------------------------------------------------------------------ -%% Main Module Helpers -%% ------------------------------------------------------------------ - -%% TODO place it to rest_helpers module - -docroot() -> - {file, Here} = code:is_loaded(?MODULE), - Dir = filename:dirname(filename:dirname(Here)), - filename:join([Dir, "priv", "www"]). - -c_tpl() -> -%% compile template (dtl) files - c_tpl([]). -c_tpl(Opts) -> - c_tpl(filelib:wildcard("apps/roster/priv/www/*.dtl"), Opts). -c_tpl([], _Opts) -> ok; -c_tpl([File | Files], Opts) -> - ok = erlydtl:compile(File, re:replace(filename:basename(File), ".dtl", "_tpl", [global, {return, list}]), Opts), - c_tpl(Files, Opts). - -%% ------------------------------------------------------------------ -%% Rest Handlers Routing -%% ------------------------------------------------------------------ - -handle_request(Req) -> - roster:info(?MODULE, "Method: ~p Path: ~p Requset: ~p", [Req:get(method), Req:get(path), Req:parse_qs()]), - Path= case Req:get(path) of "/" ++ P -> P; _ ->"/" end, -%% NOTE extra check for "favicon.ico" file on rest requests without html body - case Path of - "favicon.ico" -> Req:serve_file("assets/img/favicon.ico", docroot()); - _ -> - %% check is it static file - case string:find(Path, "assets/") of - nomatch -> -%% /test - case lists:member(nitro:to_binary(Req:get(path)), ?OPEN_API_LIST) of - true -> handle_request(Req:get(method), Req:get(path), Req); - _ -> case rest_auth_helper:authorized(Req) of - false -> rest_response_helper:response(Req, ?HTTP_CODE_401, rest_response_helper:error_401()); - {error, invalid_token} -> rest_response_helper:error_response_api(Req, ?HTTP_CODE_401, "Token is invalid."); - {error, token_expired} -> rest_response_helper:error_response_api(Req, ?HTTP_CODE_401, "Token is expired."); - {true, api} -> handle_request2(Req:get(method), Req:get(path), Req); %% We hit here if authentication is Bearer (Not Basic) - _ -> handle_request(Req:get(method), Req:get(path), Req) - end - end; - _ -> - Req:serve_file(Path, docroot()) - end - end. - -handle_request(Method, "/chat" ++ _ = P, Req) -> - ["chat", PhoneId, "message"] = lists:delete([], string:split(P, "/", all)), - rest_message:handle_request(Method, #{user => list_to_binary(PhoneId)}, Req); - -handle_request(Method, "/sessions" = Path, Req) -> case ?CHECK_SESSIONS_API of true -> rest_session :handle_request(Method, Path, Req); _ -> handle_request_404(Method, Path, Req) end; -handle_request(Method, "/whitelist" = Path, Req) -> case ?CHECK_WHITELIST of true -> rest_whitelist :handle_request(Method, Path, Req); _ -> handle_request_404(Method, Path, Req) end; -handle_request(Method, "/admin_whitelist" = Path, Req) -> case ?CHECK_WHITELIST of true -> admin_whitelist :handle_request(Method, Path, Req); _ -> handle_request_404(Method, Path, Req) end; -handle_request(Method, Path, Req) - when Path == "/fake_numbers"; Path == "/fn" -> case ?CHECK_FAKE_NUMBERS of true -> rest_fake_numbers:handle_request(Method, Path, Req); _ -> handle_request_404(Method, Path, Req) end; - - -%% NOTE! uncomment the row under to turn on call bubbles -%% handle_request(_, ?RCI_BUBBLE_ENDPOINT, Req) -> rest_cri :handle_request(Req); -handle_request(Method, "/metrics" = Path, Req) -> rest_metric :handle_request(Method, Path, Req); -handle_request(Method, ?MSG_PUSH_ENDPOINT = Path, Req) -> rest_push:handle_request(Method, Path, Req); -handle_request(Method, ?ROOM_ENDPOINT = Path, Req) -> rest_deeplink:handle_request(Method, Path, Req); -handle_request(Method, ?PUBLISH_ENDPOINT = Path, Req) -> rest_publish:handle_request(Method, Path, Req); -handle_request(Method, ?USERS_ENDPOINT = Path, Req) -> rest_users:handle_request(Method, Path, Req); -handle_request(_, "/cri/" ++ _ = Path, Req) - when Path == ?RCI_ROOM_TYPE_ENDPOINT; Path == ?RCI_ROOM_ENDPOINT; Path == ?RCI_ROOM_MEMBERS_ENDPOINT; - Path == ?RCI_BUBBLE_ENDPOINT -> rest_cri:handle_request(Req); -handle_request(Method, ?METRICS_ENDPOINT = Path, Req) -> rest_metric:handle_request(Method, Path, Req); - -handle_request('GET', ?TEST_API_ENDPOINT, Req) -> - roster:info(?MODULE, "~p:~p", [Req:get(method), Req:get(path)]), - Res = mochijson:encode({struct, [{<<"Current time">>, roster:timestamp_to_datetime(roster:now_msec())}]}), - rest_response_helper:response(Req, ?HTTP_CODE_200, Res); - -handle_request(Method, "/link/" ++ _ = Path, Req) -> - ["link", LinkId, "room_id"] = lists:filter(fun (Elem) -> Elem /= [] end, string:split(Path, "/", all)), - rest_link:handle_request(Method, #{link_id => LinkId}, Req); - -handle_request(Method, Path, Req) -> handle_request_404(Method, Path, Req). - -handle_request_404(Method, Path, Req) -> - roster:info(?MODULE, "UnexpectedHTTPRequest:~p:~p", [Method, Path]), - rest_response_helper:response(Req, ?HTTP_CODE_404, rest_response_helper:error_404()). - -%% Seperate handler funcions for routing calls under /api/ -%% All calls under /api/ are opened to the world throuth https (this is not done in the erlang code) -handle_request2(Method, "/api" ++ Path, Req) -> handle_request2(Method, Path, Req); -handle_request2(Method, "/v1" ++ Path, Req) -> handle_request2_v1(Method, lists:delete([], string:split(Path, "/", all)), Req). - -%% Handler functions for version 1 of API Protocol -handle_request2_v1(Method, ["groups", RoomId, "messages", "csv"], Req) -> rest_chat_csv:handle_request(Method, #{room => RoomId}, Req); -handle_request2_v1(Method, ["chats", PhoneId, "messages", "csv"], Req) -> rest_chat_csv:handle_request(Method, #{user => PhoneId}, Req); -handle_request2_v1(Method, ["groups", RoomId, "messages", MessageId, "transcribe"], Req) -> rest_transcribe:handle_request(Method, #{room => RoomId, message => MessageId}, Req); -handle_request2_v1(Method, ["chats", PhoneId, "messages", MessageId, "transcribe"], Req) -> rest_transcribe:handle_request(Method, #{user => PhoneId, message => MessageId}, Req); -handle_request2_v1(_, _, Req) -> rest_response_helper:error_response_api(Req, ?HTTP_CODE_400, "Bad request"). \ No newline at end of file diff --git a/apps/roster/src/rest/rest_link.erl b/apps/roster/src/rest/rest_link.erl deleted file mode 100644 index 834e1090bf4b2d4d37e3e3c9ebd836cb3dccbbef..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/rest_link.erl +++ /dev/null @@ -1,30 +0,0 @@ --module(rest_link). --include("roster.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --include_lib("kvs/include/metainfo.hrl"). - --export([ - handle_request/3 -]). - -handle_request('GET', #{link_id := LinkId}, Req) -> - {StatusCode, ResponseData} = - case kvs:get('Link', list_to_binary(LinkId)) of - {ok, #'Link'{room_id = RoomId}} -> - case kvs:get('Room', RoomId) of - {ok, #'Room'{name = RoomName}} -> - {?HTTP_CODE_200, [{<<"room_id">>, RoomId}, {<<"room_name">>, RoomName}]}; - _ -> - {?HTTP_CODE_404, [{<<"error">>, <<"not_found">>}, {<<"room_id">>, list_to_binary(RoomId)}]} - end; - {error, not_found} -> - {?HTTP_CODE_404, [{<<"error">>, <<"not_found">>}, {<<"link_id">>, list_to_binary(LinkId)}]} - end, - send_response(Req, ResponseData, StatusCode). - -send_response(Req, ResponseData, StatusCode) -> - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - ResponseHeader = [{"Content-Type", "application/json"}], - Req:respond({StatusCode, ResponseHeader, jsx:encode(ResponseData)}). - - \ No newline at end of file diff --git a/apps/roster/src/rest/rest_message.erl b/apps/roster/src/rest/rest_message.erl deleted file mode 100644 index eb08c447bbdbfb4b87b9e885e92a63e33600e16f..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/rest_message.erl +++ /dev/null @@ -1,63 +0,0 @@ --module(rest_message). --include("roster.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --include_lib("kvs/include/metainfo.hrl"). --export([ - handle_request/3 -]). - -%%%============================================================================= -%%% API -%%%============================================================================= -handle_request('GET', #{user := Receiver}, Req) -> - {SenderType, Sender, Message} = parse_get(Req:parse_qs()), - try get_users([Sender, Receiver]) of - [{ok, SenderPhoneId}, {ok, ReceiverPhoneId} | _] -> - io:format("Sender: ~p~nReceiver: ~p~n", [SenderPhoneId, ReceiverPhoneId]), - {Code, Response} = - case rest_gw:add_as_friend(SenderType, SenderPhoneId, ReceiverPhoneId) of - ok -> - case rest_gw:send_p2p_message(SenderPhoneId, ReceiverPhoneId, Message) of - ok -> {?HTTP_CODE_200, rest_response_helper:success_response(<<"ok_message_sent">>, <<"Message send to: ", Receiver/binary>>)}; - {error, send_message} -> {?HTTP_CODE_400, rest_response_helper:error_response(<<"error_sending_message">>, <<"Error sending Message to: ", Receiver/binary>>)} - end; - friend_request -> - {?HTTP_CODE_200, rest_response_helper:error_response(<<"ok_request_friend">>, <<"Friend request send to: ", Receiver/binary>>)}; - {error, request_not_accepted} -> - {?HTTP_CODE_400, rest_response_helper:error_response(<<"error_request_not_accepted">>, <<"Friend request not accepted by: ", Receiver/binary>>)}; - {error, send_friend_request} -> - {?HTTP_CODE_400, rest_response_helper:error_response(<<"error_send_friend_request">>, <<"Error Request Friend/request with: ", Receiver/binary>>)}; - {error, confirm_friend_request} -> - {?HTTP_CODE_400, rest_response_helper:error_response(<<"error_confirm_friend_request">>, <<"Error Request Friends/confirm with: ", Receiver/binary>>)}; - {error, unexpected_error} -> - {?HTTP_CODE_400, rest_response_helper:error_response(<<"error_unexpected">>, <<"Unexpected Error with: ", Receiver/binary>>)} - end, - send_response(Code, Response, Req) - catch - {error, not_found, User} -> send_response(?HTTP_CODE_400, rest_response_helper:error_response(<<"error">>, <<"User not found: ", User/binary>>), Req); - {error, bad_uuid, User} -> send_response(?HTTP_CODE_400, rest_response_helper:error_response(<<"error">>, <<"Bad User UUID: ", User/binary>>), Req) - end. - -send_response(StatusCode, Response, Req) -> - ResponseHeader = [{"Content-Type", "application/json"}], - roster:info(?MODULE, "StatusCode: ~p / Response: ~p~n", [StatusCode, Response]), - Req:respond({StatusCode, ResponseHeader, Response}). - -parse_get(ReqParams) -> - Sender = list_to_binary(proplists:get_value("sender", ReqParams)), - SenderType = list_to_binary(proplists:get_value("sender_type", ReqParams, "")), - Message = list_to_binary(proplists:get_value("text", ReqParams, "")), - {SenderType, Sender, Message}. - -get_users(Users) -> - [get_user(User) || User <- Users]. - -get_user(RosterUUID) -> - case catch uuid:is_valid(binary_to_list(RosterUUID)) of - true -> - case micro:uuid_to_id('LinkRoster', RosterUUID) of - [] -> throw({error, not_found, RosterUUID}); - RosterId -> {ok, roster:phone_id(RosterId)} - end; - {'EXIT', _} -> throw({error, bad_uuid, RosterUUID}) - end. \ No newline at end of file diff --git a/apps/roster/src/rest/rest_metric.erl b/apps/roster/src/rest/rest_metric.erl deleted file mode 100644 index 96058662c4d1d54a77a1e58dee2795ee0e637c12..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/rest_metric.erl +++ /dev/null @@ -1,22 +0,0 @@ --module(rest_metric). --compile({parse_transform, lager_transform}). --include("roster.hrl"). --include_lib("roster/include/static/prometheus_var.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --export([handle_request/3]). - - -handle_request('GET', _, Req) -> - roster:info(?MODULE, "GET:~p", [Req:get(path)]), - prometheus_gauge:set(?METRIC_CHATS_TOTAL, [?METRIC_LABEL_P2P_CHAT], roster_db:p2p_stats()), - prometheus_gauge:set(?METRIC_CHATS_TOTAL, [?METRIC_LABEL_GROUP_CHAT], roster_db:room_stats()), - [prometheus_gauge:set(?METRIC_USERS_PER_COUNTRY, [Country], Value) || {Country, Value} <- roster_db:country_user_stats()], -% [prometheus_gauge:set(?METRIC_USERS_PER_COUNTRY_ONLINE, [Country], Value) || {Country, Value} <- roster_db:country_user_stats(online)], - [prometheus_gauge:set(?METRIC_MSGS_BY_TYPE_NMBR, [Mime], Value) || {Mime, Value} <- roster_db:msg_stats()], - [prometheus_gauge:set(?METRIC_SESSIONS_PER_COUNTRY_ONLINE, [Country], Value) || {Country, Value} <- roster_db:country_session_stats(online)], - rest_response_helper:response(Req, 200, prometheus_text_format:format(), "text/plain"); - -handle_request(_, _, Req) -> - rest_response_helper:error_405_response(Req). -%% roster:info(?MODULE, "MethodNotAllowed:~p:~p", [Method, Path]), -%% Req:respond({?HTTP_CODE_405, [], []}). \ No newline at end of file diff --git a/apps/roster/src/rest/rest_publish.erl b/apps/roster/src/rest/rest_publish.erl deleted file mode 100644 index e4c1464705c1cb495c363af7a14193f982c368e9..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/rest_publish.erl +++ /dev/null @@ -1,42 +0,0 @@ --module(rest_publish). --compile({parse_transform, lager_transform}). --include("roster.hrl"). --include_lib("roster/include/static/rest_text.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --export([description/0, handle_request/3]). - -description() -> "Publish any message to the topic". - -handle_request(Method, Path, Req) when Method == 'POST' -> - ReqBody = mochiweb_request:recv_body(Req), - roster:info(?MODULE, "~p:~p:Request:~p", [Method, Path, ReqBody]), - {ResponseStatus, Response} = - case jsx:is_json(ReqBody) of - false -> - {?HTTP_CODE_400, rest_response_helper:error_response(?ERROR_INVALID_JSON)}; - _ -> - #'PublishService'{message = Msg, topic = Topic, qos = Qos} = 'PublishServiceRest':from_json(jsx:decode(ReqBody), #'PublishService'{}), - case (Msg == [] orelse Topic == []) of - true -> - {?HTTP_CODE_400, rest_response_helper:error_response(?ERROR_MISSING_PARAM)}; - _ -> - case lists:member(Qos, lists:seq(0, 2)) of - false -> {?HTTP_CODE_400, rest_response_helper:error_response(?ERROR_INVALID_QOS_PARAM)}; - _ -> - case rest_main_helper:validate_msg_to_publish(Msg) of - false -> {?HTTP_CODE_400, rest_response_helper:error_response(?ERROR_INVALID_MESSAGE_PARAM)}; - DecodedMsg -> -%% publish to mqtt topic - n2o_vnode:send(rest_main_helper:rest_pid(), Topic, term_to_binary(DecodedMsg), [{qos, Qos}]), - {?HTTP_CODE_200, rest_response_helper:success_200()} - end - end - end - end, - roster:info(?MODULE, "~p:~p:Response:~p", [Method, Path, Response]), - rest_response_helper:response(Req, ResponseStatus, Response); - -handle_request(_, _, Req) -> - rest_response_helper:error_405_response(Req). -%% roster:info(?MODULE, "MethodNotAllowed:~p:~p", [Method, Path]), -%% rest_response_helper:response(Req, ?HTTP_CODE_405, rest_response_helper:error_405()). \ No newline at end of file diff --git a/apps/roster/src/rest/rest_push.erl b/apps/roster/src/rest/rest_push.erl deleted file mode 100644 index 21fb0cf1c5c924b0a42f3f859d9b41b632fb717e..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/rest_push.erl +++ /dev/null @@ -1,42 +0,0 @@ --module(rest_push). --compile({parse_transform, lager_transform}). --include("roster.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --export([handle_request/3]). - -%% NOTE! Link for API design https://nynjadev.atlassian.net/wiki/spaces/SER/pages/119799882/REST+API - -handle_request('POST', _, Req) -> - ReqBody = mochiweb_request:recv_body(Req), - roster:info(?MODULE, "~p:~p:Request:~p", [Req:get(method), Req:get(path), ReqBody]), - ResponseStatus = - case jsx:is_json(ReqBody) of - false -> - roster:info(?MODULE, "Error Invalid Json", []), - ?HTTP_CODE_400; - _ -> -%% "payload" field is json -%% 'PushServiceRest':from_json/1 method makes it erlang json ([{key, value}, ..., {keyN, valueN}]) -%% server should encode it to json again after mapping on PushServiceRest model - #'PushService'{payload = Payload, recipients = Recipient, module = Module} = 'PushServiceRest':from_json(jsx:decode(ReqBody), #'PushService'{}), -%% also server should make all values from string to binary because erlang works with binary() data - PushAlert = PushType = list_to_binary(Module), - [[n2o_async:pid(system, roster_push) ! {async_push, Auth, list_to_binary(Payload), PushAlert, PushType} - || Auth <- kvs:index('Auth', user_id, PhoneId)] || PhoneId <- list_elements_to_binary(Recipient)], - ?HTTP_CODE_200 - end, - roster:info(?MODULE, "~p:~p:Response:~p", [Req:get(method), Req:get(path), ResponseStatus]), - Req:respond({ResponseStatus, [], []}); - -handle_request(_, _, Req) -> - rest_response_helper:error_405_response(Req). -%% roster:info(?MODULE, "MethodNotAllowed:~p:~p", [Method, Path]), -%% Req:respond({?HTTP_CODE_405, [], []}). - -list_elements_to_binary(Data) -> - lists:foldl(fun(Val, Res) -> - case is_list(Val) of - false -> Res ++ [Val]; - _ -> Res ++ [list_to_binary(Val)] - end - end, [], Data). \ No newline at end of file diff --git a/apps/roster/src/rest/rest_session.erl b/apps/roster/src/rest/rest_session.erl deleted file mode 100644 index fada62335d2779eed7b0ae6c44a3bb57c8f9f7b0..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/rest_session.erl +++ /dev/null @@ -1,52 +0,0 @@ --module(rest_session). --compile({parse_transform, lager_transform}). --include("roster.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --export([handle_request/3]). - -%% TODO refactor me! - -handle_request(Method, Path, Req) when Method == 'GET' -> - roster:info(?MODULE, "~p:GET:~p", [Req:get(path), Req:parse_qs()]), - FirstQueryParamList = lists:sublist(Req:parse_qs(), 1), - {ResponseStatus, ResponseBody} = case length(FirstQueryParamList) of - L when L == 0 -> {?HTTP_CODE_400, rest_response_helper:error_response(<<"Empty query parameter. Please, add it in format: phone=RequestPhoneNumber">>)}; - _ -> - FirstQueryParam = lists:last(FirstQueryParamList), - case element(1, FirstQueryParam) of - "phone" -> - Phone = element(2, FirstQueryParam), - Sessions = kvs:index('Auth', phone, list_to_binary(Phone)), - case Sessions of - [] -> {?HTTP_CODE_400, rest_response_helper:error_response(<<"Cannot find sessions for requested number. Please, check input query parameters">>)}; - _ -> - R = iolist_to_binary([<<"[">>, - roster:binary_join( - lists:flatten([begin - jsx:encode('AuthRest':to_json(S#'Auth'{settings = - iolist_to_binary([<<"[">>, - roster:binary_join( - lists:flatten([begin - jsx:encode('FeatureRest':to_json(Feature)) - end || Feature <- FeaturesList]), - <<",">>), - <<"]">>])})) - end || #'Auth'{settings = FeaturesList} = S <- Sessions]), - <<",">>), - <<"]">>]), - {?HTTP_CODE_200, re:replace(re:replace(re:replace(re:replace(re:replace( - R, "\\\\\\\"", "\\\"", [{return, list}, global]), - "\\]\\\"", "\\]", [{return, list}, global]), - "\\\"\\[", "\\[", [{return, list}, global]), - "\\\"\\{", "\\{", [{return, list}, global]), - "\\}\\\"", "\\}", [{return, list}, global])} - end; - _ -> {?HTTP_CODE_400, rest_response_helper:error_response(<<"Invalid query parameter. Please, add it in format: phone=RequestPhoneNumber">>)} - end - end, - roster:info(?MODULE, "~p:~p:Response:~p", [Method, Path, ResponseBody]), - rest_response_helper:response(Req, ResponseStatus, ResponseBody); - -handle_request(Method, Path, Req) -> - roster:info(?MODULE, "MethodNotAllowed:~p:~p", [Method, Path]), - rest_response_helper:response(Req, ?HTTP_CODE_405, rest_response_helper:error_405()). \ No newline at end of file diff --git a/apps/roster/src/rest/rest_transcribe.erl b/apps/roster/src/rest/rest_transcribe.erl deleted file mode 100644 index dede61b152f406b9ff6cdd01d79645e02c706b38..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/rest_transcribe.erl +++ /dev/null @@ -1,84 +0,0 @@ -%%%----------------------------------------------------------------------------- -%%% @doc API module for transcription and translation of an audio message -%%% -%%% Here we expose a GET request for transcription of an audio message. -%%% The GET request requires from_lang (language of the audio message) -%%% and to_lang (language to transcribed text should be translated). -%%% -%%% @see rest_handler:handle_request/3 -%%% @end -%%%----------------------------------------------------------------------------- --module(rest_transcribe). --include("roster.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --include_lib("kvs/include/metainfo.hrl"). - --export([ - handle_request/3 -]). - -%%%============================================================================= -%%% API -%%%============================================================================= - -handle_request('GET', #{room := RoomId, message := MessageId}, Req) -> - ReqData = Req:parse_qs(), - roster:info(?MODULE, "~p:Request:~p:~p", [Req:get(method), Req:get(path), ReqData]), - - case parse_incomming_data(ReqData) of - {ok, Lang} -> - try - Message = get_message(string:to_integer(MessageId)), - io:format("Lang:~p~nMsg:~p~n", [Lang, MessageId]), - Transcription = jsx:encode([{<<"transcription">>, <<"hardcoded transcription of text">>}]), - send_response(Transcription, Req) - catch - throw:{msg_not_found, Id} -> - rest_response_helper:error_response(Req, ?HTTP_CODE_404, io_lib:format("Message with ID=~i not found", [Id])); - _:_ -> - rest_response_helper:error_response(Req, ?HTTP_CODE_404, "Something went wrong!") - end; - {error, wrong_data} -> - rest_response_helper:error_400_response(Req) - end; - -handle_request('GET', #{user := UserId, message := MessageId}, Req) -> - ReqData = Req:parse_qs(), - roster:info(?MODULE, "~p:Request:~p:~p", [Req:get(method), Req:get(path), ReqData]), - - case parse_incomming_data(ReqData) of - {ok, Lang} -> - try - Message = get_message(string:to_integer(MessageId)), - io:format("Lang:~p~nMsg:~p~n", [Lang, MessageId]), - Transcription = jsx:encode([{<<"transcription">>, <<"hardcoded transcription of text">>}]), - send_response(Transcription, Req) - catch - throw:{msg_not_found, Id} -> - rest_response_helper:error_response(Req, ?HTTP_CODE_404, io_lib:format("Message with ID=~i not found", [Id])); - _:_ -> - rest_response_helper:error_response(Req, ?HTTP_CODE_404, "Something went wrong!") - end; - {error, wrong_data} -> - rest_response_helper:error_400_response(Req) - end; - -handle_request(_, _, Req) -> - rest_response_helper:error_405_response(Req). - -send_response(Transcription, Req) -> - {ResponseStatus, ResponseData} = {?HTTP_CODE_200, Transcription}, - ResponseHeader = [{"Content-Type", "applicaition/json"}], - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - Req:respond({ResponseStatus, ResponseHeader, ResponseData}). - --spec parse_incomming_data(list(tuple())) -> {ok, term()} | {error, wrong_data}. -parse_incomming_data([{"lang", Lang}]) -> {ok, Lang}; -parse_incomming_data(_) -> {error, wrong_data}. - - -get_message({MsgId, []}) -> - case kvs:get('Message', MsgId) of - {ok, Msg} -> Msg; - {error, not_found} -> throw({msg_not_found, MsgId}) - end. \ No newline at end of file diff --git a/apps/roster/src/rest/rest_users.erl b/apps/roster/src/rest/rest_users.erl deleted file mode 100644 index 1ea97604400dcb4b07cb07d3ade3c09353987120..0000000000000000000000000000000000000000 --- a/apps/roster/src/rest/rest_users.erl +++ /dev/null @@ -1,84 +0,0 @@ --module(rest_users). --compile({parse_transform, lager_transform}). --include("roster.hrl"). --include("rest_static.hrl"). --include_lib("roster/include/static/rest_text.hrl"). --include_lib("roster/include/static/roster_text.hrl"). --include_lib("roster/include/static/roster_var.hrl"). --include_lib("roster/include/static/main_var.hrl"). - --export([ - description/0, - handle_request/3 -]). - -description() -> "Users Interface Module". - -handle_request('GET', _, Req) -> - ReqData = Req:parse_qs(), - roster:info(?MODULE, "~p:Request:~p:~p", [Req:get(method), Req:get(path), ReqData]), - Ordering = get_ordering_param(ReqData), - SizeCount = get_size_param(ReqData), - PageNumber = get_page_param(ReqData), - {Users, UsersCount, PagesCount} = get_users(Ordering, SizeCount, PageNumber), - {ResponseStatus, ResponseData} = {200, jsx:encode([ - {page_size, SizeCount}, - {page, PageNumber}, - {pages_total, PagesCount}, - {users_total, UsersCount}, - {users, Users}])}, - roster:info(?MODULE, "ResponseData:~p", [ResponseData]), - Req:respond({ResponseStatus, [], ResponseData}); - -handle_request(_, _, Req) -> - rest_response_helper:error_405_response(Req). -%% roster:info(?MODULE, "MethodNotAllowed:~p:~p", [Method, Path]), -%% Req:respond({405, [], []}). - -%% ------------------------------------------------------------------ -%% Users Local Helpers -%% ------------------------------------------------------------------ - -get_ordering_param(RequestQuery) -> - Param = "order", - DefaultValue = <<"desc">>, - case rest_main_helper:get_query_value(RequestQuery, Param) of - {ok, Val} when Val == "asc" -> nitro:to_binary(Val); - _ -> DefaultValue - end. - -get_size_param(RequestQuery) -> - Param = "page_size", - DefaultValue = 50, - - case rest_main_helper:get_query_value(RequestQuery, Param) of - {ok, Val} -> - try - ReqParam = binary_to_integer(nitro:to_binary(Val)), - if (ReqParam > DefaultValue) orelse (ReqParam =< 0) -> DefaultValue; true -> ReqParam end - catch _:_ -> DefaultValue end; - _ -> DefaultValue - end. - -get_page_param(RequestQuery) -> - Param = "page", - DefaultValue = 1, - - case rest_main_helper:get_query_value(RequestQuery, Param) of - {ok, Val} -> - try - ReqParam = binary_to_integer(nitro:to_binary(Val)), - if ReqParam > 0 -> ReqParam; true -> DefaultValue end - catch _:_ -> DefaultValue end; - _ -> DefaultValue - end. - -get_users(Ordering, PageSize, PageNumber) -> - Users = lists:keysort(#'Roster'.id, kvs:all('Roster')), - SortedUsers = if Ordering == <<"desc">> -> lists:reverse(Users); true -> Users end, - CuttedUsers = [[{id, UID}, {phone_number, UPhone}] || #'Roster'{id = UID, phone = UPhone} <- SortedUsers], - Offset = (PageNumber - 1) * PageSize, - TotalUsersNumber = length(CuttedUsers), - PaginatedUsers = if Offset >= TotalUsersNumber -> []; true -> roster_channel_helper:paginator(CuttedUsers, PageSize, Offset) end, - TotalPageNumbers = round(TotalUsersNumber / PageSize), - {PaginatedUsers, TotalUsersNumber, TotalPageNumbers}. \ No newline at end of file diff --git a/apps/roster/src/rest/rest_whitelist.erl b/apps/roster/src/rest/rest_whitelist.erl index 194373a3c17368ee11f3fc6381c01400d4174656..02b677ad3092898347d5afa915a2a140cb2e85ff 100644 --- a/apps/roster/src/rest/rest_whitelist.erl +++ b/apps/roster/src/rest/rest_whitelist.erl @@ -1,13 +1,16 @@ -module(rest_whitelist). -include("roster.hrl"). --include_lib("roster/include/static/rest_var.hrl"). --compile({parse_transform, lager_transform}). --export([handle_request/3]). + +-export([ whitelist_json/0 + , check_put_body/1 + , update_whitelist/1 + , delete_whitelist/1 + ]). %% TODO refactor me! -handle_request('GET', "/whitelist", Req) -> - roster:info(?MODULE, "~p:GET:~p", [Req:get(path), Req:parse_qs()]), + +whitelist_json() -> WhitelistRes = iolist_to_binary([<<"[">>, roster:binary_join( lists:flatten([begin @@ -20,96 +23,65 @@ handle_request('GET', "/whitelist", Req) -> jsx:encode([{<<"Status">>, <<"Success">>}, {<<"Data">>, WhitelistRes}]), "\\\\\\\"", "\\\"", [{return, list}, global]), "\\]\\\"", "\\]", [{return, list}, global]), "\\\"\\[", "\\[", [{return, list}, global]), - roster:info(?MODULE, "Res:~p", [WhitelistRes]), - Req:respond({?HTTP_CODE_200, [], Res}); + Res. -handle_request('PUT', "/whitelist", Req) -> - ReqBody = mochiweb_request:recv_body(Req), - roster:info(?MODULE, "~p:PUT:~p", [Req:get(path), ReqBody]), - Res = jsx:encode(case ReqBody of - <<>> -> - [{<<"Status">>, <<"Error">>}, {<<"Reason">>, - <<"Empty Body section. Please, add numbers in format: {\"phone\": [PhoneNumber1,PhoneNumber2]}">>}]; - _ -> - case jsx:is_json(ReqBody) of - false -> - [{<<"Status">>, <<"Error">>}, {<<"Reason">>, - <<"Incorrect Body section. Please, add numbers in format: {\"phone\": [PhoneNumber1,PhoneNumber2]}">>}]; - _ -> - PhoneElement = lists:keyfind(<<"phone">>, 1, jsx:decode(ReqBody)), - case lists:keyfind(<<"phone">>, 1, jsx:decode(ReqBody)) of - false -> - [{<<"Status">>, <<"Error">>}, {<<"Reason">>, - <<"Cannot find phones. Please, add numbers in format: {\"phone\": [PhoneNumber1,PhoneNumber2]}">>}]; - _ -> - Phones = element(2, PhoneElement), - case is_list(Phones) of - false -> - [{<<"Status">>, <<"Error">>}, {<<"Reason">>, - <<"Incorrect phones data format. Please, add numbers in format: {\"phone\": [PhoneNumber1,PhoneNumber2]}">>}]; - _ -> - case Phones of - [] -> - [{<<"Status">>, <<"Error">>}, {<<"Reason">>, - <<"Empty phones list. Please, add numbers in format: {\"phone\": [PhoneNumber1,PhoneNumber2]}">>}]; - _ -> - case lists:member(false, lists:flatten([begin is_integer(P) end || P <- Phones])) of - true -> - [{<<"Status">>, <<"Error">>}, {<<"Reason">>, - <<"Incorrect phone format. All phone numbers should be integers">>}]; - _ -> - Whitelist = kvs:all('Whitelist'), - [begin - BinPh = integer_to_binary(P), - case lists:keyfind(BinPh, #'Whitelist'.phone, Whitelist) of - false -> - kvs:put(#'Whitelist'{phone = BinPh, created = roster:now_msec()}); - _ -> - pass - end - end || P <- Phones], - [{<<"Status">>, <<"Success">>}] - end - end - end - end - end - end), - roster:info(?MODULE, "Res:~p", [Res]), - Req:respond({case string:str(binary_to_list(Res), "Error") of 0 -> ?HTTP_CODE_200; _ -> ?HTTP_CODE_400 end, [], Res}); +check_put_body(ReqBody) -> + case ReqBody of + <<>> -> + {error, <<"Empty Body section. Please, add numbers in format: {\"phone\": [PhoneNumber1,PhoneNumber2]}">>}; + _ -> + case jsx:is_json(ReqBody) of + false -> + {error, <<"Incorrect Body section. Please, add numbers in format: {\"phone\": [PhoneNumber1,PhoneNumber2]}">>}; + _ -> + PhoneElement = lists:keyfind(<<"phone">>, 1, jsx:decode(ReqBody)), + case lists:keyfind(<<"phone">>, 1, jsx:decode(ReqBody)) of + false -> + {error, <<"Cannot find phones. Please, add numbers in format: {\"phone\": [PhoneNumber1,PhoneNumber2]}">>}; + _ -> + Phones = element(2, PhoneElement), + case is_list(Phones) of + false -> + {error, <<"Incorrect phones data format. Please, add numbers in format: {\"phone\": [PhoneNumber1,PhoneNumber2]}">>}; + _ -> + case Phones of + [] -> + {error, <<"Empty phones list. Please, add numbers in format: {\"phone\": [PhoneNumber1,PhoneNumber2]}">>}; + _ -> + case lists:member(false, lists:flatten([begin is_integer(P) end || P <- Phones])) of + true -> + {error, <<"Incorrect phone format. All phone numbers should be integers">>}; + _ -> + {ok, Phones} + end + end + end + end + end + end. -handle_request('DELETE', "/whitelist", Req) -> - QueryParams = Req:parse_qs(), - roster:info(?MODULE, "~p:DELETE:~p", [Req:get(path), QueryParams]), - PhoneQueryParams = lists:keyfind("phone", 1, QueryParams), - RawRes = case lists:keyfind("phone", 1, QueryParams) of - false -> - [{<<"Status">>, <<"Error">>}, {<<"Reason">>, - <<"Cannot find phones in query params. Please, add it in format: phone=PhoneNumber1,PhoneNumber2">>}]; - _ -> - Whitelist = kvs:all('Whitelist'), - Data = lists:flatten([begin - BinPh = list_to_binary(P), - case lists:keyfind(BinPh, #'Whitelist'.phone, Whitelist) of - false -> - jsx:encode([{<<"Phone">>,BinPh},{<<"Description">>, <<"Cannot find in Whitelist">>}]); - _ -> - kvs:delete('Whitelist', BinPh), - roster:force_logout(BinPh), - jsx:encode([{<<"Phone">>,BinPh},{<<"Description">>, <<"Deleted from Whitelist">>}]) - end - end || P <- string:tokens(element(2, PhoneQueryParams), ",")]), - [{<<"Status">>, <<"Success">>}, {<<"Data">>, Data}] - end, - Res = re:replace(re:replace(re:replace(re:replace(re:replace( - jsx:encode(RawRes), "\\\\\\\"", "\\\"", [{return, list}, global]), - "\\]\\\"", "\\]", [{return, list}, global]), - "\\\"\\[", "\\[", [{return, list}, global]), - "\\\"\\{", "\\{", [{return, list}, global]), - "\\}\\\"", "\\}", [{return, list}, global]), - roster:info(?MODULE, "Res:~p", [Res]), - Req:respond({case string:str(Res, "Error") of 0 -> ?HTTP_CODE_200; _ -> ?HTTP_CODE_400 end, [], Res}); +update_whitelist(Phones) -> + Whitelist = kvs:all('Whitelist'), + lists:foreach(fun(P) -> + BinPh = integer_to_binary(P), + case lists:keyfind(BinPh, #'Whitelist'.phone, Whitelist) of + false -> + kvs:put(#'Whitelist'{phone = BinPh, created = roster:now_msec()}); + _ -> + pass + end + end, Phones). -handle_request(Method, Path, Req) -> - roster:info(?MODULE, "MethodNotAllowed:~p:~p", [Method, Path]), - rest_response_helper:response(Req, ?HTTP_CODE_405, rest_response_helper:error_405()). \ No newline at end of file +delete_whitelist(PhoneNumbers) -> + Whitelist = kvs:all('Whitelist'), + [begin + BinPh = iolist_to_binary(P), + case lists:keyfind(BinPh, #'Whitelist'.phone, Whitelist) of + false -> + [{<<"Phone">>,BinPh},{<<"Description">>, <<"Cannot find in Whitelist">>}]; + _ -> + kvs:delete('Whitelist', BinPh), + roster:force_logout(BinPh), + [{<<"Phone">>,BinPh},{<<"Description">>, <<"Deleted from Whitelist">>}] + end + end || P <- PhoneNumbers]. diff --git a/apps/roster/src/roster.app.src b/apps/roster/src/roster.app.src index e5ed158381e133b9a1950c109e602c6724111cfc..a66e684333888a26fba6e9d9ffc27ba29563f8f7 100644 --- a/apps/roster/src/roster.app.src +++ b/apps/roster/src/roster.app.src @@ -1,7 +1,12 @@ {application, roster, [{description, "Roster Protocol"}, - {vsn, "1"}, + {vsn, "1.0.1"}, {registered, []}, - {applications, [kernel,stdlib,mnesia,kvs,emqttd,n2o,bpe,locus,prometheus,libphonenumber_erlang]}, - {mod, { roster, []}}, + {applications, [kernel,stdlib, mnesia, crypto, inets, ssl, + ibrowse, cowboy, mochiweb, gen_smtp, + kvs, nitro, n2o, emqttc, emqttd, bpe, + jose, jsx, uuid, erlydtl, jwt, + migresia, mini_s3, qdate, rest, enenra, + locus, prometheus, libphonenumber_erlang]}, + {mod, {roster, []}}, {env, []} ]}. diff --git a/apps/roster/src/roster.erl b/apps/roster/src/roster.erl index 0b62d2e39428e6fad2b6f3f62101066770be7952..9f5c1b3a68ac8c7cb87bb419cf4f2c95e4d344eb 100644 --- a/apps/roster/src/roster.erl +++ b/apps/roster/src/roster.erl @@ -2,6 +2,7 @@ -behaviour(application). -compile(export_all). -export([start/2, stop/1, init/1]). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include("static_auth.hrl"). -include_lib("n2o/include/n2o.hrl"). @@ -31,12 +32,19 @@ log_modules() -> [ %% 3rd part api amazon_api, vox_api, telesign_api, email_api, google_api, prometheus_api, %% rest api - rest_handler, rest_session, rest_whitelist, rest_whitelist_admin, rest_push, rest_deeplink, rest_helpers, rest_publish, - rest_fake_numbers, rest_metric, rest_cri, rest_users, rest_gw, rest_message, -%% test staff + rest_helpers, + rest_cri, rest_gw, +%% test stuff main_test, auth_test, room_test, -%% other staff - signup, n2o, dashboard, stream, n2o_auth, n2o_vnode, macbert_javascript +%% other stuff + signup, n2o, dashboard, stream, n2o_auth, n2o_vnode, +%% cowboy rest interface + rest_cowboy_room_handler, rest_cowboy_fake_numbers_handler, + rest_cowboy_whitelist_handler, rest_cowboy_handler, + rest_cowboy_chat_handler, rest_cowboy_metric_handler, + rest_cowboy_push_handler, rest_cowboy_cri_handler, + rest_cowboy_session_handler, rest_cowboy_test_handler, + rest_cowboy_users_handler, rest_cowboy_publish_handler ]. register_acl_mod() -> @@ -49,42 +57,46 @@ register_acl_mod() -> atoms() -> [android, ios, contact, signup, signin, welcome]. init([]) -> {ok, {{one_for_one, 5, 10}, []}}. -start(_, _) -> atoms(), catch load([]), - application:stop(n2o), - application:start(n2o), - X = try begin - A = supervisor:start_link({local, roster}, roster, []), - emqttd_access_control:register_mod(auth, n2o_auth, [[]], 9997), - emqttd_access_control:register_mod(auth, roster_auth, [[]], 9998), - emqttd_access_control:register_mod(auth, micro_auth, [[]], 9996), - register_acl_mod(), - % roster_db:restore("mnesia.backup"), - roster_message:start(), - roster_friend:start(), - roster_roster:start(), - roster_profile:start(), - % roster_search:start(), - roster_auth:start(), - micro_auth:start(), - [M:start() || M <- application:get_env(?MODULE, start_modules, [])], - roster_bpe:start(), - rest_handler:init(), - roster_room:start(), - roster_history:start(), - roster_push:start(), - stickers_api:init_default_pack(), - init_default_fake_numbers(), - prometheus_api:init(), - sync_indexes(), - A end - catch E:Z -> - io:format("FATAL ROSTER START: ~p~n",[{E, Z, erlang:get_stacktrace()}]), - [] - end, +start(_, _) -> + atoms(), + try load([]) + catch + Error:Reason -> + ?LOG_WARNING("Error loading ~p: ~p~n",[Error, Reason]) + end, + A = supervisor:start_link({local, roster}, roster, []), + try + emqttd_access_control:register_mod(auth, n2o_auth, [[]], 9997), + emqttd_access_control:register_mod(auth, roster_auth, [[]], 9998), + emqttd_access_control:register_mod(auth, micro_auth, [[]], 9996), + register_acl_mod(), + % roster_db:restore("mnesia.backup"), + roster_message:start(), + roster_friend:start(), + roster_roster:start(), + roster_profile:start(), + % roster_search:start(), + roster_auth:start(), + micro_auth:start(), + [ M:start() || M <- application:get_env(?MODULE, start_modules, [])], + roster_bpe:start(), + rest_cowboy_handler:start(), + roster_room:start(), + roster_history:start(), + roster_push:start(), + stickers_api:init_default_pack(), + init_default_fake_numbers(), + prometheus_api:init(), + sync_indexes() + catch E:Z:ST -> + io:format("FATAL ROSTER START: ~p~n",[{E, Z, ST}]), + erlang:error("FATAL ROSTER START: ~p~n", [{E, Z}]), + {E, Z} + end, google_api:start(), - X. + A. -execution_time(StartTime) -> +execution_time(StartTime) -> TimeInMicroSec = (os:system_time() - StartTime)/1000, list_to_integer(float_to_list(TimeInMicroSec,[{decimals,0}])). @@ -120,24 +132,12 @@ tables() -> [ #table{name = 'ExtendedStar', fields = record_info(fields, 'ExtendedStar')} ]. -%% LOGGER - -info(Module, String, Args) -> roster_io:log(Module, String, Args, info). -info(String, Args) -> roster_io:log(?MODULE, String, Args, info). -info(String) -> roster_io:log(?MODULE, String, [], info). -warning(Module, String, Args) -> roster_io:log(Module, String, Args, warning). -warning(String, Args) -> roster_io:log(?MODULE, String, Args, warning). -warning(String) -> roster_io:log(?MODULE, String, [], warning). -error(Module, String, Args) -> roster_io:log(Module, String, Args, error). -error(String, Args) -> roster_io:log(?MODULE, String, Args, error). -error(String) -> roster_io:log(?MODULE, String, [], error). - % PROFILE MANAGEMENT delete_sessions(Index, P) -> - [begin + [begin n2o_vnode:unsubscribe(ClientId, roster:action_topic(ClientId)), - kvs:delete('Auth', ClientId) + kvs:delete('Auth', ClientId) end || #'Auth'{client_id = ClientId} <- kvs:index('Auth', Index, P)]. delete_sessions({client_id, ClientId}) -> @@ -152,7 +152,7 @@ full_purge_user(Phone) -> purge_user(Phone, full_remove_roster). purge_user(Phone) -> purge_user(Phone, remove_roster). purge_user(Phone, Fun) when is_atom(Fun) -> - info(?MODULE, "purge_user::~p", [Phone]), + ?LOG_INFO("purge_user::~p", [Phone]), R = case kvs:get('Profile', Phone) of {ok, #'Profile'{rosters = Accs}} -> [begin unsubscribe_p2p(Phone, Acc), ?MODULE:Fun(Phone, Acc) end || Acc <- Accs], @@ -166,7 +166,7 @@ list_rosters(Phone) -> list_rosters(Phone, compact_rosters). list_rosters(Phone, Fun) -> case kvs:get('Profile', Phone) of {error, _} -> - roster:error(?MODULE, "~p profile not found", [Phone]), + ?LOG_ERROR("~p profile not found", [Phone]), {error, profile_not_found}; {ok, #'Profile'{rosters = Rosters}} -> ?MODULE:Fun(Phone, Rosters) end. @@ -177,8 +177,8 @@ add_length_settings_maybe(Settings, Size, #'Roster'{userlist = Contacts, [] -> Settings; _ -> RealStars = [RealStar || RealStar = #'Star'{} <- Stars], - info(?MODULE, - "split_roster:TotalLength: ~p Contacts | ~p Rooms | ~p Stars", + ?LOG_INFO( + "split_roster:TotalLength: ~p Contacts | ~p Rooms | ~p Stars", [length(Contacts), length(Rooms), length(RealStars)]), Settings ++ [create_length_feature(contacts, length(Contacts))] @@ -189,16 +189,16 @@ add_length_settings_maybe(Settings, Size, #'Roster'{userlist = Contacts, create_length_feature(Key, Size) -> Type = atom_to_binary(Key, utf8), - #'Feature'{id = [], + #'Feature'{id = [], key = <>, value = list_to_binary(integer_to_list(Size)), group = <<"PAGINATION">>}. send_profile(#'Profile'{rosters = []}, _N, _LastSync, _ClientId, _ClientPid) -> - roster:info(?MODULE, "SEND PROFILE []", []), + ?LOG_INFO("SEND PROFILE []", []), ok; send_profile(#'Profile'{rosters = [[]|TRosterIds]} = Profile, N, LastSync, ClientId, ClientPid) -> - roster:info(?MODULE, "SEND PROFILE []", []), + ?LOG_INFO("SEND PROFILE []", []), send_profile(Profile#'Profile'{rosters = TRosterIds}, N, LastSync, ClientId, ClientPid); send_profile(#'Profile'{ rosters = [RosterId | TRosterIds] = Rosters, @@ -209,10 +209,10 @@ send_profile(#'Profile'{ LastSync, ClientId, ClientPid) -> - roster:info(?MODULE, "SEND PROFILE [~p]", [length(Rosters)]), + ?LOG_INFO("SEND PROFILE [~p]", [length(Rosters)]), case kvs:get('Roster', RosterId) of - {error, _} -> - roster:info(?MODULE, "roster ~p not found in profile ~p", [RosterId, Phone]); + {error, _} -> + ?LOG_INFO("roster ~p not found in profile ~p", [RosterId, Phone]); {ok, #'Roster'{} = Roster} -> ProfileWithNewSettings = Profile#'Profile'{settings = add_length_settings_maybe(Settings, N, Roster)}, NewRoster = Roster#'Roster'{ @@ -244,10 +244,10 @@ send_profile(#'Profile'{ LastSync, ClientId, ClientPid) -> - roster:info(?MODULE, "SEND PROFILE [~p]", [length(Rosters)]), + ?LOG_INFO("SEND PROFILE [~p]", [length(Rosters)]), case kvs:get('Roster', RosterId) of - {error, _} -> - roster:info(?MODULE, "roster ~p not found in profile ~p", [RosterId, Phone]); + {error, _} -> + ?LOG_INFO("roster ~p not found in profile ~p", [RosterId, Phone]); {ok, #'Roster'{} = Roster} -> ProfileWithNewSettings = Profile#'Profile'{settings = add_length_settings_maybe(Settings, N, Roster)}, Fun = fun(Objects) -> @@ -257,7 +257,7 @@ send_profile(#'Profile'{ skip -> ok; NewRoster -> - info(?MODULE, "Sending roster, length ~p", [length(NonEmptyOb)]), + ?LOG_INFO("Sending roster, length ~p", [length(NonEmptyOb)]), roster:send_action(ClientPid, ClientId, ProfileWithNewSettings#'Profile'{ @@ -293,7 +293,7 @@ create_roster([#'ExtendedStar'{} | _] = Stars, Roster) -> roomlist = [], favorite = Stars }; -create_roster([], Roster) -> +create_roster([],_Roster) -> skip. @@ -459,13 +459,14 @@ get_contact(#'Roster'{userlist = Cs}, FriendId) -> get_contact(PhoneId, FriendId) -> get_contact(roster_id(PhoneId), FriendId). to_contact(Rosters, Status) -> to_contact(Rosters, Status, []). -to_contact([#'Roster'{id = Id, names = Name, surnames = SName, nick = Nick, email = EM, +to_contact([#'Roster'{id = Id, names = Name, surnames = SName, nick = Nick, phone = Phone, avatar = A, status = S} | TRosters], Status, Contacts) -> Cs = Contacts ++ [case kvs:get('Profile', Phone) of {ok, #'Profile'{presence = Pr, services = Services, settings = Settings}} -> - Vox = case Services of [#'Service'{type = vox, login = V} | _] -> V;_ -> [] end, - PhoneId = phone_id(Phone, Id), - ContactSettings = lists:flatten(Settings ++ [roster_db:new_feature(PhoneId, account_id)]), + %% TODO: This looks like a bug. Why is Vox unused? + _Vox = case Services of [#'Service'{type = vox, login = V} | _] -> V;_ -> [] end, + PhoneId = phone_id(Phone, Id), + ContactSettings = lists:flatten(Settings ++ [roster_db:new_feature(PhoneId, account_id)]), #'Contact'{phone_id = PhoneId, names = Name, services = Services, nick = Nick, presence = Pr, settings = ContactSettings, surnames = SName, avatar = A, @@ -487,7 +488,7 @@ add_contacts(#'Roster'{userlist=List}=Roster, Contacts) -> [Phone,_]=parts_phone_id(PhoneId), - info(?MODULE, "ContactServices:~p",[{PhoneId,Phone}]), + ?LOG_INFO("ContactServices:~p",[{PhoneId,Phone}]), %% {ok, #'Profile'{services = Services}} = kvs:get('Phone', Phone), {A, [C#'Contact'{presence=[], services = get_services(Phone)}|L]}; _F -> {[PhoneId|A],L} end end,{[],List},Contacts), @@ -571,19 +572,19 @@ unload() -> emqttd:unhook('client.disconnected', fun ?MODULE:on_client_disconnected/3). on_client_disconnected(_Reason, #mqtt_client{client_id = <<"emqttd_", _/bytes>> = ClientId} = Client, _Env) -> - info(roster_auth, "~p:MQTT_CLIENT DISCONNECT ~p",[ClientId, Client]), + ?LOG_INFO("~p:MQTT_CLIENT DISCONNECT ~p",[ClientId, Client]), % TODO: mqttc:publish(C, lists:concat(["ses/",Phone])) on_disconnect(ClientId); on_client_disconnected(_Reason, #mqtt_client{client_id = <<"reg_", _/bytes>> = ClientId} = Client, _) -> - info(roster_auth, "~p:MQTT_CLIENT DISCONNECT ~p",[ClientId, Client]), + ?LOG_INFO("~p:MQTT_CLIENT DISCONNECT ~p",[ClientId, Client]), final_disconnect(ClientId); on_client_disconnected(_Reason, #mqtt_client{client_id = ClientId} = Client, _Env) -> - info(roster_auth, "~p:MQTT_CLIENT DISCONNECT ~p",[ClientId, Client]), + ?LOG_INFO("~p:MQTT_CLIENT DISCONNECT ~p",[ClientId, Client]), ok. on_disconnect(<<"emqttd_", _/binary>> = ClientId) -> - info(roster_auth, "~p:CLIENT DISCONNECT",[ClientId]), + ?LOG_INFO("~p:CLIENT DISCONNECT",[ClientId]), case kvs:get('Auth', ClientId) of {ok, Auth} -> n2o_async:pid(system, roster_auth) ! {disconnect, Auth#'Auth'{type = disconnect}}, @@ -592,7 +593,7 @@ on_disconnect(<<"emqttd_", _/binary>> = ClientId) -> end, ok. final_disconnect(ClientId) -> - % info(roster_auth, "~p:ANY DISCONNECT",[RegClientId]), + % ?LOG_INFO("~p:ANY DISCONNECT",[RegClientId]), n2o_vnode:unsubscribe(ClientId, roster:action_topic(ClientId)), case kvs:get(mqtt_session, ClientId) of {ok, #mqtt_session{sess_pid = SessionPid}} -> @@ -629,7 +630,7 @@ send_event(C, ClientId, Token, Term, {K,V}) -> send_event(C, ClientId, Token, Term, VNode) -> n2o_vnode:send(C, event_topic(ClientId, Token, VNode), term_to_binary(Term)). send_action(C, ClientId, Term) -> send_action(C, ClientId, Term, "1"). -send_action(C, ClientId, <<>>, _) -> <<>>; +send_action(_C,_ClientId, <<>>, _) -> <<>>; send_action(C, ClientId, Term, VSN) -> send(C, action_topic(ClientId, VSN), Term). @@ -674,7 +675,7 @@ update_writer(Feed,{E1,V1},{E2,V2})-> kvs_stream:save(W#writer{cache = M1}); false -> skip end; _ -> skip end. -update_chains({Table, Index},{E1,V1},{E2,V2}) -> +update_chains({Table, Index},{_E1,V1},{E2,V2}) -> [kvs:put(erlang:setelement(E2,M,V2)) || M <- kvs:index(Table, Index,V1)], ok. %%update_chain({, {Table, Index},{E1,V1},{E2,V2}) -> @@ -709,7 +710,7 @@ unread_msg(#writer{cache = #'Message'{id = MaxReadId, type = [sys|_]} = LastMsg} #'Member'{id = UID, status = removed}) when ReadMsgId /= [], ReadMsgId >= MaxReadId -> GetFun = - fun (_T, MaxReadId, _D) -> {ok, LastMsg#'Message'{seenby = []}}; + fun (_T, Id, _D) when Id =:= MaxReadId -> {ok, LastMsg#'Message'{seenby = []}}; (T, Id, D) -> kvs:get(T, Id, D) end, unread_msg(Writer#writer{cache = LastMsg}, Reader#reader{cache = {'Message', MaxReadId}}, UID, GetFun); @@ -738,7 +739,7 @@ unread_msg(Feed, #reader{} = Reader, UID) when is_record(Feed, muc); is_record(F unread_msg(kvs_stream:load_writer(Feed), Reader, UID); unread_msg(_F, [], _U) -> {0, [], []}; unread_msg(F, R, U) -> - info(?MODULE, "unread_msg2: unexpected data format: ~p", [{F, R, U}]), + ?LOG_INFO("unread_msg2: unexpected data format: ~p", [{F, R, U}]), {0, [], []}. unread_msg(#writer{cache = #'Message'{id = MaxReadId}, count = Count}, @@ -792,7 +793,7 @@ unread_msg(#writer{cache = #'Message'{id = MaxReadId}, count = Count}, link_msg(#'Message'{feed_id = Feed, type = [reply|_], link = LinkId}, UID) when is_integer(LinkId) -> case kvs:get('Message', LinkId) of {error, not_found} -> - roster:info(?MODULE, "link ~p for reply not found", [LinkId]), + ?LOG_INFO("link ~p for reply not found", [LinkId]), #error{code = invalid_data}; {ok, #'Message'{feed_id = Feed, status = [], type = T} = LnkMsg} when T == []; hd(T) /= sys -> @@ -886,7 +887,7 @@ gen_token(Token, Data) -> Now = now_msec(), case depicle(Token) of <<>> -> {error, invalid_token}; {Expiration, _} when Expiration > Now -> {'Token', Token}; - {Expiration, _} -> gen_token([], Data) + {_Expiration, _} -> gen_token([], Data) end. gen_token2(Token, Data) -> case gen_token(Token, Data) of @@ -897,14 +898,14 @@ parse_token(Token) -> case depicle(Token) of <<>> -> {error, invalid_token}; - {Expiration, D} when Expiration =< Now -> + {Expiration,_D} when Expiration =< Now -> {error, token_expired}; - {_, D} -> D + {_, D} -> D end. parse_token2(Token) -> case depicle(Token) of <<>> -> {error, invalid_token}; - {_, D} -> D + {_, D} -> D end. get_vnode(Term) -> [H|_] = binary_to_list(erlang:md5(term_to_binary(Term))), @@ -928,14 +929,14 @@ validate(#'Message'{mentioned = [_|_] = Mentioned} = Msg) -> validate(#'Message'{status = Status, files = [_|_] = Descs, type = []} = Msg) when Status == []; Status == edit -> case roster:verify_thumbs(Descs) of {[], []} -> validate(Msg#'Message'{files = []}); - _ -> roster:error(?MODULE, "invalid thumb or media desc: ~p", [Msg]), [{files, Msg}] + _ -> ?LOG_ERROR("invalid thumb or media desc: ~p", [Msg]), [{files, Msg}] end; validate(#'Message'{status = [], type = [sys|_]} = Msg) -> - info(?MODULE, "invalid sys type: ~p", [Msg]), [{type, Msg}]; + ?LOG_INFO("invalid sys type: ~p", [Msg]), [{type, Msg}]; validate(#'Message'{status = [], type = [_|_] = Types} = Msg) -> case [T||T<-Types, not is_atom(T)] of [] -> []; - _ -> info(?MODULE, "invalid message type: ~p", [Msg]), [{type, Msg}] + _ -> ?LOG_INFO("invalid message type: ~p", [Msg]), [{type, Msg}] end; validate(#'Desc'{id = <<>>} = Desc) -> [{id, Desc}]; validate(_) -> []. @@ -948,7 +949,7 @@ validate(Payload, ClientId) -> Term = prevalidate(binary_to_term(Payload)), case roster_validator:validate(Term) of [] -> ok; - Res -> info(?MODULE, "validate error:~n~p", [Res]), + Res -> ?LOG_INFO("validate error:~n~p", [Res]), emqttd:publish(emqttd_message:make( ClientId, 2, action_topic(ClientId), term_to_binary(#errors{code = [<<"666">>], data = Res}))), error @@ -1102,7 +1103,7 @@ put_readers(TopAction, #'Member'{id = Id, feed_id = #muc{name = RoomId}, phone_i #reader{cache = {'Message', RMsgId}} when MsgId > RMsgId -> [WMemberId, Id]; _ -> Readers end; _ -> Readers end; - Res -> info(roster_auth, "invalid put_readers:~p", [Res]), Readers + Res -> ?LOG_INFO("invalid put_readers:~p", [Res]), Readers end, Room2 = case Readers2 of Readers -> Room;_ -> kvs:put(R2 = Room#'Room'{readers = Readers2}), R2 end, @@ -1117,7 +1118,7 @@ put_readers(read_top, #'Contact'{phone_id = PhoneId}, #reader{cache = {'Message' {Msg#'Message'{type = [read], files = [], from = PhoneId}, []}; put_readers(_TopAction, #'Contact'{} = _C, _Reader) -> {[], []}; put_readers(T, M, R) -> - info(roster_auth, "invalid put_readers: ~p", [{T, M, R}]), {[], []}. + ?LOG_INFO("invalid put_readers: ~p", [{T, M, R}]), {[], []}. put_readers(TopAction, #'Member'{feed_id = #muc{name = R}} = M) -> {ok, Room} = kvs:get('Room', R), @@ -1153,7 +1154,7 @@ contact_readmsg(LocalPhoneId, PhoneId) -> {ok, #'Roster'{} = R}-> contact_readmsg(LocalPhoneId, R); _ -> 0 end. %% TODO need refactoring. Reader may be found by feed? -update_room(#'Roster'{roomlist = Rooms} = Roster, #'Room'{status = Status} = Room)-> +update_room(#'Roster'{} = Roster, #'Room'{} = Room)-> kvs:put(Roster#'Roster'{roomlist = lists:keyreplace(#'Room'.id, Room)}). update_rooms(#'Roster'{roomlist = Rooms} = Roster, #'Room'{status = Status} = R) -> @@ -1239,11 +1240,11 @@ last_objs([Obj|TObjs], PhoneId, LastSync, N, {Acc, MaxUpd}) -> {0, Feed} end, LastUpd = erlang:max(Created, last_upd(Obj)), - Acc2 = case LastUpd > LastSync of + Acc2 = case LastUpd > LastSync of true -> Acc++[{Obj, W}]; _ -> - Acc + Acc end, %% TODO sort objects if update? last_objs(TObjs, PhoneId, LastSync, N, {Acc2, erlang:max(MaxUpd, LastUpd)}). @@ -1262,8 +1263,8 @@ split_objlist(Index, #'Roster'{} = Roster, LastSync, N, Acc) split_objlist(element(Index, Roster), Roster, LastSync, N, Acc); split_objlist([], _Roster, _LastSync, _N, Acc) -> Acc; -split_objlist([_|_] = List, #'Roster'{}=Roster, LastSync, N, Acc) -> - %[ Objs | TObjs ] = +split_objlist([_|_] = List, #'Roster'{}=Roster, LastSync, N,_Acc) -> + %[ Objs | TObjs ] = objlist(List, Roster, LastSync, N); %split_objlist(TObjs, Roster, LastSync, N, Acc ++ [Objs]); split_objlist(Index, Id, LastSync, N, Acc) @@ -1274,8 +1275,8 @@ split_objlist(Index, Id, LastSync, N, Acc) {ok, R} -> split_objlist(Index, R, LastSync, N, Acc); _ -> - info(?MODULE, "roster ~p not found", [Id]), - {error, roster_not_found} + ?LOG_INFO("roster ~p not found", [Id]), + {error, roster_not_found} end. objlist(Index, Roster) -> @@ -1306,18 +1307,18 @@ objlist([Object | _] = Objects, end, [[]], Objects), - info(?MODULE, "Objlist args ~p ~p ~p", [LastSync, N, length(hd(Objs))]), + ?LOG_INFO("Objlist args ~p ~p ~p", [LastSync, N, length(hd(Objs))]), case Objs of [[] | TObjs] -> TObjs; [H | _ ] when length(H) =< N -> Fun(H), Objs; - _ -> + _ -> Objs end; objlist([_Obj | TObjs], #'Roster'{id = _Id} = Roster, LastSync, N, Fun) -> -% roster:info(?MODULE, "invalid object in roster ~p: ~p", [Id, Obj]), +% ?LOG_INFO("invalid object in roster ~p: ~p", [Id, Obj]), objlist(TObjs, #'Roster'{} = Roster, LastSync, N, Fun); objlist(Index, Id, LastSync, N, Fun) when Index == #'Roster'.userlist; Index == #'Roster'.roomlist; @@ -1349,7 +1350,7 @@ build_object(Object, Acc, maybe_build_and_add_object(Object, Acc, Roster, Feed, LastUpd, LastSync) end. -maybe_build_and_add_object(_Object, Acc, _Roster, _Writer, LastUpd, LastSync) +maybe_build_and_add_object(_Object, Acc, _Roster, _Writer, LastUpd, LastSync) when LastUpd =< LastSync -> Acc; maybe_build_and_add_object(#'Contact'{} = Object, [HAcc | TAcc], Roster, Writer, _LastUpd, LastSync) -> @@ -1387,7 +1388,7 @@ user(#'Roster'{ id = Id, phone = Phone } = Roster, {Presence, Update} = is_online2(PhoneId), Msgs = case catch p2p_readmsgs(Roster, C) of {'EXIT', {Err, _}} -> - n2o:error(?MODULE,"Catch:~p~n",[n2o:stack_trace(error, Err)]), + ?LOG_ERROR("Catch:~p~n",[n2o:stack_trace(error, Err)]), throw({error, {user, LocalPhoneId, PhoneId}}); Ms -> Ms end, @@ -1406,15 +1407,15 @@ user(#'Roster'{ id = Id, phone = Phone } = Roster, reader = Msgs, settings = Settings }; -user(#'Roster'{id = RosterId, userlist = Users, phone = Phone}=R, UserId, W, LastSync) -> +user(#'Roster'{id = RosterId, userlist = Users}=R, UserId, W, LastSync) -> case lists:keyfind(UserId, #'Contact'.phone_id, Users) of #'Contact'{} = C -> user(R, C, W, LastSync); - false -> info(?MODULE, "user ~p not found in list for users~p", [UserId, RosterId]), [] end; + false -> ?LOG_INFO("user ~p not found in list for users~p", [UserId, RosterId]), [] end; user(RosterId, UserId, W, LastSync) -> %% get Contact object from userlist, RosterId - acted user Roster.id, UserId - friend phone_id case kvs:get('Roster', RosterId) of {ok, #'Roster'{} = R} -> user(R, UserId, W, LastSync); - {error, _} -> info(?MODULE, "roster ~p not found", [RosterId]), [] end. + {error, _} -> ?LOG_INFO("roster ~p not found", [RosterId]), [] end. roomlist(Id) when is_integer(Id) -> roomlist(Id, []). @@ -1426,7 +1427,7 @@ roomlist(Id, N) -> case kvs:get('Roster', Id) of {ok, R} -> roomlist(R, N); _ -> room(Roster, Room) -> room(Roster, Room, [], 0). room(#'Roster'{} = Roster, #'Room'{id = Name} = Room, [], LastSync) -> room(Roster, Room, kvs_stream:load_writer(#muc{name = Name}), LastSync); -room(#'Roster'{id = Id, phone = Phone}, #'Room'{id = Name, last_msg = LMId, type = Type} = Room, +room(#'Roster'{id = Id, phone = Phone}, #'Room'{id = Name, last_msg = LMId} = Room, #writer{} = W, _LastSync) -> Local = phone_id(Phone, Id), case muc_member(Local, Name) of @@ -1440,7 +1441,7 @@ room(#'Roster'{id = Id, phone = Phone}, #'Room'{id = Name, last_msg = LMId, type {Unread, LastMsg, Mentions} = case unread_msg(Rec, Reader, Member) of {_, #'Message'{}, _} = Res -> Res; - Res -> {0,[],[]} + _Res -> {0,[],[]} end, %% {Unread, LastMsg = #'Message'{from = PMention, mentioned = Mentioned}, Mentions} @@ -1469,12 +1470,12 @@ room(#'Roster'{id = Id, phone = Phone}, #'Room'{id = Name, last_msg = LMId, type room(#'Roster'{id = RosterId, roomlist = Rooms} = R, RoomId, W, LastSync) -> case lists:keyfind(RoomId, #'Room'.id, Rooms) of #'Room'{} = Room -> room(R, Room, W, LastSync); - false -> info(?MODULE, "room ~p not found in roomlist ~p", [RoomId, RosterId]), [] end; + false -> ?LOG_INFO("room ~p not found in roomlist ~p", [RoomId, RosterId]), [] end; room(RosterId, RoomId, W, LastSync) -> %% get Room object from all rooms, RosterId - acted user, RoomId - room to be found case kvs:get('Roster', RosterId) of {ok, #'Roster'{} = R} -> room(R, RoomId, W, LastSync); - {error, _} -> info(?MODULE, "roster ~p not found", [RosterId]), [] end. + {error, _} -> ?LOG_INFO("roster ~p not found", [RosterId]), [] end. % STARRED MESSAGES MANAGEMENT @@ -1542,12 +1543,12 @@ roster(#'Roster'{} = Roster, N, LastSync) -> favorite = objlist(#'Roster'.favorite, Roster, LastSync, N) }; -roster(Id, N, LastSync) -> +roster(Id, N, LastSync) -> case kvs:get('Roster', Id) of {ok, Roster} -> roster(Roster, N, LastSync); _ -> - [] + [] end. sub_room(subscribe, #'Member'{status = removed}) -> []; @@ -1622,7 +1623,7 @@ is_shared_rooms_([#'Room'{id = Id, status = S1} | _], true; is_shared_rooms_([#'Room'{id = Id1} | Rooms1], [#'Room'{id = Id2} | _] = Rooms2) when Id1 < Id2 -> is_shared_rooms_(Rooms1, Rooms2); -is_shared_rooms_([#'Room'{id = Id1} | _] = Rooms1, [#'Room'{id = Id2} | Rooms2]) -> +is_shared_rooms_([#'Room'{} | _] = Rooms1, [#'Room'{} | Rooms2]) -> is_shared_rooms_(Rooms1, Rooms2). patch_room(#'Room'{update = Upd} = OldR, #'Room'{settings = Settings} = NewR) when Settings /= [] -> @@ -1639,7 +1640,7 @@ patch_room(#'Room'{} = OldR, #'Room'{data = Descs} = NewR) -> patch_member(OldM, NewM) -> patch_member(OldM, NewM, [id, container, feed_id, prev, next, feeds, phone_id, presence, reader, status]). -patch_member(#'Member'{phone_id = PhoneId, feed_id = Feed}=OldM, #'Member'{alias = Alias} = NewM, ExcludeFields) -> +patch_member(#'Member'{}=OldM, #'Member'{alias = Alias} = NewM, ExcludeFields) -> M = patch_record(ExcludeFields, OldM, NewM#'Member'{alias = Alias}, record_info(fields, 'Member')), M#'Member'{update = now_msec()}. @@ -1870,10 +1871,10 @@ presence(#'Contact'{phone_id = PhoneId} = C) -> {ok, #'Profile'{presence = Presence, update = Update}} -> C#'Contact'{presence = Presence, update = Update}; {error, _} -> - info(?MODULE, "invalid contact: ~p", [PhoneId]), C end. + ?LOG_INFO("invalid contact: ~p", [PhoneId]), C end. get_data_val(Key, Data) -> - case get_data(Key, Data) of + case get_data(Key, Data) of [] -> []; #'Feature'{value = Val} -> Val @@ -1883,7 +1884,7 @@ get_data(Key, #'Desc'{data = Data}) when not is_record(Data, 'Desc') -> get_data(Key, Data) -> case lists:keyfind(Key, #'Feature'.key, Data) of false -> []; - F -> F + F -> F end. set_data(Key, Val, Data) -> case get_data(Key, Data) of @@ -1921,10 +1922,10 @@ get_feed_data(#p2p{from = PartyA, to = PartyB} = Feed, PhoneId) when PartyA == P #writer{cache = #'Message'{id = LastId}} = kvs_stream:load_writer(roster:feed_key(Feed)), {PhoneId, ReaderId, LastId, get_contact(Friend, PhoneId)}; {error, Reason} -> - info(?MODULE, "History/get.ContactError:~p", [Reason]), + ?LOG_INFO("History/get.ContactError:~p", [Reason]), {error, invalid_data, [], []} end; get_feed_data(Feed, PhoneId) -> - info(?MODULE, "~p:History/get.FeedError:~p", [PhoneId, Feed]), + ?LOG_INFO("~p:History/get.FeedError:~p", [PhoneId, Feed]), {error, invalid_feed, [], []}. get_feed_msg(Feed) -> get_feed_msg(Feed, 0, []). @@ -1945,7 +1946,7 @@ get_reader(Feed, RosterId) -> #p2p{from = From, to = To} -> [Opp]=[P||P<-[From, To], roster_id(P) /= RosterId], #'Contact'{reader = Reader} = roster:get_contact(Opp, phone_id(RosterId)), Reader; - _ -> info(?MODULE, "FeedErrorOccured:~p", [Feed]), 0 + _ -> ?LOG_INFO("FeedErrorOccured:~p", [Feed]), 0 end. %% utils @@ -1965,7 +1966,7 @@ next_key(Table, CurrentId, Count) -> %% kvs utils %% TODO add to kvs fold(_, Acc, _, [], _, _) -> Acc; -fold(_, Acc, _, _, [], _) -> info(?MODULE, "uncertain direction", []), Acc; +fold(_, Acc, _, _, [], _) -> ?LOG_INFO("uncertain direction", []), Acc; fold(_, Acc, _, S, S, _) -> Acc; fold(Fun, Acc, Table, Start, Finish, Driver) -> Direction = case Start > Finish of true -> #iterator.next; false -> #iterator.prev end, @@ -1994,7 +1995,7 @@ fold(Fun, Acc, Table, Start, Finish, Driver, Direction, Count, GetFun) when Star {_, _} when Dir -> Acc2; _ -> fold(Fun, Acc2, Table, Prev, Finish, Driver, Direction, Count2) end; _ -> Acc end catch Err:Rea -> - n2o:error(?MODULE, "Catch fold2:~p~n", [n2o:stack_trace(Err, Rea)]), Acc end. + ?LOG_ERROR("Catch fold2:~p~n", [n2o:stack_trace(Err, Rea)]), Acc end. %%count(#reader{pos = P}, #writer{count = T}) -> T - P. @@ -2153,13 +2154,13 @@ start_vnodes() -> [n2o_pi:start(#pi{module=n2o_vnode,table=ring,sup=n2o,state=[] hard_restart_vnodes() -> [try n2o_pi:restart(ring, Pos) catch Err:Rea -> - n2o:error(?MODULE,"Catch stop vnode:~p~n",[n2o:stack_trace(Err, Rea)]), + ?LOG_ERROR("Catch stop vnode:~p~n",[n2o:stack_trace(Err, Rea)]), case n2o_pi:pid(ring, Pos) of Pid when is_pid(Pid) -> exit(Pid, kill), - roster:info(?MODULE, "~p virtual node has being killed (~p)", [Pos, Pid]), + ?LOG_INFO("~p virtual node has being killed (~p)", [Pos, Pid]), n2o_pi:start(#pi{module=n2o_vnode,table=ring,sup=n2o,state=[],name=Pos}); - Data -> roster:error(?MODULE, "invalid pid data : ~p", [Data]), + Data -> ?LOG_ERROR("invalid pid data : ~p", [Data]), {error,{not_pid,Data}} end end || {_, Pos} <- lists:zip(n2o:ring(), lists:seq(1, length(n2o:ring())))]. @@ -2270,8 +2271,8 @@ init_default_fake_numbers() -> {error, _} -> kvs:put(#'FakeNumbers'{phone = PhoneNumber, created = now_msec()}); _ -> ok end || PhoneNumber <- ?FAKE_NUMBERS], - info(?MODULE, "InitiatedDefaultFakeNumbers", []); - _ -> info(?MODULE, "FakeNumberTableExists", []) + ?LOG_INFO("InitiatedDefaultFakeNumbers", []); + _ -> ?LOG_INFO("FakeNumberTableExists", []) end. add_fake_number(PhoneNumber) when is_binary(PhoneNumber) -> @@ -2282,8 +2283,8 @@ add_fake_number(PhoneNumber) when is_binary(PhoneNumber) -> %% TEST API check(Module) -> case lists:all(fun(X) -> X == ok end, Module:suite()) of - true -> roster:info("ALL TESTS PASSED", []), true; - false -> roster:info("TEST ERRORS", []), false end. + true -> ?LOG_INFO("ALL TESTS PASSED", []), true; + false -> ?LOG_INFO("TEST ERRORS", []), false end. n2o_pid(Name) -> diff --git a/apps/roster/src/roster_client.erl b/apps/roster/src/roster_client.erl index 3912fb78ce44dab8ec31f4c63b47855c29522d10..ae94ab202a839561a04e321a68fde7964b38906f 100644 --- a/apps/roster/src/roster_client.erl +++ b/apps/roster/src/roster_client.erl @@ -1,5 +1,6 @@ -module(roster_client). -compile(export_all). +-include_lib("kernel/include/logger.hrl"). -include_lib("n2o/include/n2o.hrl"). -include_lib("emqttc/include/emqttc_packet.hrl"). -include("roster.hrl"). @@ -52,14 +53,14 @@ filter(Term, _) -> case Term of {send_push, _, _, _} -> skip; #'Message'{type = [sys | _], status = [], files = Files} = _Msg -> - roster:info(?MODULE, "system:~p", [case Files of [] -> []; _ -> + ?LOG_INFO("system:~p", [case Files of [] -> []; _ -> (hd(Files))#'Desc'.payload end]); %% ignore muc system messages #'Contact'{presence = Presence} when Presence == online; Presence == offline -> skip; %% ignore Contact presence #'Contact'{status = internal} -> skip; %% ignore Contact presence #'Member'{presence = Presence, status = Status} when Status /= patch andalso (Presence == online orelse Presence == offline) -> skip; %% ignore Member presence - _ -> % roster:info("Term value: ~p", [Term]), + _ -> % ?LOG_INFO("Term value: ~p", [Term]), send end. filter_friend(Term, State) -> case {Term, filter(Term, State)} of @@ -74,12 +75,12 @@ proc(init, #handler{name = ?SYS_REST_CLIENT, state = #state{receive_pid = Self} {ok, Async#handler{state = State#state{mqttc = C, client_id = ?SYS_REST_CLIENT}, seq = 0}}; proc(init, #handler{name = ?SYS_BRIDGE_TEST_CLIENT, state = #state{receive_pid = Self} = State} = Async) -> {ok, C} = emqttc:start_link([{client_id, ?SYS_BRIDGE_TEST_CLIENT}, {logger, {console, error}}, {reconnect, 5}]), - roster:info(?MODULE, "MICRO BRIDGE SIMULATOR PROC started", []), + ?LOG_INFO("MICRO BRIDGE SIMULATOR PROC started", []), register(sys_bridge, C), Self ! init, {ok, Async#handler{state = State#state{mqttc = whereis(sys_bridge), client_id = ?SYS_BRIDGE_TEST_CLIENT}, seq = 0}}; proc(init, #handler{name = ClientId, state = #state{mqtt_opts = Opts, receive_pid = Self, username = Username} = State} = Async) -> - roster:info(?MODULE, "ClientInit:~p\r", [ClientId]), + ?LOG_INFO("ClientInit:~p\r", [ClientId]), {ok, C} = emqttc:start_link([{client_id, ClientId}, {clean_sess, false}, {logger, {console, error}}, {username, Username}, {reconnect, 5}] ++ Opts), @@ -103,7 +104,7 @@ proc({publish, _, BinTerm}, #handler{state = #state{receive_pid = Self, last_res Self ! Term; _ -> skip end, {reply, [], H#'handler'{state = State#state{last_res = [Term | LastRes]}}}; proc(_Term, #handler{state = #state{mqttc = {error, _} = Err}} = H) -> - roster:info("mqttc error: ~p", [Err]), + ?LOG_INFO("mqttc error: ~p", [Err]), {reply, [], H}; proc({callback, CallbackFun}, #handler{state = State} = H) -> State2 = CallbackFun(State), @@ -112,11 +113,21 @@ proc(Term, #handler{state = #state{mqttc = C}} = H) -> roster:send_event(C, <<>>, <<>>, Term), {reply, [], H}. +-ifdef(REMOTE_NODE). + stop_vnodes() when ?HOST == ?LOC -> roster:stop_vnodes(), timer:sleep(1000); stop_vnodes() -> skip. start_vnodes() when ?HOST == ?LOC -> roster:start_vnodes(), timer:sleep(1000); start_vnodes() -> skip. +-else. + +stop_vnodes() -> skip. +start_vnodes() -> skip. + +-endif. + + gen_name(Name) -> gen_name(Name, iolist_to_binary(["DevKey_", Name])). gen_name(Name, DevKey) -> gen_name(Name, DevKey, "emqttd_"). gen_name_reg(Name) -> gen_name_reg(Name, iolist_to_binary(["DevKey_", Name])). @@ -165,7 +176,7 @@ test_info(#'History'{roster_id = Phone, feed = Feed, data = Data} = History) -> data = [Msg#'Message'{feed_id = feed(MsgFeed), from = roster:phone_id(From), to = roster:phone_id(To)} || #'Message'{feed_id = MsgFeed, from = From, to = To} = Msg <- Data]}, Phone); -test_info(#'Message'{from= From, to=To, feed_id = Feed, files = Data} = Message) -> +test_info(#'Message'{from= From, to=_To, feed_id =_Feed, files =_Data} = Message) -> test_info(roster_message, Message#'Message'{}, From). @@ -283,7 +294,7 @@ stop_connect(Host, CurrentId, Count, MaxCount) -> NextId when Count =< MaxCount -> {ok, #'Auth'{client_id = ClientId}} = kvs:get('Auth', NextId), stop_client(ClientId), - roster:info(?MODULE, "stop connect. Number of connects is ~p", [MaxCount -Count-1]), + ?LOG_INFO("stop connect. Number of connects is ~p", [MaxCount -Count-1]), stop_connect(Host, NextId, Count+1, MaxCount); NextId -> {NextId, Count} end. diff --git a/apps/roster/src/roster_data.erl b/apps/roster/src/roster_data.erl index a03632729ca5b907b314c53dee53755ebf0ee655..834f914870c225e0663aaf05d2d439d038b1b9be 100644 --- a/apps/roster/src/roster_data.erl +++ b/apps/roster/src/roster_data.erl @@ -1,6 +1,7 @@ -module(roster_data). -compile(export_all). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). @@ -131,7 +132,7 @@ friend_test_rand(N) when is_integer(N) -> friend_test_rand(RosterIds) -> try Rosters = [case R of #'Roster'{} -> R; _-> element(2, kvs:get('Roster', R)) end||R<-RosterIds], - Counter = length(Rosters), + _ = length(Rosters), %% Data = [{APhoneId, AClientId, AToken}, {BPhoneId, BClientId, BToken}] = start_rosters(Rosters), [APhoneId, BPhoneId] = [roster:phone_id(P, RosterId)||#'Roster'{phone = P, id = RosterId}<-Rosters], roster_client:test_info(roster_friend, #'Friend'{phone_id = APhoneId, friend_id = BPhoneId, status = request}, roster:phone(APhoneId)), @@ -143,7 +144,7 @@ friend_test_rand(RosterIds) -> %% roster_client:set_filer([AClientId, BClientId], filter), %% lists:map(fun roster_client:stop_client/1, ClientIds), ok catch Err:Rea -> - n2o:error(?MODULE, "Catch:~p~n", [n2o:stack_trace(Err, Rea)]), ok + ?LOG_ERROR("Catch:~p~n", [n2o:stack_trace(Err, Rea)]), ok end. get_roster_ids() -> @@ -190,7 +191,7 @@ create_room(RosterIds, Room, PageMembCount, Type) -> [roster_client:test_info(roster_room, #'Room'{status = add, id = Room, members = Mmbrs}, AdmPhone)|| Mmbrs <-Mmbrs2, Mmbrs /= []] %% roster_client:stop_client(AClientId) catch Err:Rea -> - n2o:error(?MODULE, "Catch:~p~n", [n2o:stack_trace(Err, Rea)]) + ?LOG_ERROR("Catch:~p~n", [n2o:stack_trace(Err, Rea)]) end, Room. create_channel() -> @@ -290,7 +291,7 @@ get_user_favorites(RosterId) -> case kvs:get('Roster', RosterId) of {ok, #'Roster'{favorite = FavList}} -> {length(FavList), FavList}; _ -> - roster:info(?MODULE, "RosterNotFound:~p", [RosterId]), + ?LOG_INFO("RosterNotFound:~p", [RosterId]), {0, []} end. diff --git a/apps/roster/src/roster_db.erl b/apps/roster/src/roster_db.erl index 50c2f46697875e34ba7a3a354ad81a0a5606baaa..a95ea7bec62fef43dc4347c012e3cdd3519524a0 100644 --- a/apps/roster/src/roster_db.erl +++ b/apps/roster/src/roster_db.erl @@ -1,6 +1,7 @@ -module(roster_db). -compile(export_all). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include("micro.hrl"). -include_lib("emqttd/include/emqttd.hrl"). @@ -36,7 +37,7 @@ fix_duration() -> #'Feature'{key = <<"DURATION">>, value = V}=F<-Data, is_integer(V)]. fix_duration2() -> - [M#'Message'{files = lists:keystore(Id, #'Desc'.id, Descs, + _ = [M#'Message'{files = lists:keystore(Id, #'Desc'.id, Descs, D#'Desc'{data = lists:keystore(FId, #'Feature'.id, Data, F#'Feature'{key = <<"DURATION">>})})} ||#'Message'{files = Descs}=M<-kvs:all('Message'), #'Desc'{id = Id, mime = <<"audio">>, data = Data} = D<-Descs, #'Feature'{key = <<"SIZE">>, id = FId}=F<-Data], @@ -53,7 +54,7 @@ fix_empty_desc_ids() -> find_invalid_rooms(Id) when is_integer(Id) -> find_invalid_rooms(Id, []). find_invalid_rooms(#'Roster'{roomlist = Rooms} = Roster, N) -> lists:flatten([case catch roster:room(Roster, R2) of - {'EXIT', _} -> false = R2; + {'EXIT', _} -> []; Room -> Room end || R2 = #'Room'{} <- case N of [] -> Rooms;_ -> roster:last_rooms(Rooms, 100) end]); @@ -73,7 +74,7 @@ check_desc_migration() -> delete_posttranslate_msg() -> [kvs:put(Msg#'Message'{files = lists:keydelete(Id, #'Desc'.id, Descs)}) ||#'Message'{files = Descs} = Msg<-kvs:all('Message'), - #'Desc'{mime = <<"posttranslate">>, id = Id} = D<- Descs]. + #'Desc'{mime = <<"posttranslate">>, id = Id} <- Descs]. del_rooms_with_one_msg() -> [[case kvs:get('Roster', roster:roster_id(PhoneId)) of @@ -82,7 +83,7 @@ del_rooms_with_one_msg() -> #'Message'{next = [], prev = []} -> roster:purge_room(To); _ -> [] end; _ -> [] end || PhoneId <- case Feed of #muc{} -> [From]; _ -> [From, To] end] - || Msg = #'Message'{id = Id, feed_id = Feed, from = From, to = To}<-kvs:all('Message')]. + || Msg = #'Message'{feed_id = Feed, from = From, to = To}<-kvs:all('Message')]. clean_msg_without_user() -> L = lists:flatten([ @@ -94,12 +95,12 @@ clean_msg_without_user() -> end; _ -> [] end - catch Err:Rea -> [] + catch _Err:_Rea -> [] end || PhoneId <- case Feed of #muc{} -> [From]; _ -> [From, To] end] - || Msg = #'Message'{id = Id, feed_id = Feed, from = From, to = To}<-kvs:all('Message')]), + || Msg = #'Message'{feed_id = Feed, from = From, to = To}<-kvs:all('Message')]), lists:ukeysort(#'Message'.created, sets:to_list(sets:from_list(L))). get_room_message(Muc) -> @@ -109,7 +110,7 @@ get_p2p_message(P2P) -> [Msg || Msg = #'Message'{feed_id = P2P2} <- kvs:all('Message'), P2P == P2P2]. get_entity(Feed, Fun) -> - #writer{cache=Last,first=First}= kvs_stream:load_writer(Feed), + #writer{cache=Last}= kvs_stream:load_writer(Feed), roster:entries(Fun, element(2,Last), element(1,Last), undefined). %fun(A, Acc) -> case element(2, A) > Limit of true -> Acc; false -> [A | Acc] end end) @@ -125,9 +126,9 @@ fix_empty_services() -> test_db_accounts() -> lists:flatten([case catch roster:roster(Id) of - {error, {user, {Local, PhoneId}} = Res} -> Res; - {error, {room, {PhoneId, Room}, Res} = R} -> R; - R -> [] + {error, {user, {_Local, _PhoneId}} = Res} -> Res; + {error, {room, {_PhoneId, _Room},_Res} = R} -> R; + _R -> [] end || #'Roster'{id = Id} <- kvs:all('Roster')]). purge_invalid_roooms() -> purge_invalid_roooms(10, []). @@ -161,9 +162,9 @@ fix_rosters_names() -> case kvs:get('Roster', RId = roster:roster_id(PhoneId)) of {ok, #'Roster'{names = Name, userlist = [#'Contact'{} | _]} = R} -> case {Name, RId} of - {<>, _} -> + {<<_, _/binary>>, _} -> {[Cont#'Contact'{names = Name} | Acc], R}; - {_, Id} -> NN = case CName of <> -> CName; _ -> + {_, Id} -> NN = case CName of <<_, _/binary>> -> CName; _ -> list_to_binary(random_string(lists:seq(1, 10))) end, {[Cont#'Contact'{names = NN} | Acc], R#'Roster'{names = NN}}; _ when is_binary(CName) -> @@ -174,7 +175,7 @@ fix_rosters_names() -> {[Cont#'Contact'{names = NN} | Acc], Rost} end; E -> %roster:purge_user(Phone), - roster:info(?MODULE, "Shit:~p", [E]), + ?LOG_INFO("Shit:~p", [E]), {Acc, Rost} end; (_, Res) -> Res end, {[], Roster}, L), @@ -329,16 +330,16 @@ search_room(Keyword) -> end, FilterFun = - fun(Table, NextId, Keyword) -> + fun(Table, NextId, KW) -> {ok, #'Room'{name = Name} = Next} = kvs:get(Table, NextId), - [KeyWLow, LowName] = [string:lowercase(Word) || Word <- [Keyword, Name]], + [KeyWLow, LowName] = [string:lowercase(Word) || Word <- [KW, Name]], case binary:match(LowName, [KeyWLow]) of nomatch -> []; _ -> [Next] end end, Res = search_data(Keyword, {mnesia:dirty_first('Room'), []}, 'Room', NextFun, FilterFun), T1 = erlang:timestamp(), - roster:info(?MODULE, "TIME for room search in ms:~p", [timer:now_diff(T1, T0) / 1000]), + ?LOG_INFO("TIME for room search in ms:~p", [timer:now_diff(T1, T0) / 1000]), Res. get_features() -> get_features([<<"image">>, <<"video">>, <<"thumb">>]). @@ -413,7 +414,7 @@ get_ips(Begin) -> Begin == hd(binary:split(IP, <<".">>))])). validate() -> - lists:flatten([validate_table(Table) || #table{name = Table}<-roster:tables(), Rec<-kvs:all(Table), + lists:flatten([validate_table(Table) || #table{name = Table}<-roster:tables(), not lists:member(Table, [chain, writer, reader])]). validate_table(Table) -> @@ -452,18 +453,14 @@ fix_jobs() -> [opt_put(J#'Job'{settings = case S of #'Feature'{} -> [S]; _-> S end}, J) || J = #'Job'{settings = S}<-kvs:all('Job')], ok. -find_msg_by_text(Text) when is_binary(Text) -> - [M||M=#'Message'{files = Descs}<-kvs:all('Message'), #'Desc'{mime = <<"text">>, payload = <>} <- Descs]; -find_msg_by_text(_) -> []. - broken_chains_feed(Tab) -> lists:flatten([ begin Feed=element(#iterator.feed_id,M), Id=element(#iterator.id,M), case kvs_stream:load_writer(Feed) of - #writer{cache = CE, first = FE} when (element(2,CE)==Id orelse element(2,FE)==Id) -> []; - #writer{cache = CE, first = FE} ->Feed; - %fix_chain(W), - _ -> kvs:remove(element(1,M),Id), Feed end end - || M <- kvs:all(Tab),(element(#iterator.prev,M)/= [] andalso element(#iterator.id,M) > element(#iterator.prev,M)) + #writer{cache = CE, first = FE} when (element(2,CE)==Id orelse element(2,FE)==Id) -> []; + #writer{cache =_CE, first =_FE} -> Feed; + %fix_chain(W), + _ -> kvs:remove(element(1,M),Id), Feed end end + || M <- kvs:all(Tab),(element(#iterator.prev,M)/= [] andalso element(#iterator.id,M) > element(#iterator.prev,M)) orelse (element(#iterator.next,M)/= [] andalso element(#iterator.id,M) < element(#iterator.next,M)) orelse element(#iterator.next,M)== [] orelse element(#iterator.prev,M)==[] ]) % del_invalid_messages() @@ -480,7 +477,7 @@ broken_chains_feed(Tab) -> set_prev(E,Start,N)-> Fun = fun(A, Acc) -> [A | Acc] end, - % roster:info(?MODULE, "Start:~p", [Start]), + % ?LOG_INFO("Start:~p", [Start]), case kvs:fold(Fun, [Start], element(1,E), element(2,Start), N, #iterator.next, #kvs{mod = store_mnesia}) of [H] -> H; [H1,H2|_] -> @@ -495,7 +492,7 @@ set_prev(E,Start,N)-> valid_chains({Tab,feed},Id,Acc) -> [Entity]=mnesia:dirty_read(Tab,Id), -% roster:info(?MODULE, "Start:~p", [Acc]), +% ?LOG_INFO("Start:~p", [Acc]), Feed=element(#iterator.feed_id, Entity), case lists:keyfind(Feed, 1, Acc) of {Feed,Ids} -> lists:keyreplace(Feed, 1, Acc, {Feed,[Id]++Ids}); @@ -507,7 +504,7 @@ valid_chains({Tab,feed},Id,Acc) -> valid_chains({Tab,Feed},Id,Acc) -> [Entity]=mnesia:dirty_read(Tab,Id), -% roster:info(?MODULE, "Start:~p", [Acc]), +% ?LOG_INFO("Start:~p", [Acc]), case element(#iterator.feed_id, Entity) of Feed ->[Id]++Acc; _ -> Acc @@ -516,38 +513,42 @@ valid_chains({Tab,Feed},Id,Acc) -> repair_chain(_,[],_) -> []; -repair_chain(Tab,[Id],N) -> [One]=mnesia:dirty_read(Tab,Id), - case kvs_stream:load_writer(element(#iterator.feed_id,One)) of - #writer{count=C}=W when C==1 -> One1=setelement(#iterator.next,One, []), - One2=setelement(#iterator.prev,One1, []), - kvs:put(W#writer{cache=One2, first=One2}),[One2]; - #writer{count=C}=W -> One1=setelement(#iterator.prev,One,[]), - kvs:put(W#writer{count=N, cache=One1}),[One1]; - _ -> kvs:remove(element(1,One),element(2,One)), [] - end; -repair_chain(Tab,[H|T]=NewL,N) -> +repair_chain(Tab,[Id],N) -> + [One]=mnesia:dirty_read(Tab,Id), + case kvs_stream:load_writer(element(#iterator.feed_id,One)) of + #writer{count=C}=W when C==1 -> + One1=setelement(#iterator.next,One, []), + One2=setelement(#iterator.prev,One1, []), + kvs:put(W#writer{cache=One2, first=One2}),[One2]; + #writer{}=W -> + One1=setelement(#iterator.prev,One,[]), + kvs:put(W#writer{count=N, cache=One1}),[One1]; + _ -> + kvs:remove(element(1,One),element(2,One)), [] + end; +repair_chain(Tab,[_H|T]=NewL,N) -> %NewL=[H|T]=lists:usort(List), - % roster:info(?MODULE, "Start:~p", [NewL]), + % ?LOG_INFO("Start:~p", [NewL]), Fun=fun(A,{AOut, []})-> {AOut++repair_chain(Tab,[A],N),[]}; (A,{AOut, [Ap|TIn]})-> - [E]=mnesia:dirty_read(Tab,A), - {E1,NEl}=case AOut of - [] -> - NA=setelement(#iterator.next,E, []), - kvs_stream:save((kvs_stream:load_writer(element(#iterator.feed_id,E)))#writer{first=NA}), - {NA, []}; - _ -> %[Elast]=mnesia:dirty_read(Tab,), - Next=lists:last(AOut), - {setelement(#iterator.next,E,Next), Next} - end, - %[Ep]=mnesia:dirty_read(Tab,Ap), - case (element(#iterator.prev,E)==Ap andalso element(#iterator.next,E)==NEl) of - true -> skip; - _ -> kvs:put(setelement(#iterator.prev,E1,Ap)) - end, - {AOut++[A],TIn} - end, - lists:foldl(Fun, {[],T}, NewL),ok. + [E]=mnesia:dirty_read(Tab,A), + {E1,NEl}=case AOut of + [] -> + NA=setelement(#iterator.next,E, []), + kvs_stream:save((kvs_stream:load_writer(element(#iterator.feed_id,E)))#writer{first=NA}), + {NA, []}; + _ -> %[Elast]=mnesia:dirty_read(Tab,), + Next=lists:last(AOut), + {setelement(#iterator.next,E,Next), Next} + end, + %[Ep]=mnesia:dirty_read(Tab,Ap), + case (element(#iterator.prev,E)==Ap andalso element(#iterator.next,E)==NEl) of + true -> skip; + _ -> kvs:put(setelement(#iterator.prev,E1,Ap)) + end, + {AOut++[A],TIn} + end, + lists:foldl(Fun, {[],T}, NewL),ok. dirty_foldl(Fun, Acc, E) -> dirty_foldl(Fun, Acc, element(1, E), E). dirty_foldl(Fun, Acc, Tab, E) -> @@ -598,14 +599,14 @@ backup(Schema, Tables, Node, File, Opts) when is_list(Tables) -> ok -> mnesia:deactivate_checkpoint(Name), ok; {error, Reason} = Err -> - roster:info(?MODULE, "~p", [Reason]), + ?LOG_INFO("~p", [Reason]), mnesia:deactivate_checkpoint(Name), Err end; {error, {"Cannot prepare checkpoint (replica not available)", [Table, _]}} -> backup(Schema, Tables -- [Table], Node, File, Opts); {error, Reason} = Err -> - roster:info(?MODULE, "~p", [Reason]), + ?LOG_INFO("~p", [Reason]), Err end. @@ -614,7 +615,7 @@ restore(File, Node, SkipTbs) -> application:stop(bpe), case mnesia:restore(File, [{skip_tables, SkipTbs}]) of {aborted, {no_exists, Tab}} -> restore(File, Node, [Tab | SkipTbs]); - {aborted, Reason} -> roster:info(?MODULE, "aborted backup restore from ~s by reason ~p", [File, Reason]), + {aborted, Reason} -> ?LOG_INFO("aborted backup restore from ~s by reason ~p", [File, Reason]), {error, Reason}; {atomic, Tabs} -> application:start(bpe), roster:restart_module(roster_bpe), @@ -740,13 +741,13 @@ broken_feeds(Opts) -> Map = lists:foldl(CollectMessagesByFeed, maps:new(), lists:sort(mnesia:dirty_all_keys('Message'))), Fun = fun(_Key, #{msg:= #'Message'{id = MsgId, feed_id = F}, status := error}, Acc) -> case lists:member("debug", Opts) of - true -> roster:error(?MODULE, "Feed: ~p~nMsgId: ~p~n", [F, MsgId]); + true -> ?LOG_ERROR("Feed: ~p~nMsgId: ~p~n", [F, MsgId]); false -> ok end, [#{feed => F, msg_id => MsgId} | Acc]; (_Key, #{msg:= #'Message'{feed_id = F}, status := ok}, Acc) -> case lists:member("debug", Opts) of - true -> roster:info(?MODULE, "Ok feed: ~p~n", [F]); + true -> ?LOG_INFO("Ok feed: ~p~n", [F]); false -> ok end, Acc @@ -833,7 +834,7 @@ fixed_messages (Opts)-> % find messages that actually needs update NeedsUpdateFn = fun(Msg) -> case kvs:get('Message',element(2,Msg)) of {ok,Msg} -> false; - {ok,Old} -> {true,Msg}; + {ok,_} -> {true,Msg}; Other -> erlang:error( Other) end end, lists:filtermap(NeedsUpdateFn,ReorderedMessages). @@ -850,7 +851,7 @@ fix_broken_feeds (Opts) -> loop_rosters() -> FirstId = mnesia:dirty_first('Roster'), loop_rosters(kvs:get('Roster', FirstId), [], []). -loop_rosters({error, not_found}, Acc, Ers) -> +loop_rosters({error, not_found},_Acc, Ers) -> %{lists:flatten(Acc), lists:filter(fun({_, Err}) -> Err =/= [] end, Ers)}; lists:flatten(Ers); loop_rosters({ok, #'Roster'{id = Id, userlist = Contacts} = R}, Acc, Ers) -> @@ -937,9 +938,9 @@ correct_numbers([Number | Rest], Acc) -> case lists:filter(fun(Code) -> case string:split(Number, Code) of [Number] -> false; - [<<>>, <<"0", GoodNumber/binary>>] -> true; - [<<>>, GoodNumber] -> true; - _ -> false + [<<>>, <<"0",_GoodNumber/binary>>] -> true; + [<<>>,_GoodNumber] -> true; + _ -> false end end, country_codes()) of [CountryCode] -> @@ -997,35 +998,12 @@ all_contacts([Rid | Rest], Acc) -> Filtered = lists:filter(fun(#'Contact'{phone_id = Pid}) -> Pid =/= roster:phone_id(Rid) end, Contacts), all_contacts(Rest, [Filtered | Acc]). -add_linkage([], Acc1, Acc2) -> - io:format("Acc1: ~p~nAcc2: ~p~n", [Acc1, Acc2]); -add_linkage([{PhoneMiss, PhoneHas} | Rest], Acc1, Acc2) -> - case kvs:index('LinkProfile', phone, PhoneHas) of - [#'LinkProfile'{id = P_UUID}] -> - LinkP = #'LinkProfile'{id = P_UUID, phone = PhoneMiss}, - {ok, #'Profile'{rosters = [RidMiss]}} = kvs:get('Profile', PhoneMiss), - {ok, #'Profile'{rosters = [RidHas]}} = kvs:get('Profile', PhoneHas), - case kvs:index('LinkRoster', phone_id, roster:phone_id(RidHas)) of - [#'LinkRoster'{id = R_UUID}] -> - LinkR = #'LinkRoster'{id = R_UUID, phone_id = roster:phone_id(RidMiss)}, - {struct, Json1} = json_rec:to_json(LinkP, roster_export), - {struct, Json2} = json_rec:to_json(LinkR, roster_export), - add_linkage(Rest, - [jsx:encode(Json1) | Acc1], - [jsx:encode(Json2) | Acc2]); - [] -> - io:format("Error LinkRoster ~p~n", [PhoneHas]) - end; - [] -> - io:format("ERROR Link Proile: ~p~n Acc: ~p~n", [PhoneHas, PhoneMiss]) - end. - read_link(FileName) -> case file:read_file(FileName) of {ok, Text} -> read_link(string:split(Text, ";", all), []); {error, _} = Reason -> - roster:info(?MODULE, "read_link file not read ~p", [Reason]), + ?LOG_INFO("read_link file not read ~p", [Reason]), [] end. @@ -1045,7 +1023,7 @@ loop_rooms() -> loop_rooms([], Acc, Ers) -> {lists:usort(lists:flatten(Acc)), lists:flatten(Ers)}; loop_rooms([RoomId | Rest], Acc, Ers) -> - {ok, R = #'Room'{admins = Admins, members = Members}} = kvs:get('Room', RoomId), + {ok, #'Room'{admins = Admins, members = Members}} = kvs:get('Room', RoomId), {NewAdmins, AErr} = loop_members(Admins, [], []), {NewMembers, MErr} = loop_members(Members, [], []), % Uncomment to save to mnesia diff --git a/apps/roster/src/roster_export.erl b/apps/roster/src/roster_export.erl deleted file mode 100644 index 0d2b71a8268f0f76ae33c1f7c1aab193618a909d..0000000000000000000000000000000000000000 --- a/apps/roster/src/roster_export.erl +++ /dev/null @@ -1,70 +0,0 @@ --module(roster_export). --include("roster.hrl"). --include("micro.hrl"). - --compile(export_all). --compile({parse_transform, exprecs}). --export_records(['Profile', 'Roster', 'Service', 'Feature', - 'Contact', 'Room', 'ExtendedStar', 'Tag', - 'Desc', 'Star', 'Message', p2p, muc, 'Member', 'LinkRoster', 'LinkProfile']). - -to_file(Table, File) -> - file:write_file(File, to_json(Table)). - -json(Rec, Acc) -> Acc++[to_json(Rec)]. - -to_json(Table, Fun, Acc) when is_atom(Table) -> - iolist_to_binary(mochijson2:encode(roster_db:fold(Fun, Acc, Table))). -to_json(Table, Id) -> - case kvs:get(Table, Id) of {ok, Rec} -> to_json(Rec); Err -> Err end. -to_json(#'Roster'{surnames = [_|_] = Surname} = R) -> - to_json(R#'Roster'{surnames = iolist_to_binary(Surname)}); -to_json(#'Roster'{names = [_|_] = Name} = R) -> - to_json(R#'Roster'{names = iolist_to_binary(Name)}); -to_json(#'Roster'{nick = [_|_] = Nick} = R) -> - to_json(R#'Roster'{nick = iolist_to_binary(Nick)}); -to_json(#'Roster'{favorite = [_|_]} = R) -> - to_json(R#'Roster'{favorite = []}); -to_json(#'Roster'{tags = T} = R) when is_list(T) -> - to_json(add_phone(R)); -to_json(#'Roster'{userlist = U} = R) when is_list(U) -> - to_json(add_profile_UUID(R)); -to_json(#'Roster'{roomlist = L} = R) when is_list(L) -> - to_json(add_roster_UUID(R)); -to_json(#'LinkProfile'{id = <<_:128>> = Id} = LP) -> - to_json(LP#'LinkProfile'{id = micro:norm_uuid(Id)}); -to_json(#'LinkRoster'{id = <<_:128>> = Id} = LP) -> - to_json(LP#'LinkRoster'{id = micro:norm_uuid(Id)}); -to_json(Table) when is_atom(Table) -> - to_json(Table, fun json/2, []); -to_json(Rec) when is_tuple(Rec), is_atom(element(1, Rec)) -> - json_rec:to_json(Rec, ?MODULE). - -add_phone(#'Roster'{phone = P} = R) -> - case kvs:get('Profile', P) of - {ok, #'Profile'{settings = S}} -> - Phone = - case roster:get_data_val(<<"PHONE">>, S) of - [] -> - case catch uuid:is_valid(binary_to_list(P)) of - true -> <<"">>; - {'EXIT', _} -> P - end; - Data -> Data - end, - R#'Roster'{tags = Phone}; - {error, not_found} -> R#'Roster'{tags = <<"">>} - end. - -add_profile_UUID(#'Roster'{phone = P} = R) -> - case kvs:index('LinkProfile', phone, P) of - [#'LinkProfile'{id = Id} | _] -> R#'Roster'{userlist = Id}; - [] -> R#'Roster'{userlist = <<"">>} - end. - -add_roster_UUID(#'Roster'{phone = P} = R) -> - case kvs:index('LinkRoster', phone_id, roster:phone_id(P)) of - [#'LinkRoster'{id = Id} | _] -> R#'Roster'{roomlist = Id}; - [] -> R#'Roster'{roomlist = <<"">>} - end. - diff --git a/apps/roster/src/roster_io.erl b/apps/roster/src/roster_io.erl deleted file mode 100644 index c06cb8b5ac360267c3a287068e78e7a20379a3ba..0000000000000000000000000000000000000000 --- a/apps/roster/src/roster_io.erl +++ /dev/null @@ -1,90 +0,0 @@ --module(roster_io). - --export([info/3, warning/3, error/3, log/4, log_modules/0, log_level/0, parse/1]). - --define(LOGGER, roster_io). - -%% -%% Main Logic -%% - -info(Module, String, Args) -> - io:format(format_message(Module, String), to_one_row(Args)). - -warning(Module, String, Args) -> - io:format(format_message(Module, String), Args). - -error(Module, String, Args) -> - io:format(format_message(error, Module, String), to_one_row(Args)). - -format_message(Module, String) -> - format_message([], Module, String). -format_message(error, Module, String) -> - format_message("\t***ERROR***\t", Module, String); -format_message(AttentionModificator, Module, String) -> - nitro:to_list([roster:timestamp_to_datetime(roster:now_msec()), "::", Module, ":", AttentionModificator, ":", String, "~n"]). - -log_modules() -> [?MODULE]. -log_level() -> info. - --define(LOG_MODULES, (application:get_env(roster,log_modules,roster))). --define(LOG_LEVEL, (application:get_env(roster,log_level,roster))). - -log_level(none) -> 3; -log_level(error) -> 2; -log_level(warning) -> 1; -log_level(_) -> 0. - -log(Module, String, Args, Fun) -> - case log_level(Fun) < log_level(?LOG_LEVEL:log_level()) of - true -> skip; - false -> case ?LOG_MODULES:log_modules() of - any -> ?LOGGER:Fun(Module, String, Args); - Allowed -> case lists:member(Module, Allowed) of - true -> ?LOGGER:Fun(Module, String, Args); - false -> skip end end end. -%% -%% Helpers -%% - -to_one_row(Args) -> - [case is_tuple(Arg) of - true -> tuple_to_string(Arg); - _ -> - case check_is_list_of_tuples(Arg) of - true -> re:replace([tuple_to_string(A) || A <- Arg], "\\n\\s+", "", [global,{return,list}]); - _ -> Arg end - end || Arg <- Args]. - -tuple_to_string(Arg) -> - re:replace(lists:flatten(io_lib:format("~p", [Arg])), "\\n\\s+", "", [global,{return,list}]). - -%% Parse new format of logged data -%% NOTE! Can be removed to other module - -parse(Data) -> -%% parse from string to tuple - case check_is_string(Data) of - false -> info(?MODULE, "Input data should be string", []); - _ -> - {ok, Ts, _} = erl_scan:string(dot_ended(Data)), - {ok, Tup} = erl_parse:parse_term(Ts), - Tup end. - -dot_ended(Data) -> -%% Check does input data contains dot at the end (required for erl_scan and erl_parse libs). If not - add it) - case lists:suffix(".", Data) of true -> Data; _ -> Data ++ "." end. - -check_is_string(Data) -> -%% check is input data a string (really string, not a list) - case is_list(Data) of false -> false; true -> case Data of [_] -> false; _ -> true end end. - -check_is_list_of_tuples(Data) -> -%% check is input data a list (really list, not a string) - case is_list(Data) of - false -> false; - true -> -%% check each element - is it tuple or not - ContainsOnlyTuples = sets:to_list(sets:from_list(lists:foldl(fun(El, Acc) -> case is_tuple(El) of true -> Acc ++ [true]; _ -> Acc ++ [false] end end, [], Data))), - case ContainsOnlyTuples of [true] -> true; _ -> false end - end. \ No newline at end of file diff --git a/apps/roster/src/roster_oam.erl b/apps/roster/src/roster_oam.erl new file mode 100644 index 0000000000000000000000000000000000000000..f18fed0346cde932022fa400e70921e8f99e1da2 --- /dev/null +++ b/apps/roster/src/roster_oam.erl @@ -0,0 +1,62 @@ +-module(roster_oam). + +-export([ node_running/0 + , disable/0 + , ensure_tables_loaded/0 + , revert_db_sync/1]). + +node_running() -> + lists:keymember(roster, 1, application:which_applications()). + +disable() -> + [application:stop(A) || A <- apps_to_stop()]. + +revert_db_sync(DelNode) -> + %% setting master nodes won't matter if the tables are already + %% loaded, but if the node has restarted and is hanging on table + %% load, it would break the deadlock. + mnesia:set_master_nodes(mnesia:system_info(db_nodes) -- [DelNode]), + ok = ensure_tables_loaded(), + mnesia:set_master_nodes([]), % resets master_nodes setting + [application:ensure_all_started(A) || A <- apps_to_restart()], + remove_replicas(DelNode). + +apps_to_stop() -> + lists:takewhile(fun(A) -> + A =/= mnesia + end, lists:reverse(applist())). + +apps_to_restart() -> + [mnesia|Rest] = lists:dropwhile(fun(A) -> + A =/= mnesia + end, applist()), + Rest. + +applist() -> + TopDir = filename:dirname(mnesia:system_info(directory)), + {ok, Bin} = file:read_file(filename:join(TopDir, ".applist")), + {ok, Ts,_} = erl_scan:string(binary_to_list(Bin)), + {ok, Apps} = erl_parse:parse_term(Ts ++ [{dot,1}]), + Apps. + +ensure_tables_loaded() -> + wait_for_tables(tabs(), 5000). + +wait_for_tables(Tabs, Timeout) -> + case mnesia:wait_for_tables(Tabs, Timeout) of + {timeout, Remain} -> + io:format("Timeout, tables remaining: ~p~n", [Remain]), + wait_for_tables(Remain, Timeout); + ok -> + ok + end. + +remove_replicas(DelNode) -> + lists:foreach( + fun(Tab) -> + mnesia:del_table_copy(Tab, DelNode) + end, tabs()). + +tabs() -> + mnesia:system_info(tables) -- [schema]. + diff --git a/apps/roster/src/roster_proto.erl b/apps/roster/src/roster_proto.erl index 1e2d3bdff188b6de998b6ad80d8b5da5b291e8ac..98f671450cc08f7cc8f6f36b82cda82a11be30da 100644 --- a/apps/roster/src/roster_proto.erl +++ b/apps/roster/src/roster_proto.erl @@ -1,4 +1,5 @@ -module(roster_proto). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). -include_lib("kvs/include/metainfo.hrl"). @@ -41,5 +42,5 @@ do_info(#'Auth'{} = Auth, Req, #cx{params = <<"sys_" , do_info(#'Auth'{} = Auth, Req, State) -> roster_auth :info(Auth, Req, State); do_info(Message, Req, State) -> - roster:info(?MODULE, "UNKNOWN:~p, ~p", [Message, State]), + ?LOG_INFO("UNKNOWN:~p, ~p", [Message, State]), {unknown, Message, Req, State}. \ No newline at end of file diff --git a/apps/roster/src/roster_rest.erl b/apps/roster/src/roster_rest.erl index 9204c55ca094a81eb2b1c0aee5cc6d73ef4186e5..c8c838c7565109e0da12ace546aa71f1dccf4021 100644 --- a/apps/roster/src/roster_rest.erl +++ b/apps/roster/src/roster_rest.erl @@ -1,19 +1,20 @@ -module(roster_rest). +-include_lib("kernel/include/logger.hrl"). -compile(export_all). send_request(Method, RequestData, HTTPOptions, Options) -> - roster:info(?MODULE, "Request {~p, ~p}~n~n", [Method, RequestData]), + ?LOG_INFO("Request {~p, ~p}~n~n", [Method, RequestData]), case httpc:request(Method, RequestData, HTTPOptions, Options) of {ok, {{_, 200, _}, _, Body}} -> {ok, Body}; - {ok, {{_, HTTPStatusCode, HTTPStatus}, _, Body} = Result} -> + {ok, {{_, HTTPStatusCode, HTTPStatus}, _, Body}} -> Err = {[{http_code, HTTPStatusCode}, {http_status, HTTPStatus}, {body, Body}]}, - roster:error(?MODULE, "Response ~p~n~n", [Err]), + ?LOG_ERROR("Response ~p~n~n", [Err]), {error, Err}; {error, Result} -> - roster:error(?MODULE, "Response ~p~n~n", [Result]), + ?LOG_ERROR("Response ~p~n~n", [Result]), {error, Result} end. test() -> - send_request(get, {"https://api.voximplant.com/platform_api/BindUser?account_id=1152724&api_key=94b9f0f0-7d93-4e63-9061-aef62c30182b&user_id=82428&application_name=videoconf.nynja.voximplant.com", []}, [], []). \ No newline at end of file + send_request(get, {"https://api.voximplant.com/platform_api/BindUser?account_id=1152724&api_key=94b9f0f0-7d93-4e63-9061-aef62c30182b&user_id=82428&application_name=videoconf.nynja.voximplant.com", []}, [], []). diff --git a/apps/roster/src/roster_validator.erl b/apps/roster/src/roster_validator.erl index ea5d1c818f74323a1d884f78e1ed29693fb2d005..648ed7360da46a7efd9e5ca7e075b8268b80ce58 100644 --- a/apps/roster/src/roster_validator.erl +++ b/apps/roster/src/roster_validator.erl @@ -1,982 +1,1215 @@ -module(roster_validator). --include_lib("bpe/include/bpe.hrl"). --include_lib("emqttd/include/emqttd.hrl"). --include_lib("kvs/include/feed.hrl"). --include_lib("kvs/include/group.hrl"). --include_lib("kvs/include/kvs.hrl"). + +-include("roster.hrl"). +-include("static_auth.hrl"). +-include_lib("n2o/include/n2o.hrl"). -include_lib("kvs/include/metainfo.hrl"). -include_lib("kvs/include/user.hrl"). --include_lib("n2o/include/n2o.hrl"). --include_lib("roster/include/roster.hrl"). --include_lib("roster/include/static_auth.hrl"). --compile(export_all). +-include_lib("kvs/include/group.hrl"). +-include_lib("emqttd/include/emqttd.hrl"). +-include_lib("mnesia/src/mnesia.hrl"). -custom_validate(_Obj) -> []. -validate(Obj) -> validate(Obj, [], application:get_env(bert, custom_validate, {?MODULE, custom_validate})). -validate(Obj, Acc, _) when is_atom(Obj) -> Acc; -validate(Obj, Acc, _) when is_integer(Obj) -> Acc; -validate(Obj, Acc, _) when is_binary(Obj) -> Acc; +-compile(export_all). +-define(COND_FUN(Cond), fun(Rec) when Cond -> true; (_) -> false end). +validate(Obj) -> validate(Obj, []). +validate(_, [{[_|_] , _R}|_] = Acc) -> {error, Acc}; +validate([], _) -> ok; +validate(Objs, [{[] , R}|T]) -> validate(Objs, [R|T]); +validate([{CondFun, _, []}|T], Acc) when is_function(CondFun) -> validate(T, Acc); +validate([{CondFun, Field, [Obj|TObjs]}|T], Acc) when is_function(CondFun) -> + case CondFun(Obj) of + true -> validate([{CondFun, Field, TObjs}|T], Acc); + false -> {error, [Field, Obj|Acc]} end; +validate([{CondFun, Field, Obj}|T], Acc) when is_function(CondFun) -> + case CondFun(Obj) of true -> validate(T, Acc); false -> {error, [Field, Obj|Acc]} end; +validate([{_Field, []}|T], Acc) -> validate(T, Acc); +validate([{Field, [Obj|TObjs]}|T], Acc) -> + case validate(Obj, [Field|Acc]) of + ok -> validate([{Field, TObjs}|T], Acc); + Err -> Err + end; -validate(D = #'writer'{id = Id, count = Count, cache = Cache, args = Args, first = First}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} -> Acc2; - {count,_} when is_integer(Count) -> Acc2; - {cache,_} when Cache==[] orelse is_tuple(Cache) -> Acc2; - {args,_} -> Acc2; - {first,_} when First==[] orelse is_tuple(First) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'writer'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'reader'{id = Id, pos = Pos, cache = Cache, args = Args, feed = Feed, dir = Dir}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} -> Acc2; - {pos,_} when Pos==[] orelse is_integer(Pos) -> Acc2; - {cache,_} when Cache==[] orelse is_integer(Cache) -> Acc2; - {args,_} -> Acc2; - {feed,_} -> Acc2; - {dir,_} when Dir==0 orelse Dir==1 -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'reader'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'cur'{id = Id, top = Top, bot = Bot, dir = Dir, reader = Reader, writer = Writer, args = Args}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} -> Acc2; - {top,_} when Top==[] orelse is_integer(Top) -> Acc2; - {bot,_} when Bot==[] orelse is_integer(Bot) -> Acc2; - {dir,_} when Dir==0 orelse Dir==1 -> Acc2; - {reader,_} when Reader==[] orelse is_tuple(Reader) -> Acc2; - {writer,_} when Writer==[] orelse is_tuple(Writer) -> Acc2; - {args,_} when is_list(Args) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) orelse is_integer(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{args, D}|Acc3] end, Acc2, Args); - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'cur'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'iter'{id = Id, container = Container, feed = Feed, next = Next, prev = Prev}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {container,_} when is_atom(Container) -> Acc2; - {feed,_} -> Acc2; - {next,_} when Next==[] orelse is_integer(Next) -> Acc2; - {prev,_} when Prev==[] orelse is_integer(Prev) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'iter'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'container'{id = Id, top = Top, rear = Rear, count = Count}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {top,_} when Top==[] orelse is_integer(Top) -> Acc2; - {rear,_} when Rear==[] orelse is_integer(Rear) -> Acc2; - {count,_} when is_integer(Count) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'container'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'iterator'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, feeds = Feeds}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {container,_} when is_atom(Container) -> Acc2; - {feed_id,_} -> Acc2; - {prev,_} when Prev==[] orelse is_integer(Prev) -> Acc2; - {next,_} when Next==[] orelse is_integer(Next) -> Acc2; - {feeds,_} when is_list(Feeds) -> []; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'iterator'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'log'{id = Id, top = Top, rear = Rear, count = Count}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {top,_} when Top==[] orelse is_integer(Top) -> Acc2; - {rear,_} when Rear==[] orelse is_integer(Rear) -> Acc2; - {count,_} when is_integer(Count) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'log'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'operation'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, feeds = Feeds}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {container,_} when is_atom(Container) -> Acc2; - {feed_id,_} -> Acc2; - {prev,_} when Prev==[] orelse is_integer(Prev) -> Acc2; - {next,_} when Next==[] orelse is_integer(Next) -> Acc2; - {feeds,_} when is_list(Feeds) -> []; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'operation'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'feed'{id = Id, top = Top, rear = Rear, count = Count}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {top,_} when Top==[] orelse is_integer(Top) -> Acc2; - {rear,_} when Rear==[] orelse is_integer(Rear) -> Acc2; - {count,_} when is_integer(Count) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'feed'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'task'{name = Name, module = Module, prompt = Prompt, roles = Roles}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when Name==[] orelse is_atom(Name) -> Acc2; - {module,_} when Module==[] orelse is_atom(Module) -> Acc2; - {prompt,_} when is_list(Prompt) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{prompt, D}|Acc3] end, Acc2, Prompt); - {roles,_} when is_binary(Roles) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'task'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'userTask'{name = Name, module = Module, prompt = Prompt, roles = Roles}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when Name==[] orelse is_atom(Name) -> Acc2; - {module,_} when Module==[] orelse is_atom(Module) -> Acc2; - {prompt,_} when is_list(Prompt) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{prompt, D}|Acc3] end, Acc2, Prompt); - {roles,_} when is_binary(Roles) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'userTask'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'serviceTask'{name = Name, module = Module, prompt = Prompt, roles = Roles}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when Name==[] orelse is_atom(Name) -> Acc2; - {module,_} when Module==[] orelse is_atom(Module) -> Acc2; - {prompt,_} when is_list(Prompt) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{prompt, D}|Acc3] end, Acc2, Prompt); - {roles,_} when is_binary(Roles) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'serviceTask'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'receiveTask'{name = Name, module = Module, prompt = Prompt, roles = Roles}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when Name==[] orelse is_atom(Name) -> Acc2; - {module,_} when Module==[] orelse is_atom(Module) -> Acc2; - {prompt,_} when is_list(Prompt) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{prompt, D}|Acc3] end, Acc2, Prompt); - {roles,_} when is_binary(Roles) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'receiveTask'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'messageEvent'{name = Name, module = Module, prompt = Prompt, payload = Payload, timeout = Timeout}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when Name==[] orelse is_atom(Name) -> Acc2; - {module,_} when Module==[] orelse is_atom(Module) -> Acc2; - {prompt,_} when is_list(Prompt) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{prompt, D}|Acc3] end, Acc2, Prompt); - {payload,_} when is_binary(Payload) -> Acc2; - {timeout,_} when is_tuple(Timeout) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'messageEvent'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'boundaryEvent'{name = Name, module = Module, prompt = Prompt, payload = Payload, timeout = Timeout, timeDate = TimeDate, timeDuration = TimeDuration, timeCycle = TimeCycle}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when Name==[] orelse is_atom(Name) -> Acc2; - {module,_} when Module==[] orelse is_atom(Module) -> Acc2; - {prompt,_} when is_list(Prompt) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{prompt, D}|Acc3] end, Acc2, Prompt); - {payload,_} when is_binary(Payload) -> Acc2; - {timeout,_} when is_tuple(Timeout) -> Acc2; - {timeDate,_} when is_binary(TimeDate) -> Acc2; - {timeDuration,_} when is_binary(TimeDuration) -> Acc2; - {timeCycle,_} when is_binary(TimeCycle) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'boundaryEvent'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'timeoutEvent'{name = Name, module = Module, prompt = Prompt, payload = Payload, timeout = Timeout, timeDate = TimeDate, timeDuration = TimeDuration, timeCycle = TimeCycle}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when Name==[] orelse is_atom(Name) -> Acc2; - {module,_} when Module==[] orelse is_atom(Module) -> Acc2; - {prompt,_} when is_list(Prompt) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{prompt, D}|Acc3] end, Acc2, Prompt); - {payload,_} when Payload==[] orelse is_binary(Payload) -> Acc2; - {timeout,_} when Timeout==[] orelse is_tuple(Timeout) -> Acc2; - {timeDate,_} when TimeDate==[] orelse is_binary(TimeDate) -> Acc2; - {timeDuration,_} when TimeDuration==[] orelse is_binary(TimeDuration) -> Acc2; - {timeCycle,_} when TimeCycle==[] orelse is_binary(TimeCycle) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'timeoutEvent'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'beginEvent'{name = Name, module = Module, prompt = Prompt}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when Name==[] orelse is_atom(Name) -> Acc2; - {module,_} when Module==[] orelse is_atom(Module) -> Acc2; - {prompt,_} when is_list(Prompt) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{prompt, D}|Acc3] end, Acc2, Prompt); - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'beginEvent'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'endEvent'{name = Name, module = Module, prompt = Prompt}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when Name==[] orelse is_atom(Name) -> Acc2; - {module,_} when Module==[] orelse is_atom(Module) -> Acc2; - {prompt,_} when is_list(Prompt) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{prompt, D}|Acc3] end, Acc2, Prompt); - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'endEvent'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'sequenceFlow'{source = Source, target = Target}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {source,_} when Source==[] orelse is_atom(Source) -> Acc2; - {target,_} when Target==[] orelse is_atom(Target) orelse is_list(Target) -> - lists:foldl(fun(Tmp, Acc3) when is_atom(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{target, D}|Acc3] end, Acc2, Target); - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'sequenceFlow'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'hist'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, feeds = Feeds, name = Name, task = Task, docs = Docs, time = Time}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {container,_} when is_atom(Container) -> Acc2; - {feed_id,_} -> Acc2; - {prev,_} when Prev==[] orelse is_integer(Prev) -> Acc2; - {next,_} when Next==[] orelse is_integer(Next) -> Acc2; - {feeds,_} when is_list(Feeds) -> []; - {name,_} when Name==[] orelse is_binary(Name) -> Acc2; - {task,_} when is_atom(Task) -> Acc2; - {docs,_} when is_list(Docs) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{docs, D}|Acc3] end, Acc2, Docs); - {time,_} -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'hist'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'process'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, feeds = Feeds, name = Name, roles = Roles, tasks = Tasks, events = Events, hist = Hist, flows = Flows, rules = Rules, docs = Docs, options = Options, task = Task, timer = Timer, notifications = Notifications, result = Result, started = Started, beginEvent = BeginEvent, endEvent = EndEvent}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {container,_} when is_atom(Container) -> Acc2; - {feed_id,_} -> Acc2; - {prev,_} when Prev==[] orelse is_integer(Prev) -> Acc2; - {next,_} when Next==[] orelse is_integer(Next) -> Acc2; - {feeds,_} when is_list(Feeds) -> []; - {name,_} when Name==[] orelse is_binary(Name) -> Acc2; - {roles,_} when is_list(Roles) -> []; - {tasks,_} when is_list(Tasks) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'task') orelse is_record(Tmp,'serviceTask') orelse is_record(Tmp,'userTask') orelse is_record(Tmp,'receiveTask') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{tasks, D}|Acc3] end, Acc2, Tasks); - {events,_} when is_list(Events) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'messageEvent') orelse is_record(Tmp,'boundaryEvent') orelse is_record(Tmp,'timeoutEvent') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{events, D}|Acc3] end, Acc2, Events); - - {flows,_} when is_list(Flows) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'sequenceFlow') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{flows, D}|Acc3] end, Acc2, Flows); - - {docs,_} when is_list(Docs) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{docs, D}|Acc3] end, Acc2, Docs); - {options,_} -> Acc2; - {task,_} when Task==[] orelse is_atom(Task) -> Acc2; - {timer,_} when Timer==[] orelse is_binary(Timer) -> Acc2; - {notifications,_} when Notifications==[] orelse true -> Acc2; - {result,_} when Result==[] orelse is_binary(Result) -> Acc2; - {started,_} when Started==[] orelse is_tuple(Started) -> Acc2; - {beginEvent,_} when BeginEvent==[] orelse is_atom(BeginEvent) -> Acc2; - {endEvent,_} when EndEvent==[] orelse is_atom(EndEvent) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'process'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'complete'{id = Id}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'complete'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'proc'{id = Id}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'proc'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'load'{id = Id}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'load'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'histo'{id = Id}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'histo'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'create'{proc = Proc, docs = Docs}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {proc,_} when Proc==[] orelse is_record(Proc,'process') orelse is_binary(Proc) -> Acc2; - {docs,_} when Docs==[] orelse is_list(Docs) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{docs, D}|Acc3] end, Acc2, Docs); - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'create'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'amend'{id = Id, docs = Docs}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {docs,_} when Docs==[] orelse is_list(Docs) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{docs, D}|Acc3] end, Acc2, Docs); - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'amend'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'chain'{id = Id, top = Top, rear = Rear, count = Count}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {top,_} when Top==[] orelse is_integer(Top) -> Acc2; - {rear,_} when Rear==[] orelse is_integer(Rear) -> Acc2; - {count,_} when is_integer(Count) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'chain'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'push'{model = Model, type = Type, title = Title, alert = Alert, badge = Badge, sound = Sound}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {model,_} when Model==[] orelse true -> Acc2; - {type,_} when Type==[] orelse is_binary(Type) -> Acc2; - {title,_} when Title==[] orelse is_binary(Title) -> Acc2; - {alert,_} when Alert==[] orelse is_binary(Alert) -> Acc2; - {badge,_} when Badge==[] orelse is_integer(Badge) -> Acc2; - {sound,_} when Sound==[] orelse is_binary(Sound) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'push'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Search'{id = Id, ref = Ref, field = Field, type = Type, value = Value, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when is_integer(Id) orelse is_binary(Id) -> Acc2; - {ref,_} when is_binary(Ref) -> Acc2; - {field,_} when is_binary(Field) -> Acc2; - {type,_} when Type=='==' orelse Type=='!=' orelse Type=='like' -> Acc2; - {value,_} when is_list(Value) -> - lists:foldl(fun(Tmp, Acc3) when true orelse is_binary(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{value, D}|Acc3] end, Acc2, Value); - {status,_} when Status=='profile' orelse Status=='roster' orelse Status=='contact' orelse Status=='member' orelse Status=='room' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Search'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'p2p'{from = From, to = To}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {from,_} when is_binary(From) -> Acc2; - {to,_} when is_binary(To) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'p2p'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'muc'{name = Name}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when is_binary(Name) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'muc'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'mqi'{feed_id = Feed_id, query = Query, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {feed_id, #'muc'{}} -> Acc2; - {query,_} when Query==[] orelse is_binary(Query) -> Acc2; - {status,_} when Status==[] orelse Status=='admin' orelse Status=='member' orelse Status=='removed' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'mqi'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Feature'{id = Id, key = Key, value = Value, group = Group}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_binary(Id) -> Acc2; - {key,_} when is_binary(Key) -> Acc2; - {value,_} when is_binary(Value) -> Acc2; - {group,_} when is_binary(Group) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Feature'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Service'{id = Id, type = Type, data = Data, login = Login, password = Password, expiration = Expiration, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_binary(Id) -> Acc2; - {type,_} when Type=='email' orelse Type=='vox' orelse Type=='aws' orelse Type=='wallet' -> Acc2; - {data,_} -> Acc2; - {login,_} when Login==[] orelse is_binary(Login) -> Acc2; - {password,_} when Password==[] orelse is_binary(Password) -> Acc2; - {expiration,_} when Expiration==[] orelse is_integer(Expiration) -> Acc2; - {status,_} when Status==[] orelse Status=='verified' orelse Status=='added' orelse Status=='add' orelse Status=='remove' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Service'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Member'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, feeds = Feeds, phone_id = Phone_id, avatar = Avatar, names = Names, surnames = Surnames, alias = Alias, reader = Reader, update = Update, settings = Settings, services = Services, presence = Presence, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {container,_} when Container=='chain' orelse Container=='cur' orelse Container==[] -> Acc2; - {feed_id,_} when is_record(Feed_id,'muc') orelse is_record(Feed_id,'p2p') orelse Feed_id==[] -> Acc2; - {prev,_} when Prev==[] orelse is_integer(Prev) -> Acc2; - {next,_} when Next==[] orelse is_integer(Next) -> Acc2; - {feeds,_} when is_list(Feeds) -> []; - {phone_id,_} when Phone_id==[] orelse is_binary(Phone_id) -> Acc2; - {avatar,_} when Avatar==[] orelse is_binary(Avatar) -> Acc2; - {names,_} when Names==[] orelse is_binary(Names) -> Acc2; - {surnames,_} when Surnames==[] orelse is_binary(Surnames) -> Acc2; - {alias,_} when Alias==[] orelse is_binary(Alias) -> Acc2; - {reader,_} when Reader==[] orelse is_integer(Reader) -> Acc2; - {update,_} when Update==[] orelse is_integer(Update) -> Acc2; - {settings,_} when Settings==[] orelse is_list(Settings) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Feature') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{settings, D}|Acc3] end, Acc2, Settings); - {services,_} when Services==[] orelse is_list(Services) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Service') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{services, D}|Acc3] end, Acc2, Services); - {presence,_} when Presence==[] orelse Presence=='online' orelse Presence=='offline' -> Acc2; - {status,_} when Status==[] orelse Status=='admin' orelse Status=='member' orelse Status=='removed' orelse Status=='patch' orelse Status=='owner' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Member'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Desc'{id = Id, mime = Mime, payload = Payload, parentid = Parentid, data = Data}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_binary(Id) -> Acc2; - {mime,_} when Mime==[] orelse is_binary(Mime) -> Acc2; - {payload,_} when Payload==[] orelse is_binary(Payload) -> Acc2; - {parentid,_} when Parentid==[] orelse is_binary(Parentid) -> Acc2; - {data,_} when is_list(Data) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Feature') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{data, D}|Acc3] end, Acc2, Data); - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Desc'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'StickerPack'{id = Id, name = Name, keywords = Keywords, description = Description, author = Author, stickers = Stickers, created = Created, updated = Updated, downloaded = Downloaded}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {name,_} when Name==[] orelse is_binary(Name) -> Acc2; - {keywords,_} when is_list(Keywords) -> - lists:foldl(fun(Tmp, Acc3) when is_binary(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{keywords, D}|Acc3] end, Acc2, Keywords); - {description,_} when Description==[] orelse is_binary(Description) -> Acc2; - {author,_} when Author==[] orelse is_binary(Author) -> Acc2; - {stickers,_} when is_list(Stickers) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Desc') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{stickers, D}|Acc3] end, Acc2, Stickers); - {created,_} when is_integer(Created) -> Acc2; - {updated,_} when is_integer(Updated) -> Acc2; - {downloaded,_} when is_integer(Downloaded) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'StickerPack'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Message'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, msg_id = Msg_id, from = From, to = To, created = Created, files = Files, type = Type, link = Link, seenby = Seenby, repliedby = Repliedby, mentioned = Mentioned, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {container,_} when Container=='chain' orelse Container=='cur' orelse Container==[] -> Acc2; - {feed_id,_} when is_record(Feed_id,'muc') orelse is_record(Feed_id,'p2p') -> Acc2; - {prev,_} when Prev==[] orelse is_integer(Prev) -> Acc2; - {next,_} when Next==[] orelse is_integer(Next) -> Acc2; - {msg_id,_} when Msg_id==[] orelse is_binary(Msg_id) -> Acc2; - {from,_} when From==[] orelse is_binary(From) -> Acc2; - {to,_} when To==[] orelse is_binary(To) -> Acc2; - {created,_} when Created==[] orelse is_integer(Created) -> Acc2; - {files,_} when is_list(Files) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Desc') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{files, D}|Acc3] end, Acc2, Files); - {type,_} when is_list(Type) -> - lists:foldl(fun(Tmp, Acc3) when Tmp=='sys' orelse Tmp=='reply' orelse Tmp=='forward' orelse Tmp=='read' orelse Tmp=='edited' orelse Tmp=='cursor' orelse Tmp=='draft' -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{type, D}|Acc3] end, Acc2, Type); - {link,_} when Link==[] orelse is_integer(Link) orelse is_record(Link,'Message') -> Acc2; - {seenby,_} when Seenby==[] orelse is_list(Seenby) -> - lists:foldl(fun(Tmp, Acc3) when is_binary(Tmp) orelse is_integer(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{seenby, D}|Acc3] end, Acc2, Seenby); - {repliedby,_} when Repliedby==[] orelse is_list(Repliedby) -> - lists:foldl(fun(Tmp, Acc3) when is_integer(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{repliedby, D}|Acc3] end, Acc2, Repliedby); - {mentioned,_} when Mentioned==[] orelse is_list(Mentioned) -> - lists:foldl(fun(Tmp, Acc3) when is_integer(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{mentioned, D}|Acc3] end, Acc2, Mentioned); - {status,_} when Status==[] orelse Status=='async' orelse Status=='delete' orelse Status=='clear' orelse Status=='update' orelse Status=='edit' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Message'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Link'{id = Id, name = Name, room_id = Room_id, created = Created, type = Type, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_binary(Id) -> Acc2; - {name,_} when Name==[] orelse is_binary(Name) -> Acc2; - {room_id,_} when Room_id==[] orelse is_binary(Room_id) -> Acc2; - {created,_} when Created==[] orelse is_integer(Created) -> Acc2; - {type,_} when Type==[] orelse Type=='group' orelse Type=='channel' -> Acc2; - {status,_} when Status==[] orelse Status=='gen' orelse Status=='check' orelse Status=='add' orelse Status=='get' orelse Status=='join' orelse Status=='update' orelse Status=='delete' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Link'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Room'{id = Id, name = Name, links = Links, description = Description, settings = Settings, members = Members, admins = Admins, data = Data, type = Type, tos = Tos, tos_update = Tos_update, unread = Unread, mentions = Mentions, readers = Readers, last_msg = Last_msg, update = Update, created = Created, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_binary(Id) -> Acc2; - {name,_} when Name==[] orelse is_binary(Name) -> Acc2; - {links,_} when Links==[] orelse is_list(Links) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Link') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{links, D}|Acc3] end, Acc2, Links); - {description,_} when Description==[] orelse is_binary(Description) -> Acc2; - {settings,_} when is_list(Settings) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Feature') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{settings, D}|Acc3] end, Acc2, Settings); - {members,_} when is_list(Members) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Member') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{members, D}|Acc3] end, Acc2, Members); - {admins,_} when is_list(Admins) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Member') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{admins, D}|Acc3] end, Acc2, Admins); - {data,_} when is_list(Data) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Desc') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{data, D}|Acc3] end, Acc2, Data); - {type,_} when Type==[] orelse Type=='group' orelse Type=='channel' orelse Type=='call' -> Acc2; - {tos,_} when Tos==[] orelse is_binary(Tos) -> Acc2; - {tos_update,_} when Tos_update==[] orelse is_integer(Tos_update) -> Acc2; - {unread,_} when Unread==[] orelse is_integer(Unread) -> Acc2; - {mentions,_} when is_list(Mentions) -> - lists:foldl(fun(Tmp, Acc3) when is_integer(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{mentions, D}|Acc3] end, Acc2, Mentions); - {readers,_} when is_list(Readers) -> - lists:foldl(fun(Tmp, Acc3) when is_integer(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{readers, D}|Acc3] end, Acc2, Readers); - {last_msg,_} when Last_msg==[] orelse is_integer(Last_msg) orelse is_record(Last_msg,'Message') -> Acc2; - {update,_} when Update==[] orelse is_integer(Update) -> Acc2; - {created,_} when Created==[] orelse is_integer(Created) -> Acc2; - {status,_} when Status==[] orelse Status=='create' orelse Status=='leave' orelse Status=='add' orelse Status=='remove' orelse Status=='removed' orelse Status=='join' orelse Status=='joined' orelse Status=='info' orelse Status=='patch' orelse Status=='get' orelse Status=='delete' orelse Status=='last_msg' orelse Status=='mute' orelse Status=='unmute' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Room'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Tag'{roster_id = Roster_id, name = Name, color = Color, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {roster_id,_} when Roster_id==[] orelse is_binary(Roster_id) -> Acc2; - {name,_} when is_binary(Name) -> Acc2; - {color,_} when is_binary(Color) -> Acc2; - {status,_} when Status==[] orelse Status=='create' orelse Status=='remove' orelse Status=='edit' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Tag'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Star'{id = Id, client_id = Client_id, roster_id = Roster_id, message = Message, tags = Tags, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {client_id,_} when Client_id==[] orelse is_binary(Client_id) -> Acc2; - {roster_id,_} when Roster_id==[] orelse is_integer(Roster_id) -> Acc2; - {message,_} when Message==[] orelse is_record(Message,'Message') -> Acc2; - {tags,_} when is_list(Tags) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Tag') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{tags, D}|Acc3] end, Acc2, Tags); - {status,_} when Status==[] orelse Status=='add' orelse Status=='remove' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Star'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Typing'{phone_id = Phone_id, comments = Comments}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {phone_id,_} when is_binary(Phone_id) -> Acc2; - {comments,_} -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Typing'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Contact'{phone_id = Phone_id, avatar = Avatar, names = Names, surnames = Surnames, nick = Nick, reader = Reader, unread = Unread, last_msg = Last_msg, update = Update, created = Created, settings = Settings, services = Services, presence = Presence, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {phone_id,_} when Phone_id==[] orelse is_binary(Phone_id) -> Acc2; - {avatar,_} when Avatar==[] orelse is_binary(Avatar) -> Acc2; - {names,_} when Names==[] orelse is_binary(Names) -> Acc2; - {surnames,_} when Surnames==[] orelse is_binary(Surnames) -> Acc2; - {nick,_} when Nick==[] orelse is_binary(Nick) -> Acc2; - {reader,_} when Reader==[] orelse is_integer(Reader) orelse is_list(Reader) -> - lists:foldl(fun(Tmp, Acc3) when is_integer(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{reader, D}|Acc3] end, Acc2, Reader); - {unread,_} when Unread==[] orelse is_integer(Unread) -> Acc2; - {last_msg,_} when Last_msg==[] orelse is_record(Last_msg,'Message') -> Acc2; - {update,_} when Update==[] orelse is_integer(Update) -> Acc2; - {created,_} when Created==[] orelse is_integer(Created) -> Acc2; - {settings,_} when is_list(Settings) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Feature') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{settings, D}|Acc3] end, Acc2, Settings); - {services,_} when is_list(Services) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Service') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{services, D}|Acc3] end, Acc2, Services); - {presence,_} when Presence==[] orelse Presence=='online' orelse Presence=='offline' orelse is_binary(Presence) -> Acc2; - {status,_} when Status==[] orelse Status=='request' orelse Status=='authorization' orelse Status=='ignore' orelse Status=='internal' orelse Status=='friend' orelse Status=='last_msg' orelse Status=='ban' orelse Status=='banned' orelse Status=='deleted' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Contact'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'ExtendedStar'{star = Star, from = From}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {star,_} when is_record(Star,'Star') orelse Star==[] -> Acc2; - {from,_} when is_record(From,'Contact') orelse is_record(From,'Room') orelse From==[] -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'ExtendedStar'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Auth'{client_id = Client_id, dev_key = Dev_key, user_id = User_id, phone = Phone, token = Token, type = Type, sms_code = Sms_code, attempts = Attempts, services = Services, settings = Settings, push = Push, os = Os, created = Created, last_online = Last_online}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {client_id,_} when Client_id==[] orelse is_binary(Client_id) -> Acc2; - {dev_key,_} when Dev_key==[] orelse is_binary(Dev_key) -> Acc2; - {user_id,_} when User_id==[] orelse is_binary(User_id) -> Acc2; - {phone,_} when Phone==[] orelse is_binary(Phone) orelse is_tuple(Phone) -> Acc2; - {token,_} when Token==[] orelse is_binary(Token) -> Acc2; - {type,_} when Type==[] orelse is_atom(Type) -> Acc2; - {sms_code,_} when Sms_code==[] orelse is_binary(Sms_code) -> Acc2; - {attempts,_} when Attempts==[] orelse is_integer(Attempts) -> Acc2; - {services,_} when is_list(Services) -> - lists:foldl(fun(Tmp, Acc3) when true -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{services, D}|Acc3] end, Acc2, Services); - {settings,_} when Settings==[] orelse is_list(Settings) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Feature') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{settings, D}|Acc3] end, Acc2, Settings); - {push,_} when Push==[] orelse is_binary(Push) -> Acc2; - {os,_} when Os==[] orelse Os=='ios' orelse Os=='android' orelse Os=='web' -> Acc2; - {created,_} when Created==[] orelse is_integer(Created) -> Acc2; - {last_online,_} when Last_online==[] orelse is_integer(Last_online) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Auth'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Roster'{id = Id, names = Names, surnames = Surnames, email = Email, nick = Nick, userlist = Userlist, roomlist = Roomlist, favorite = Favorite, tags = Tags, phone = Phone, avatar = Avatar, update = Update, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_binary(Id) orelse is_integer(Id) -> Acc2; - {names,_} when Names==[] orelse is_binary(Names) -> Acc2; - {surnames,_} when Surnames==[] orelse is_binary(Surnames) -> Acc2; - {email,_} when Email==[] orelse is_binary(Email) -> Acc2; - {nick,_} when Nick==[] orelse is_binary(Nick) -> Acc2; - {userlist,_} when is_list(Userlist) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Contact') orelse is_integer(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{userlist, D}|Acc3] end, Acc2, Userlist); - {roomlist,_} when is_list(Roomlist) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Room') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{roomlist, D}|Acc3] end, Acc2, Roomlist); - {favorite,_} when is_list(Favorite) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Star') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{favorite, D}|Acc3] end, Acc2, Favorite); - {tags,_} when is_list(Tags) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Tag') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{tags, D}|Acc3] end, Acc2, Tags); - {phone,_} when Phone==[] orelse is_binary(Phone) -> Acc2; - {avatar,_} when Avatar==[] orelse is_binary(Avatar) -> Acc2; - {update,_} when Update==[] orelse is_integer(Update) -> Acc2; - {status,_} when Status==[] orelse Status=='get' orelse Status=='create' orelse Status=='del' orelse Status=='remove' orelse Status=='nick' orelse Status=='add' orelse Status=='update' orelse Status=='list' orelse Status=='patch' orelse Status=='last_msg' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Roster'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Profile'{phone = Phone, services = Services, rosters = Rosters, settings = Settings, update = Update, balance = Balance, presence = Presence, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {phone,_} when Phone==[] orelse is_binary(Phone) -> Acc2; - {services,_} when Services==[] orelse is_list(Services) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Service') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{services, D}|Acc3] end, Acc2, Services); - {rosters,_} when Rosters==[] orelse is_list(Rosters) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Roster') orelse is_binary(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{rosters, D}|Acc3] end, Acc2, Rosters); - {settings,_} when Settings==[] orelse is_list(Settings) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Feature') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{settings, D}|Acc3] end, Acc2, Settings); - {update,_} when is_integer(Update) -> Acc2; - {balance,_} when is_integer(Balance) -> Acc2; - {presence,_} when Presence==[] orelse Presence=='offline' orelse Presence=='online' orelse is_binary(Presence) -> Acc2; - {status,_} when Status==[] orelse Status=='remove' orelse Status=='get' orelse Status=='patch' orelse Status=='update' orelse Status=='delete' orelse Status=='create' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Profile'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Presence'{uid = Uid, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {uid,_} when is_binary(Uid) -> Acc2; - {status,_} when Status==[] orelse Status=='offline' orelse Status=='online' orelse is_binary(Status) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Presence'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Friend'{phone_id = Phone_id, friend_id = Friend_id, settings = Settings, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {phone_id,_} when is_binary(Phone_id) -> Acc2; - {friend_id,_} when is_binary(Friend_id) -> Acc2; - {settings,_} when Settings==[] orelse is_list(Settings) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Feature') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{settings, D}|Acc3] end, Acc2, Settings); - {status,_} when Status=='ban' orelse Status=='unban' orelse Status=='request' orelse Status=='confirm' orelse Status=='update' orelse Status=='ignore' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Friend'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'act'{name = Name, data = Data}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when Name==[] orelse is_binary(Name) -> Acc2; - {data,_} when is_binary(Data) orelse is_integer(Data) orelse is_list(Data) -> - lists:foldl(fun(Tmp, Acc3) when true -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{data, D}|Acc3] end, Acc2, Data); - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'act'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Job'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, context = Context, proc = Proc, time = Time, data = Data, events = Events, settings = Settings, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {container,_} when Container=='chain' orelse Container==[] -> Acc2; - {feed_id, #'act'{}} -> Acc2; - {prev,_} when Prev==[] orelse is_integer(Prev) -> Acc2; - {next,_} when Next==[] orelse is_integer(Next) -> Acc2; - {context,_} when Context==[] orelse is_integer(Context) orelse is_binary(Context) -> Acc2; - {proc,_} when Proc==[] orelse is_integer(Proc) orelse is_record(Proc,'process') -> Acc2; - {time,_} when Time==[] orelse is_integer(Time) -> Acc2; - {data,_} when Data==[] orelse is_binary(Data) orelse is_list(Data) -> - lists:foldl(fun(Tmp, Acc3) when true orelse is_record(Tmp,'Message') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{data, D}|Acc3] end, Acc2, Data); - {events,_} when Events==[] orelse is_list(Events) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'messageEvent') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{events, D}|Acc3] end, Acc2, Events); - {settings,_} when Settings==[] orelse is_list(Settings) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Feature') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{settings, D}|Acc3] end, Acc2, Settings); - {status,_} when Status==[] orelse Status=='init' orelse Status=='update' orelse Status=='delete' orelse Status=='pending' orelse Status=='stop' orelse Status=='complete' orelse Status=='restart' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Job'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'History'{roster_id = Roster_id, feed = Feed, size = Size, entity_id = Entity_id, data = Data, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {roster_id,_} when is_binary(Roster_id) -> Acc2; - {feed,_} when is_record(Feed,'p2p') orelse is_record(Feed,'muc') orelse is_record(Feed,'act') orelse is_record(Feed,'StickerPack') orelse Feed==[] -> Acc2; - {size,_} when Size==[] orelse is_integer(Size) -> Acc2; - {entity_id,_} when Entity_id==[] orelse is_integer(Entity_id) -> Acc2; - {data,_} when Data==[] orelse is_list(Data) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'Message') orelse is_record(Tmp,'Job') orelse is_record(Tmp,'StickerPack') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{data, D}|Acc3] end, Acc2, Data); - {status,_} when Status=='updated' orelse Status=='get' orelse Status=='update' orelse Status=='last_loaded' orelse Status=='last_msg' orelse Status=='get_reply' orelse Status=='double_get' orelse Status=='delete' orelse Status=='image' orelse Status=='video' orelse Status=='file' orelse Status=='link' orelse Status=='audio' orelse Status=='contact' orelse Status=='location' orelse Status=='text' orelse Status=='draft' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'History'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Schedule'{id = Id, proc = Proc, data = Data, state = State}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) orelse is_tuple(Id) -> Acc2; - {proc,_} when Proc==[] orelse is_integer(Proc) orelse is_binary(Proc) -> Acc2; - {data,_} when is_binary(Data) orelse is_list(Data) -> - lists:foldl(fun(Tmp, Acc3) when true -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{data, D}|Acc3] end, Acc2, Data); - {state,_} when State==[] orelse true -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Schedule'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Index'{id = Id, roster = Roster}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse true -> Acc2; - {roster,_} when is_list(Roster) -> - lists:foldl(fun(Tmp, Acc3) when true -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{roster, D}|Acc3] end, Acc2, Roster); - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Index'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Whitelist'{phone = Phone, created = Created}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {phone,_} when Phone==[] orelse is_binary(Phone) -> Acc2; - {created,_} when Created==[] orelse is_integer(Created) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Whitelist'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'error'{code = Code}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {code,_} when Code==[] orelse is_atom(Code) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'error'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'ok'{code = Code}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {code,_} when Code==[] orelse is_binary(Code) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'ok'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'error2'{code = Code, src = Src}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {code,_} when Code==[] orelse is_atom(Code) -> Acc2; - {src,_} when Src==[] orelse is_binary(Src) orelse is_integer(Src) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'error2'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'ok2'{code = Code, src = Src}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {code,_} when Code==[] orelse is_atom(Code) -> Acc2; - {src,_} when Src==[] orelse is_tuple(Src) orelse is_binary(Src) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'ok2'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'io'{code = Code, data = Data}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {code,_} when Code==[] orelse is_record(Code,'ok') orelse is_record(Code,'error') orelse is_record(Code,'ok2') orelse is_record(Code,'error2') orelse Code=='transcribe' -> Acc2; - {data,_} when Data==[] orelse is_binary(Data) orelse is_record(Data,'Roster') orelse is_tuple(Data) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'io'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Ack'{table = Table, id = Id}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {table,_} when is_atom(Table) -> Acc2; - {id,_} when Id==[] orelse is_binary(Id) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Ack'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'errors'{code = Code, data = Data}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {code,_} when Code==[] orelse is_list(Code) -> - lists:foldl(fun(Tmp, Acc3) when is_binary(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{code, D}|Acc3] end, Acc2, Code); - {data,_} when Data==[] orelse true -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'errors'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'PushService'{recipients = Recipients, id = Id, ttl = Ttl, module = Module, priority = Priority, payload = Payload}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {recipients,_} when is_list(Recipients) -> - lists:foldl(fun(Tmp, Acc3) when is_binary(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{recipients, D}|Acc3] end, Acc2, Recipients); - {id,_} when Id==[] orelse is_binary(Id) -> Acc2; - {ttl,_} when Ttl==[] orelse is_integer(Ttl) -> Acc2; - {module,_} when Module==[] orelse is_binary(Module) -> Acc2; - {priority,_} when Priority==[] orelse is_binary(Priority) -> Acc2; - {payload,_} when Payload==[] orelse is_binary(Payload) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'PushService'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'PublishService'{message = Message, topic = Topic, qos = Qos}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {message,_} when Message==[] orelse is_binary(Message) -> Acc2; - {topic,_} when Topic==[] orelse is_binary(Topic) -> Acc2; - {qos,_} when Qos==[] orelse is_integer(Qos) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'PublishService'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'FakeNumbers'{phone = Phone, created = Created}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {phone,_} when Phone==[] orelse is_binary(Phone) -> Acc2; - {created,_} when Created==[] orelse is_integer(Created) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'FakeNumbers'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'Draft'{data = Data, status = Status}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {data,_} when is_list(Data) -> - lists:foldl(fun(Tmp, Acc3) when true -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{data, D}|Acc3] end, Acc2, Data); - {status,_} when Status=='update' orelse Status=='get' orelse Status=='delete' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'Draft'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'handler'{name = Name, module = Module, class = Class, group = Group, config = Config, state = State, seq = Seq}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when is_atom(Name) -> Acc2; - {module,_} when is_atom(Module) -> Acc2; - {class,_} -> Acc2; - {group,_} when is_atom(Group) -> Acc2; - {config,_} -> Acc2; - {state,_} -> Acc2; - {seq,_} -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'handler'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'pi'{name = Name, sup = Sup, module = Module, state = State}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {name,_} when is_atom(Name) -> Acc2; - {sup,_} when is_atom(Sup) -> Acc2; - {module,_} when is_atom(Module) -> Acc2; - {state,_} -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'pi'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'cx'{handlers = Handlers, actions = Actions, req = Req, module = Module, lang = Lang, path = Path, session = Session, formatter = Formatter, params = Params, node = Node, client_pid = Client_pid, state = State, from = From, vsn = Vsn}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {handlers,_} when is_list(Handlers) -> - lists:foldl(fun(Tmp, Acc3) when is_record(Tmp,'handler') -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{handlers, D}|Acc3] end, Acc2, Handlers); - {actions,_} when is_list(Actions) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{actions, D}|Acc3] end, Acc2, Actions); - {req,_} when Req==[] orelse true -> Acc2; - {module,_} when Module==[] orelse is_atom(Module) -> Acc2; - {lang,_} when Lang==[] orelse is_atom(Lang) -> Acc2; - {path,_} when Path==[] orelse is_binary(Path) -> Acc2; - {session,_} when Session==[] orelse is_binary(Session) -> Acc2; - {formatter,_} when Formatter=='bert' orelse Formatter=='json' -> Acc2; - {params,_} when Params==[] orelse is_list(Params) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{params, D}|Acc3] end, Acc2, Params); - {node,_} when Node==[] orelse is_atom(Node) -> Acc2; - {client_pid,_} when Client_pid==[] orelse true -> Acc2; - {state,_} when State==[] orelse true -> Acc2; - {from,_} when From==[] orelse is_binary(From) -> Acc2; - {vsn,_} when Vsn==[] orelse is_binary(Vsn) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'cx'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'user'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, feeds = Feeds, email = Email}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {container,_} when is_atom(Container) -> Acc2; - {feed_id,_} -> Acc2; - {prev,_} when Prev==[] orelse is_integer(Prev) -> Acc2; - {next,_} when Next==[] orelse is_integer(Next) -> Acc2; - {feeds,_} when is_list(Feeds) -> []; - {email,_} when Email==[] orelse is_binary(Email) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'user'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'group'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, feeds = Feeds}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when Id==[] orelse is_integer(Id) -> Acc2; - {container,_} when is_atom(Container) -> Acc2; - {feed_id,_} -> Acc2; - {prev,_} when Prev==[] orelse is_integer(Prev) -> Acc2; - {next,_} when Next==[] orelse is_integer(Next) -> Acc2; - {feeds,_} when is_list(Feeds) -> []; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'group'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'mqtt_topic'{topic = Topic, flags = Flags}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {topic,_} when is_binary(Topic) -> Acc2; - {flags,_} when is_list(Flags) -> - lists:foldl(fun(Tmp, Acc3) when Tmp=='retained' orelse Tmp=='static' -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{flags, D}|Acc3] end, Acc2, Flags); - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'mqtt_topic'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'mqtt_client'{client_id = Client_id, client_pid = Client_pid, username = Username, peername = Peername, clean_sess = Clean_sess, proto_ver = Proto_ver, will_topic = Will_topic, ws_initial_headers = Ws_initial_headers}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {client_id,_} when is_binary(Client_id) orelse Client_id=='undefined' -> Acc2; - - {username,_} when is_binary(Username) orelse Username=='undefined' -> Acc2; - {peername,_} when is_tuple(Peername) -> Acc2; - - {proto_ver,_} when Proto_ver==3 orelse Proto_ver==4 -> Acc2; - {will_topic,_} when Will_topic=='undefined' orelse is_binary(Will_topic) -> Acc2; - {ws_initial_headers,_} when is_list(Ws_initial_headers) -> - lists:foldl(fun(Tmp, Acc3) when is_tuple(Tmp) -> validate(Tmp, Acc3, CM); (Tmp, Acc3) -> [{ws_initial_headers, D}|Acc3] end, Acc2, Ws_initial_headers); - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'mqtt_client'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'mqtt_session'{client_id = Client_id, sess_pid = Sess_pid, clean_sess = Clean_sess}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {client_id,_} when is_binary(Client_id) -> Acc2; - - - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'mqtt_session'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'mqtt_message'{id = Id}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when is_binary(Id) orelse Id=='undefined' -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'mqtt_message'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'mqtt_delivery'{sender = Sender, message = Message}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - - {message, #'mqtt_message'{}} -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'mqtt_delivery'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'mqtt_route'{topic = Topic, node = Node}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {topic,_} when is_binary(Topic) -> Acc2; - - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'mqtt_route'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end; -validate(D = #'mqtt_alarm'{id = Id, severity = Severity, title = Title, summary = Summary}, Acc, {CustomValidateModule, ValidateFun} = CM) -> - ErrFields = lists:foldl(fun ({RecField, F}, Acc2) -> - case {RecField, F} of - {id,_} when is_binary(Id) -> Acc2; - {severity,_} when Severity=='warning' orelse Severity=='error' orelse Severity=='critical' -> Acc2; - {title,_} when is_list(Title) orelse is_binary(Title) -> Acc2; - {summary,_} when is_list(Summary) orelse is_binary(Summary) -> Acc2; - _ -> [{RecField, D}|Acc2] - end end, Acc, lists:zip(record_info(fields, 'mqtt_alarm'), tl(tuple_to_list(D)))), - ErrFields++case ErrFields of [] -> CustomValidateModule:ValidateFun(D); _ -> [] end. \ No newline at end of file +validate(D = #'writer'{count = Count, cache = Cache, first = First}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} -> []; + {count,_} when is_integer(Count) -> []; + {cache,_} when (is_tuple(Cache) orelse Cache==[]) -> []; + {args,_} -> []; + {first,_} when (is_tuple(First) orelse First==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'writer'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'reader'{pos = Pos, cache = Cache}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} -> []; + {pos,_} when (is_integer(Pos) orelse Pos==[]) -> []; + {cache,_} when (is_integer(Cache) orelse Cache==[]) -> []; + {args,_} -> []; + {feed,_} -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'reader'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'iter'{id = Id, container = Container, next = Next, prev = Prev}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {container,_} when is_atom(Container) -> []; + {feed,_} -> []; + {next,_} when (is_integer(Next) orelse Next==[]) -> []; + {prev,_} when (is_integer(Prev) orelse Prev==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'iter'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'container'{id = Id, top = Top, rear = Rear, count = Count}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {top,_} when (is_integer(Top) orelse Top==[]) -> []; + {rear,_} when (is_integer(Rear) orelse Rear==[]) -> []; + {count,_} when is_integer(Count) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'container'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'iterator'{id = Id, container = Container, prev = Prev, next = Next, feeds = Feeds}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {container,_} when is_atom(Container) -> []; + {feed_id,_} -> []; + {prev,_} when (is_integer(Prev) orelse Prev==[]) -> []; + {next,_} when (is_integer(Next) orelse Next==[]) -> []; + {feeds,_} when is_list(Feeds) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'iterator'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'task'{name = Name, module = Module, prompt = Prompt, roles = Roles}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when (is_atom(Name) orelse Name==[]) -> []; + {module,_} when (is_atom(Module) orelse Module==[]) -> []; + {prompt,_} when is_list(Prompt) -> []; + {roles,_} when is_binary(Roles) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'task'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'userTask'{name = Name, module = Module, prompt = Prompt, roles = Roles}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when (is_atom(Name) orelse Name==[]) -> []; + {module,_} when (is_atom(Module) orelse Module==[]) -> []; + {prompt,_} when is_list(Prompt) -> []; + {roles,_} when is_binary(Roles) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'userTask'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'serviceTask'{name = Name, module = Module, prompt = Prompt, roles = Roles}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when (is_atom(Name) orelse Name==[]) -> []; + {module,_} when (is_atom(Module) orelse Module==[]) -> []; + {prompt,_} when is_list(Prompt) -> []; + {roles,_} when is_binary(Roles) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'serviceTask'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'receiveTask'{name = Name, module = Module, prompt = Prompt, roles = Roles}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when (is_atom(Name) orelse Name==[]) -> []; + {module,_} when (is_atom(Module) orelse Module==[]) -> []; + {prompt,_} when is_list(Prompt) -> []; + {roles,_} when is_binary(Roles) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'receiveTask'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'messageEvent'{name = Name, module = Module, prompt = Prompt, payload = Payload, timeout = Timeout}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when (is_atom(Name) orelse Name==[]) -> []; + {module,_} when (is_atom(Module) orelse Module==[]) -> []; + {prompt,_} when is_list(Prompt) -> []; + {payload,_} when is_binary(Payload) -> []; + {timeout,_} when is_tuple(Timeout) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'messageEvent'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'boundaryEvent'{name = Name, module = Module, prompt = Prompt, payload = Payload, timeout = Timeout, timeDate = TimeDate, timeDuration = TimeDuration, timeCycle = TimeCycle}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when (is_atom(Name) orelse Name==[]) -> []; + {module,_} when (is_atom(Module) orelse Module==[]) -> []; + {prompt,_} when is_list(Prompt) -> []; + {payload,_} when is_binary(Payload) -> []; + {timeout,_} when is_tuple(Timeout) -> []; + {timeDate,_} when is_binary(TimeDate) -> []; + {timeDuration,_} when is_binary(TimeDuration) -> []; + {timeCycle,_} when is_binary(TimeCycle) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'boundaryEvent'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'timeoutEvent'{name = Name, module = Module, prompt = Prompt, payload = Payload, timeout = Timeout, timeDate = TimeDate, timeDuration = TimeDuration, timeCycle = TimeCycle}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when (is_atom(Name) orelse Name==[]) -> []; + {module,_} when (is_atom(Module) orelse Module==[]) -> []; + {prompt,_} when is_list(Prompt) -> []; + {payload,_} when (is_binary(Payload) orelse Payload==[]) -> []; + {timeout,_} when (is_tuple(Timeout) orelse Timeout==[]) -> []; + {timeDate,_} when (is_binary(TimeDate) orelse TimeDate==[]) -> []; + {timeDuration,_} when (is_binary(TimeDuration) orelse TimeDuration==[]) -> []; + {timeCycle,_} when (is_binary(TimeCycle) orelse TimeCycle==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'timeoutEvent'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'beginEvent'{name = Name, module = Module, prompt = Prompt}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when (is_atom(Name) orelse Name==[]) -> []; + {module,_} when (is_atom(Module) orelse Module==[]) -> []; + {prompt,_} when is_list(Prompt) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'beginEvent'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'endEvent'{name = Name, module = Module, prompt = Prompt}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when (is_atom(Name) orelse Name==[]) -> []; + {module,_} when (is_atom(Module) orelse Module==[]) -> []; + {prompt,_} when is_list(Prompt) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'endEvent'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'sequenceFlow'{source = Source, target = Target}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {source,_} when (is_atom(Source) orelse Source==[]) -> []; + {target,_} when (is_list(Target) orelse is_atom(Target) orelse Target==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'sequenceFlow'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'hist'{id = Id, container = Container, prev = Prev, next = Next, feeds = Feeds, name = Name, task = Task, docs = Docs}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {container,_} when is_atom(Container) -> []; + {feed_id,_} -> []; + {prev,_} when (is_integer(Prev) orelse Prev==[]) -> []; + {next,_} when (is_integer(Next) orelse Next==[]) -> []; + {feeds,_} when is_list(Feeds) -> []; + {name,_} when (is_binary(Name) orelse Name==[]) -> []; + {task,_} when is_atom(Task) -> []; + {docs,_} when is_list(Docs) -> []; + {time,_} -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'hist'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'process'{id = Id, container = Container, prev = Prev, next = Next, feeds = Feeds, name = Name, roles = Roles, tasks = Tasks, events = Events, flows = Flows, docs = Docs, task = Task, timer = Timer, notifications = Notifications, result = Result, started = Started, beginEvent = BeginEvent, endEvent = EndEvent}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {container,_} when is_atom(Container) -> []; + {feed_id,_} -> []; + {prev,_} when (is_integer(Prev) orelse Prev==[]) -> []; + {next,_} when (is_integer(Next) orelse Next==[]) -> []; + {feeds,_} when is_list(Feeds) -> []; + {name,_} when (is_binary(Name) orelse Name==[]) -> []; + {roles,_} when is_list(Roles) -> []; + {tasks,_} when is_list(Tasks) -> []; + {events,_} when is_list(Events) -> []; + {flows,_} when is_list(Flows) -> []; + {docs,_} when is_list(Docs) -> []; + {options,_} -> []; + {task,_} when (is_atom(Task) orelse Task==[]) -> []; + {timer,_} when (is_binary(Timer) orelse Timer==[]) -> []; + {notifications,_} when (Notifications==[]) -> []; + {result,_} when (is_binary(Result) orelse Result==[]) -> []; + {started,_} when (is_tuple(Started) orelse Started==[]) -> []; + {beginEvent,_} when (is_atom(BeginEvent) orelse BeginEvent==[]) -> []; + {endEvent,_} when (is_atom(EndEvent) orelse EndEvent==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'process'), tl(tuple_to_list(D)))]), + CondFuns = [?COND_FUN(is_record(Rec, 'sequenceFlow'))], + Fields = [Flows], + FieldNames = [flows], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'complete'{id = Id}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'complete'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'proc'{id = Id}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'proc'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'load'{id = Id}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'load'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'histo'{id = Id}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'histo'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'create'{proc = Proc, docs = Docs}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {proc,_} when (is_binary(Proc) orelse is_record(Proc,'process') orelse Proc==[]) -> []; + {docs,_} when (is_list(Docs) orelse Docs==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'create'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'amend'{id = Id, docs = Docs}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {docs,_} when (is_list(Docs) orelse Docs==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'amend'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'push'{model = Model, type = Type, title = Title, alert = Alert, badge = Badge, sound = Sound}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {model,_} when (Model==[]) -> []; + {type,_} when (is_binary(Type) orelse Type==[]) -> []; + {title,_} when (is_binary(Title) orelse Title==[]) -> []; + {alert,_} when (is_binary(Alert) orelse Alert==[]) -> []; + {badge,_} when (is_integer(Badge) orelse Badge==[]) -> []; + {sound,_} when (is_binary(Sound) orelse Sound==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'push'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Search'{id = Id, ref = Ref, type = Type, value = Value, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_binary(Id) orelse is_integer(Id)) -> []; + {ref,_} when is_binary(Ref) -> []; + {field,_} when is_binary(Field) -> []; + {type,_} when (Type=='like' orelse Type=='!=' orelse Type=='==') -> []; + {value,_} when is_list(Value) -> []; + {status,_} when (Status=='room' orelse Status=='member' orelse Status=='contact' orelse Status=='roster' orelse Status=='profile') -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Search'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'p2p'{from = From, to = To}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {from,_} when is_binary(From) -> []; + {to,_} when is_binary(To) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'p2p'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'muc'{name = Name}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when is_binary(Name) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'muc'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'mqi'{feed_id = Feed_id, query = Query, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {feed_id,_} when is_record(Feed_id,'mqi') -> []; + {query,_} when (is_binary(Query) orelse Query==[]) -> []; + {status,_} when (Status=='removed' orelse Status=='member' orelse Status=='admin' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'mqi'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Feature'{id = Id, key = Key, value = Value, group = Group}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_binary(Id) orelse Id==[]) -> []; + {key,_} when is_binary(Key) -> []; + {value,_} when is_binary(Value) -> []; + {group,_} when is_binary(Group) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Feature'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Service'{id = Id, type = Type, login = Login, password = Password, expiration = Expiration, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_binary(Id) orelse Id==[]) -> []; + {type,_} when (Type=='wallet' orelse Type=='aws' orelse Type=='vox' orelse Type=='email') -> []; + {data,_} -> []; + {login,_} when (is_binary(Login) orelse Login==[]) -> []; + {password,_} when (is_binary(Password) orelse Password==[]) -> []; + {expiration,_} when (is_integer(Expiration) orelse Expiration==[]) -> []; + {status,_} when (Status=='remove' orelse Status=='add' orelse Status=='added' orelse Status=='verified' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Service'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Member'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, feeds = Feeds, phone_id = Phone_id, avatar = Avatar, names = Names, surnames = Surnames, alias = Alias, reader = Reader, update = Update, settings = Settings, services = Services, presence = Presence, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {container,_} when (Container==[] orelse Container=='cur' orelse Container=='chain') -> []; + {feed_id,_} when (Feed_id==[] orelse is_record(Feed_id,'p2p') orelse is_record(Feed_id,'muc')) -> []; + {prev,_} when (is_integer(Prev) orelse Prev==[]) -> []; + {next,_} when (is_integer(Next) orelse Next==[]) -> []; + {feeds,_} when is_list(Feeds) -> []; + {phone_id,_} when (is_binary(Phone_id) orelse Phone_id==[]) -> []; + {avatar,_} when (is_binary(Avatar) orelse Avatar==[]) -> []; + {names,_} when (is_binary(Names) orelse Names==[]) -> []; + {surnames,_} when (is_binary(Surnames) orelse Surnames==[]) -> []; + {alias,_} when (is_binary(Alias) orelse Alias==[]) -> []; + {reader,_} when (is_integer(Reader) orelse Reader==[]) -> []; + {update,_} when (is_integer(Update) orelse Update==[]) -> []; + {settings,_} when (is_list(Settings) orelse Settings==[]) -> []; + {services,_} when (is_list(Services) orelse Services==[]) -> []; + {presence,_} when (Presence=='offline' orelse Presence=='online' orelse Presence==[]) -> []; + {status,_} when (Status=='owner' orelse Status=='patch' orelse Status=='removed' orelse Status=='member' orelse Status=='admin' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Member'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Desc'{id = Id, mime = Mime, payload = Payload, parentid = Parentid, data = Data}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_binary(Id) orelse Id==[]) -> []; + {mime,_} when (is_binary(Mime) orelse Mime==[]) -> []; + {payload,_} when (is_binary(Payload) orelse Payload==[]) -> []; + {parentid,_} when (is_binary(Parentid) orelse Parentid==[]) -> []; + {data,_} when is_list(Data) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Desc'), tl(tuple_to_list(D)))]), + CondFuns = [?COND_FUN(is_record(Rec, 'Feature'))], + Fields = [Data], + FieldNames = [data], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'StickerPack'{id = Id, name = Name, keywords = Keywords, description = Description, author = Author, stickers = Stickers, created = Created, updated = Updated, downloaded = Downloaded}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {name,_} when (is_binary(Name) orelse Name==[]) -> []; + {keywords,_} when is_list(Keywords) -> []; + {description,_} when (is_binary(Description) orelse Description==[]) -> []; + {author,_} when (is_binary(Author) orelse Author==[]) -> []; + {stickers,_} when is_list(Stickers) -> []; + {created,_} when is_integer(Created) -> []; + {updated,_} when is_integer(Updated) -> []; + {downloaded,_} when is_integer(Downloaded) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'StickerPack'), tl(tuple_to_list(D)))]), + CondFuns = [?COND_FUN(is_record(Rec, 'Desc'))], + Fields = [Stickers], + FieldNames = [stickers], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Message'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, msg_id = Msg_id, from = From, to = To, created = Created, files = Files, type = Type, link = Link, seenby = Seenby, repliedby = Repliedby, mentioned = Mentioned, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {container,_} when (Container==[] orelse Container=='cur' orelse Container=='chain') -> []; + {feed_id,_} when (is_record(Feed_id,'p2p') orelse is_record(Feed_id,'muc')) -> []; + {prev,_} when (is_integer(Prev) orelse Prev==[]) -> []; + {next,_} when (is_integer(Next) orelse Next==[]) -> []; + {msg_id,_} when (is_binary(Msg_id) orelse Msg_id==[]) -> []; + {from,_} when (is_binary(From) orelse From==[]) -> []; + {to,_} when (is_binary(To) orelse To==[]) -> []; + {created,_} when (is_integer(Created) orelse Created==[]) -> []; + {files,_} when is_list(Files) -> []; + {type,_} when is_list(Type) -> []; + {link,_} when (is_record(Link,'Message') orelse is_integer(Link) orelse Link==[]) -> []; + {seenby,_} when (is_list(Seenby) orelse Seenby==[]) -> []; + {repliedby,_} when (is_list(Repliedby) orelse Repliedby==[]) -> []; + {mentioned,_} when (is_list(Mentioned) orelse Mentioned==[]) -> []; + {status,_} when (Status=='edit' orelse Status=='update' orelse Status=='clear' orelse Status=='delete' orelse Status=='async' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Message'), tl(tuple_to_list(D)))]), + CondFuns = [?COND_FUN(is_record(Rec, 'Desc'))], + Fields = [Files], + FieldNames = [files], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Link'{id = Id, name = Name, room_id = Room_id, created = Created, type = Type, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_binary(Id) orelse Id==[]) -> []; + {name,_} when (is_binary(Name) orelse Name==[]) -> []; + {room_id,_} when (is_binary(Room_id) orelse Room_id==[]) -> []; + {created,_} when (is_integer(Created) orelse Created==[]) -> []; + {type,_} when (Type=='channel' orelse Type=='group' orelse Type==[]) -> []; + {status,_} when (Status=='delete' orelse Status=='update' orelse Status=='join' orelse Status=='get' orelse Status=='add' orelse Status=='check' orelse Status=='gen' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Link'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Room'{id = Id, name = Name, links = Links, description = Description, settings = Settings, members = Members, admins = Admins, data = Data, type = Type, tos = Tos, tos_update = Tos_update, unread = Unread, mentions = Mentions, readers = Readers, last_msg = Last_msg, update = Update, created = Created, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_binary(Id) orelse Id==[]) -> []; + {name,_} when (is_binary(Name) orelse Name==[]) -> []; + {links,_} when (is_list(Links) orelse Links==[]) -> []; + {description,_} when (is_binary(Description) orelse Description==[]) -> []; + {settings,_} when is_list(Settings) -> []; + {members,_} when is_list(Members) -> []; + {admins,_} when is_list(Admins) -> []; + {data,_} when is_list(Data) -> []; + {type,_} when (Type=='call' orelse Type=='channel' orelse Type=='group' orelse Type==[]) -> []; + {tos,_} when (is_binary(Tos) orelse Tos==[]) -> []; + {tos_update,_} when (is_integer(Tos_update) orelse Tos_update==[]) -> []; + {unread,_} when (is_integer(Unread) orelse Unread==[]) -> []; + {mentions,_} when is_list(Mentions) -> []; + {readers,_} when is_list(Readers) -> []; + {last_msg,_} when (is_record(Last_msg,'Message') orelse is_integer(Last_msg) orelse Last_msg==[]) -> []; + {update,_} when (is_integer(Update) orelse Update==[]) -> []; + {created,_} when (is_integer(Created) orelse Created==[]) -> []; + {status,_} when (Status=='unmute' orelse Status=='mute' orelse Status=='last_msg' orelse Status=='delete' orelse Status=='get' orelse Status=='patch' orelse Status=='info' orelse Status=='joined' orelse Status=='join' orelse Status=='removed' orelse Status=='remove' orelse Status=='add' orelse Status=='leave' orelse Status=='create' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Room'), tl(tuple_to_list(D)))]), + CondFuns = [?COND_FUN(is_record(Rec, 'Feature')),?COND_FUN(is_record(Rec, 'Member')),?COND_FUN(is_record(Rec, 'Member')),?COND_FUN(is_record(Rec, 'Desc'))], + Fields = [Settings,Members,Admins,Data], + FieldNames = [settings,members,admins,data], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Tag'{roster_id = Roster_id, name = Name, color = Color, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {roster_id,_} when (is_binary(Roster_id) orelse Roster_id==[]) -> []; + {name,_} when is_binary(Name) -> []; + {color,_} when is_binary(Color) -> []; + {status,_} when (Status=='edit' orelse Status=='remove' orelse Status=='create' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Tag'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Star'{id = Id, client_id = Client_id, roster_id = Roster_id, message = Message, tags = Tags, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {client_id,_} when (is_binary(Client_id) orelse Client_id==[]) -> []; + {roster_id,_} when (is_integer(Roster_id) orelse Roster_id==[]) -> []; + {message,_} when (is_record(Message,'Message') orelse Message==[]) -> []; + {tags,_} when is_list(Tags) -> []; + {status,_} when (Status=='remove' orelse Status=='add' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Star'), tl(tuple_to_list(D)))]), + CondFuns = [?COND_FUN(is_record(Rec, 'Tag'))], + Fields = [Tags], + FieldNames = [tags], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Typing'{phone_id = Phone_id}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {phone_id,_} when is_binary(Phone_id) -> []; + {comments,_} -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Typing'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Contact'{phone_id = Phone_id, avatar = Avatar, names = Names, surnames = Surnames, nick = Nick, reader = Reader, unread = Unread, last_msg = Last_msg, update = Update, created = Created, settings = Settings, services = Services, presence = Presence, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {phone_id,_} when (is_binary(Phone_id) orelse Phone_id==[]) -> []; + {avatar,_} when (is_binary(Avatar) orelse Avatar==[]) -> []; + {names,_} when (is_binary(Names) orelse Names==[]) -> []; + {surnames,_} when (is_binary(Surnames) orelse Surnames==[]) -> []; + {nick,_} when (is_binary(Nick) orelse Nick==[]) -> []; + {reader,_} when (is_list(Reader) orelse is_integer(Reader) orelse Reader==[]) -> []; + {unread,_} when (is_integer(Unread) orelse Unread==[]) -> []; + {last_msg,_} when (is_record(Last_msg,'Message') orelse Last_msg==[]) -> []; + {update,_} when (is_integer(Update) orelse Update==[]) -> []; + {created,_} when (is_integer(Created) orelse Created==[]) -> []; + {settings,_} when is_list(Settings) -> []; + {services,_} when is_list(Services) -> []; + {presence,_} when (is_binary(Presence) orelse Presence=='offline' orelse Presence=='online' orelse Presence==[]) -> []; + {status,_} when (Status=='deleted' orelse Status=='banned' orelse Status=='ban' orelse Status=='last_msg' orelse Status=='friend' orelse Status=='internal' orelse Status=='ignore' orelse Status=='authorization' orelse Status=='request' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Contact'), tl(tuple_to_list(D)))]), + CondFuns = [?COND_FUN(is_record(Rec, 'Feature')),?COND_FUN(is_record(Rec, 'Service'))], + Fields = [Settings,Services], + FieldNames = [settings,services], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'ExtendedStar'{star = Star, from = From}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {star,_} when (Star==[] orelse is_record(Star,'Star')) -> []; + {from,_} when (From==[] orelse is_record(From,'Room') orelse is_record(From,'Contact')) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'ExtendedStar'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Auth'{client_id = Client_id, dev_key = Dev_key, user_id = User_id, phone = Phone, token = Token, type = Type, sms_code = Sms_code, attempts = Attempts, services = Services, settings = Settings, push = Push, os = Os, created = Created, last_online = Last_online}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {client_id,_} when (is_binary(Client_id) orelse Client_id==[]) -> []; + {dev_key,_} when (is_binary(Dev_key) orelse Dev_key==[]) -> []; + {user_id,_} when (is_binary(User_id) orelse User_id==[]) -> []; + {phone,_} when (is_tuple(Phone) orelse is_binary(Phone) orelse Phone==[]) -> []; + {token,_} when (is_binary(Token) orelse Token==[]) -> []; + {type,_} when (is_atom(Type) orelse Type==[]) -> []; + {sms_code,_} when (is_binary(Sms_code) orelse Sms_code==[]) -> []; + {attempts,_} when (is_integer(Attempts) orelse Attempts==[]) -> []; + {services,_} when is_list(Services) -> []; + {settings,_} when (is_list(Settings) orelse Settings==[]) -> []; + {push,_} when (is_binary(Push) orelse Push==[]) -> []; + {os,_} when (Os=='web' orelse Os=='android' orelse Os=='ios' orelse Os==[]) -> []; + {created,_} when (is_integer(Created) orelse Created==[]) -> []; + {last_online,_} when (is_integer(Last_online) orelse Last_online==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Auth'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Roster'{id = Id, names = Names, surnames = Surnames, email = Email, nick = Nick, userlist = Userlist, roomlist = Roomlist, favorite = Favorite, tags = Tags, phone = Phone, avatar = Avatar, update = Update, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse is_binary(Id) orelse Id==[]) -> []; + {names,_} when (is_binary(Names) orelse Names==[]) -> []; + {surnames,_} when (is_binary(Surnames) orelse Surnames==[]) -> []; + {email,_} when (is_binary(Email) orelse Email==[]) -> []; + {nick,_} when (is_binary(Nick) orelse Nick==[]) -> []; + {userlist,_} when is_list(Userlist) -> []; + {roomlist,_} when is_list(Roomlist) -> []; + {favorite,_} when is_list(Favorite) -> []; + {tags,_} when is_list(Tags) -> []; + {phone,_} when (is_binary(Phone) orelse Phone==[]) -> []; + {avatar,_} when (is_binary(Avatar) orelse Avatar==[]) -> []; + {update,_} when (is_integer(Update) orelse Update==[]) -> []; + {status,_} when (Status=='last_msg' orelse Status=='patch' orelse Status=='list' orelse Status=='update' orelse Status=='add' orelse Status=='nick' orelse Status=='remove' orelse Status=='del' orelse Status=='create' orelse Status=='get' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Roster'), tl(tuple_to_list(D)))]), + CondFuns = [?COND_FUN(is_record(Rec, 'Room')),?COND_FUN(is_record(Rec, 'Star')),?COND_FUN(is_record(Rec, 'Tag'))], + Fields = [Roomlist,Favorite,Tags], + FieldNames = [roomlist,favorite,tags], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Profile'{phone = Phone, services = Services, rosters = Rosters, settings = Settings, update = Update, balance = Balance, presence = Presence, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {phone,_} when (is_binary(Phone) orelse Phone==[]) -> []; + {services,_} when (is_list(Services) orelse Services==[]) -> []; + {rosters,_} when (is_list(Rosters) orelse Rosters==[]) -> []; + {settings,_} when (is_list(Settings) orelse Settings==[]) -> []; + {update,_} when is_integer(Update) -> []; + {balance,_} when is_integer(Balance) -> []; + {presence,_} when (is_binary(Presence) orelse Presence=='online' orelse Presence=='offline' orelse Presence==[]) -> []; + {status,_} when (Status=='create' orelse Status=='delete' orelse Status=='update' orelse Status=='patch' orelse Status=='get' orelse Status=='remove' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Profile'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Presence'{uid = Uid, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {uid,_} when is_binary(Uid) -> []; + {status,_} when (is_binary(Status) orelse Status=='online' orelse Status=='offline' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Presence'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Friend'{phone_id = Phone_id, friend_id = Friend_id, settings = Settings, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {phone_id,_} when is_binary(Phone_id) -> []; + {friend_id,_} when is_binary(Friend_id) -> []; + {settings,_} when (is_list(Settings) orelse Settings==[]) -> []; + {status,_} when (Status=='ignore' orelse Status=='update' orelse Status=='confirm' orelse Status=='request' orelse Status=='unban' orelse Status=='ban') -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Friend'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'act'{name = Name, data = Data}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when (is_binary(Name) orelse Name==[]) -> []; + {data,_} when (is_list(Data) orelse is_integer(Data) orelse is_binary(Data)) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'act'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Job'{id = Id, container = Container, feed_id = Feed_id, prev = Prev, next = Next, context = Context, proc = Proc, time = Time, data = Data, events = Events, settings = Settings, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {container,_} when (Container==[] orelse Container=='chain') -> []; + {feed_id,_} when is_record(Feed_id,'Job') -> []; + {prev,_} when (is_integer(Prev) orelse Prev==[]) -> []; + {next,_} when (is_integer(Next) orelse Next==[]) -> []; + {context,_} when (is_binary(Context) orelse is_integer(Context) orelse Context==[]) -> []; + {proc,_} when (is_record(Proc,'process') orelse is_integer(Proc) orelse Proc==[]) -> []; + {time,_} when (is_integer(Time) orelse Time==[]) -> []; + {data,_} when (is_list(Data) orelse is_binary(Data) orelse Data==[]) -> []; + {events,_} when (is_list(Events) orelse Events==[]) -> []; + {settings,_} when (is_list(Settings) orelse Settings==[]) -> []; + {status,_} when (Status=='restart' orelse Status=='complete' orelse Status=='stop' orelse Status=='pending' orelse Status=='delete' orelse Status=='update' orelse Status=='init' orelse Status==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Job'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'History'{roster_id = Roster_id, feed = Feed, size = Size, entity_id = Entity_id, data = Data, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {roster_id,_} when is_binary(Roster_id) -> []; + {feed,_} when (Feed==[] orelse is_record(Feed,'StickerPack') orelse is_record(Feed,'act') orelse is_record(Feed,'muc') orelse is_record(Feed,'p2p')) -> []; + {size,_} when (is_integer(Size) orelse Size==[]) -> []; + {entity_id,_} when (is_integer(Entity_id) orelse Entity_id==[]) -> []; + {data,_} when (is_list(Data) orelse Data==[]) -> []; + {status,_} when (Status=='draft' orelse Status=='text' orelse Status=='location' orelse Status=='contact' orelse Status=='audio' orelse Status=='link' orelse Status=='file' orelse Status=='video' orelse Status=='image' orelse Status=='delete' orelse Status=='double_get' orelse Status=='get_reply' orelse Status=='last_msg' orelse Status=='last_loaded' orelse Status=='update' orelse Status=='get' orelse Status=='updated') -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'History'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Schedule'{id = Id, proc = Proc, data = Data, state = State}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_tuple(Id) orelse is_integer(Id) orelse Id==[]) -> []; + {proc,_} when (is_binary(Proc) orelse is_integer(Proc) orelse Proc==[]) -> []; + {data,_} when (is_list(Data) orelse is_binary(Data)) -> []; + {state,_} when (State==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Schedule'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Index'{id = Id, roster = Roster}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (Id==[]) -> []; + {roster,_} when is_list(Roster) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Index'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Whitelist'{phone = Phone, created = Created}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {phone,_} when (is_binary(Phone) orelse Phone==[]) -> []; + {created,_} when (is_integer(Created) orelse Created==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Whitelist'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'error'{code = Code}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {code,_} when (is_atom(Code) orelse Code==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'error'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'ok'{code = Code}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {code,_} when (is_binary(Code) orelse Code==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'ok'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'error2'{code = Code, src = Src}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {code,_} when (is_atom(Code) orelse Code==[]) -> []; + {src,_} when (is_integer(Src) orelse is_binary(Src) orelse Src==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'error2'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'ok2'{code = Code, src = Src}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {code,_} when (is_atom(Code) orelse Code==[]) -> []; + {src,_} when (is_binary(Src) orelse is_tuple(Src) orelse Src==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'ok2'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'MessageErr'{feed_id = Feed_id, msg_id = Msg_id, error = Error}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {feed_id,_} when (is_record(Feed_id,'p2p') orelse is_record(Feed_id,'muc')) -> []; + {msg_id,_} when (is_binary(Msg_id) orelse Msg_id==[]) -> []; + {error,_} when (is_record(Error,'error') orelse Error==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'MessageErr'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'io'{code = Code, data = Data}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {code,_} when (is_record(Code,'MessageErr') orelse Code=='transcribe' orelse is_record(Code,'error2') orelse is_record(Code,'ok2') orelse is_record(Code,'error') orelse is_record(Code,'ok') orelse Code==[]) -> []; + {data,_} when (is_tuple(Data) orelse is_record(Data,'Roster') orelse is_binary(Data) orelse Data==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'io'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Ack'{table = Table, id = Id}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {table,_} when is_atom(Table) -> []; + {id,_} when (is_binary(Id) orelse Id==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Ack'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'MessageAck'{id = Id, next = Next, feed_id = Feed_id, created = Created, msg_id = Msg_id}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {id,_} when (is_integer(Id) orelse Id==[]) -> []; + {next,_} when (is_integer(Next) orelse Next==[]) -> []; + {feed_id,_} when (is_record(Feed_id,'p2p') orelse is_record(Feed_id,'muc')) -> []; + {created,_} when (is_integer(Created) orelse Created==[]) -> []; + {msg_id,_} when (is_binary(Msg_id) orelse Msg_id==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'MessageAck'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'errors'{code = Code, data = Data}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {code,_} when (is_list(Code) orelse Code==[]) -> []; + {data,_} when (Data==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'errors'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'PushService'{recipients = Recipients, id = Id, ttl = Ttl, module = Module, priority = Priority, payload = Payload}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {recipients,_} when is_list(Recipients) -> []; + {id,_} when (is_binary(Id) orelse Id==[]) -> []; + {ttl,_} when (is_integer(Ttl) orelse Ttl==[]) -> []; + {module,_} when (is_binary(Module) orelse Module==[]) -> []; + {priority,_} when (is_binary(Priority) orelse Priority==[]) -> []; + {payload,_} when (is_binary(Payload) orelse Payload==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'PushService'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'PublishService'{message = Message, topic = Topic, qos = Qos}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {message,_} when (is_binary(Message) orelse Message==[]) -> []; + {topic,_} when (is_binary(Topic) orelse Topic==[]) -> []; + {qos,_} when (is_integer(Qos) orelse Qos==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'PublishService'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'FakeNumbers'{phone = Phone, created = Created}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {phone,_} when (is_binary(Phone) orelse Phone==[]) -> []; + {created,_} when (is_integer(Created) orelse Created==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'FakeNumbers'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'Draft'{data = Data, status = Status}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {data,_} when is_list(Data) -> []; + {status,_} when (Status=='delete' orelse Status=='get' orelse Status=='update') -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'Draft'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'handler'{name = Name, module = Module, group = Group}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {name,_} when is_atom(Name) -> []; + {module,_} when is_atom(Module) -> []; + {class,_} -> []; + {group,_} when is_atom(Group) -> []; + {config,_} -> []; + {state,_} -> []; + {seq,_} -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'handler'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'cx'{handlers = Handlers, actions = Actions, req = Req, module = Module, lang = Lang, path = Path, session = Session, formatter = Formatter, params = Params, node = Node, client_pid = Client_pid, state = State, from = From, vsn = Vsn}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {handlers,_} when is_list(Handlers) -> []; + {actions,_} when is_list(Actions) -> []; + {req,_} when (Req==[]) -> []; + {module,_} when (is_atom(Module) orelse Module==[]) -> []; + {lang,_} when (is_atom(Lang) orelse Lang==[]) -> []; + {path,_} when (is_binary(Path) orelse Path==[]) -> []; + {session,_} when (is_binary(Session) orelse Session==[]) -> []; + {formatter,_} when (Formatter=='json' orelse Formatter=='bert') -> []; + {params,_} when (is_list(Params) orelse Params==[]) -> []; + {node,_} when (is_atom(Node) orelse Node==[]) -> []; + {client_pid,_} when (Client_pid==[]) -> []; + {state,_} when (State==[]) -> []; + {from,_} when (is_binary(From) orelse From==[]) -> []; + {vsn,_} when (is_binary(Vsn) orelse Vsn==[]) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'cx'), tl(tuple_to_list(D)))]), + CondFuns = [?COND_FUN(is_record(Rec, 'handler'))], + Fields = [Handlers], + FieldNames = [handlers], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'mqtt_topic'{topic = Topic, flags = Flags}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {topic,_} when is_binary(Topic) -> []; + {flags,_} when is_list(Flags) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'mqtt_topic'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'mqtt_session'{client_id = Client_id}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {client_id,_} when is_binary(Client_id) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'mqtt_session'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end; +validate(D = #'mqtt_route'{topic = Topic}, Acc) -> + ErrFields = lists:flatten( + [case {Field, F} of + {topic,_} when is_binary(Topic) -> []; + _ -> Field + end || {Field, F} <- lists:zip(record_info(fields, 'mqtt_route'), tl(tuple_to_list(D)))]), + CondFuns = [], + Fields = [], + FieldNames = [], + case validate(lists:zip3(CondFuns, FieldNames, Fields), [{ErrFields, D}|Acc]) of + ok -> validate(lists:zip(FieldNames,Fields), [{ErrFields, D}|Acc]); + Err -> Err + end. diff --git a/apps/roster/src/test/friend_test.erl b/apps/roster/src/test/friend_test.erl deleted file mode 100644 index 232bf39b56f1ab20e4f06b355acdd570ef998ea4..0000000000000000000000000000000000000000 --- a/apps/roster/src/test/friend_test.erl +++ /dev/null @@ -1,17 +0,0 @@ --module(friend_test). --include("roster.hrl"). --include("roster_test.hrl"). - --export([make_friends/2]). - -send_friend_request(User1PhoneId, User2PhoneId) -> - ok. - -confirm_friend_request(User1PhoneId, User2PhoneId) -> - ok. - -make_friends(User1PhoneId, User2PhoneId) -> -%% Steps -%% 1 - User1 - send friend request -%% 2 - User2 - confirm friend request - ok. \ No newline at end of file diff --git a/apps/roster/src/test/auth_test.erl b/apps/roster/test/auth_test.erl similarity index 100% rename from apps/roster/src/test/auth_test.erl rename to apps/roster/test/auth_test.erl diff --git a/apps/roster/src/test/call_room_test.erl b/apps/roster/test/call_room_test.erl similarity index 100% rename from apps/roster/src/test/call_room_test.erl rename to apps/roster/test/call_room_test.erl diff --git a/apps/roster/src/test/channel_test.erl b/apps/roster/test/channel_test.erl similarity index 100% rename from apps/roster/src/test/channel_test.erl rename to apps/roster/test/channel_test.erl diff --git a/apps/roster/src/test/job_tests.erl b/apps/roster/test/job_tests.erl similarity index 92% rename from apps/roster/src/test/job_tests.erl rename to apps/roster/test/job_tests.erl index a05e0f44113fbf87fe5c4b9ba62eb0f9054a467e..75cefea7569f9c72e01118fee2839a850d76b8ce 100644 --- a/apps/roster/src/test/job_tests.erl +++ b/apps/roster/test/job_tests.erl @@ -1,5 +1,6 @@ -module(job_tests). -compile(export_all). +-include_lib("kernel/include/logger.hrl"). -include_lib("n2o/include/n2o.hrl"). -include_lib("kvs/include/metainfo.hrl"). -include_lib("emqttc/include/emqttc_packet.hrl"). @@ -16,7 +17,7 @@ delete() -> [kvs:put(#'Whitelist'{phone = Phone, created = roster:now_msec()}) || Phone <- Phones ++ [PhoneA]], [roster:purge_user(Phone) || Phone <- [PhoneA | Phones]], [{B, BClientId, _}, {C, _, _}, {A, AClientId, _}] = - [begin C = roster_client:gen_name_reg(Phone), + [begin roster_client:gen_name_reg(Phone), {ClientId, Token} = roster_client:reg_fake_user(Phone), roster_client:receive_drop(), %roster_client:stop_client(C), roster_client:start_cli_receive(ClientId, Token), @@ -60,7 +61,7 @@ delete() -> #'Job'{id = DeleteId, proc = Proc, status=delete} = roster_client:send_receive(AClientId, #'Job'{id=DeleteId,feed_id = {act, <<"publish">>, A},status = delete}), timer:sleep(2000), #'History'{} = roster_client:send_receive(AClientId, #'History'{roster_id = A, feed = {act, <<"publish">>, A}, size = [], status = get}), - #'Job'{id=Id, proc = Proc} = roster_client:send_receive(AClientId, #'Job'{time = roster:now_msec() + 70000, feed_id = {act, <<"publish">>, A}, data = [ + #'Job'{proc = Proc} = roster_client:send_receive(AClientId, #'Job'{time = roster:now_msec() + 70000, feed_id = {act, <<"publish">>, A}, data = [ #'Message'{feed_id = roster:feed_key(p2p, A, B), from = A, to = B, files = [#'Desc'{id = <<"3">>, payload = <<"TestA!">>}], status = []}] , status = init}), #'History'{data=[#'Job'{status=pending}]} = roster_client:send_receive(AClientId, #'History'{roster_id = A, feed = {act, <<"publish">>, A}, size = [], status = get}), @@ -73,10 +74,8 @@ suite() -> [delete()]. check() -> case lists:all(fun(X) -> X == ok end, suite()) of - true -> roster:info("ALL TESTS PASSED", []), true; - false -> roster:info("TEST ERRORS", []), false end. + true -> ?LOG_INFO("ALL TESTS PASSED", []), true; + false -> ?LOG_INFO("TEST ERRORS", []), false end. purge_fake_users() -> length([roster:purge_user(Phone) || #'Auth'{phone = Phone} <- kvs:all('Auth'), byte_size(Phone) < 12]). - - diff --git a/apps/roster/src/test/main_test.erl b/apps/roster/test/main_test.erl similarity index 97% rename from apps/roster/src/test/main_test.erl rename to apps/roster/test/main_test.erl index d9d3166d250a5575e20c047c927b60ecf87ad352..c3aac4b5f2042686d788913f7ebdc104ef9e2243 100644 --- a/apps/roster/src/test/main_test.erl +++ b/apps/roster/test/main_test.erl @@ -1,5 +1,6 @@ -module(main_test). -compile(export_all). +-include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include("roster_test.hrl"). @@ -13,13 +14,13 @@ is_test_session(Features) -> remove_fake_users_data() -> L = length([begin - roster:info(?MODULE, "FakeUserPurge:~p", [Phone]), roster:purge_user(Phone) + ?LOG_INFO("FakeUserPurge:~p", [Phone]), roster:purge_user(Phone) end || #'Auth'{settings = Settings, phone = Phone} <- kvs:all('Auth'), is_test_session(Settings)]), - roster:info(?MODULE, "RemovedFakeUsersCount:~p", [L]). + ?LOG_INFO("RemovedFakeUsersCount:~p", [L]). muc_msgs() -> RoomName = Room = <<"test_room_777">>, - Phones = [{APhone = <<"73777">>, admin}, {<<"79773">>, admin}, {<<"173737">>, member}, {<<"273747">>, member}], + Phones = [{_APhone = <<"73777">>, admin}, {<<"79773">>, admin}, {<<"173737">>, member}, {<<"273747">>, member}], %% Room2 = get_room_id(APhone, RoomName), Counter = length(Phones), roster:purge_room(Room), @@ -76,7 +77,7 @@ muc_msgs() -> [#'Message'{id = BFid, type = [read]}, #'Room'{}] = roster_client:send_receive(DClientId, last_res), %% Edit with msg_id - #'Message'{files = F} = roster_client:send_receive(BClientId, Counter, #'Message'{id=BFid, msg_id = <>, feed_id=#muc{name = Room}, + #'Message'{files = _F} = roster_client:send_receive(BClientId, Counter, #'Message'{id=BFid, msg_id = <>, feed_id=#muc{name = Room}, from=BPhoneId, files = [#'Desc'{id= <<"7">>, payload = <<"Edited by B!">>}], status = edit}), #'Ack'{} = roster_client:send_receive(BClientId, #'Message'{id=BFid, msg_id = <>, feed_id=#muc{name = Room}, diff --git a/apps/roster/src/test/micro_test.erl b/apps/roster/test/micro_test.erl similarity index 89% rename from apps/roster/src/test/micro_test.erl rename to apps/roster/test/micro_test.erl index 793b62026c408a319670b095d58ef467cc70bb76..e0bc62a418bd9ea6d9d8a9eb362f32955207ba33 100644 --- a/apps/roster/src/test/micro_test.erl +++ b/apps/roster/test/micro_test.erl @@ -61,10 +61,10 @@ test_roster_mismatch_data() -> Roster = #'Roster'{id = Rid} = roster:new_roster(P_UUID), kvs:put(#'LinkRoster'{id = R_UUID, phone_id = roster:phone_id(P_UUID, Rid)}), kvs:put(Profile = #'Profile'{phone = P_UUID, rosters = [Rid]}), - {reply, {bert, {io, {error, mismatch_user_data}, P_UUID}}, _, _} = - roster_client:test_info(micro_profile, Profile#'Profile'{status = create, + {reply, {bert, {io, {error, mismatch_user_data}, P_UUID}}, _, _} = + roster_client:test_info(micro_profile, Profile#'Profile'{status = create, rosters = [Roster#'Roster'{status = add, - id = R_UUID, + id = R_UUID, phone = P_UUID}]}, #cx{params = <<"sys_test">>}), {error, not_found} = kvs:get('Profile', P_UUID), %% Check we don't have left overs of new Profile {error, not_found} = kvs:get('LinkProfile', P_UUID), %% Check we don't have left overs of Linkage of new Profile @@ -77,7 +77,7 @@ test_roster_account_already_exists() -> R_UUID = list_to_binary(uuid:to_string(uuid:uuid4())), Roster = #'Roster'{id = Rid} = roster:new_roster(P_UUID), kvs:put(#'LinkRoster'{id = R_UUID, phone_id = roster:phone_id(P_UUID, Rid)}), - kvs:put(Profile = #'Profile'{phone = P_UUID, rosters = [Rid]}), + kvs:put(#'Profile'{phone = P_UUID, rosters = [Rid]}), kvs:put(#'LinkProfile'{id = P_UUID, phone = P_UUID}), {reply, {bert, {io, {error, account_already_exist}, <<>>}}, _, _} = roster_client:test_info(micro_roster, Roster#'Roster'{status = add, id = R_UUID}, #cx{params = <<"sys_test">>}), @@ -92,22 +92,22 @@ test_profile_import_error() -> R_UUID = list_to_binary(uuid:to_string(uuid:uuid4())), Roster = #'Roster'{id = Rid} = roster:new_roster(Phone), kvs:put(#'Index'{id = {nick, Nick}, roster = [50000]}), %% Give nick to some Roster - kvs:put(Profile = #'Profile'{phone = Phone, + kvs:put(Profile = #'Profile'{phone = Phone, services = [], - rosters = [Rid], - settings = [#'Feature'{id = <<"0ed58da1-097e-4241-a3dc-f4d38221e068">>, - key = <<"PHONE">>, - value = Phone, + rosters = [Rid], + settings = [#'Feature'{id = <<"0ed58da1-097e-4241-a3dc-f4d38221e068">>, + key = <<"PHONE">>, + value = Phone, group = <<"PROFILE_DATA">>}]}), {reply, {bert, {io, {error, nick}, P_UUID}}, _, _} = - roster_client:test_info(micro_profile, New = Profile#'Profile'{status = create, + roster_client:test_info(micro_profile, New = Profile#'Profile'{status = create, services = [#'Service'{type = wallet, login = <<"some-data">>}], %% Add some random data to the new Profile - phone = P_UUID, - rosters = [Roster#'Roster'{status = add, + phone = P_UUID, + rosters = [Roster#'Roster'{status = add, nick = Nick, %% Use the same nick for the New Roster (This should cause an error of used nick) - id = R_UUID, + id = R_UUID, phone = P_UUID}]}, #cx{params = <<"sys_test">>}), - Profile /= New, %% New profile has different data (added services), which we should not have in the DB + true = (Profile /= New), %% New profile has different data (added services), which we should not have in the DB {ok, Profile} = kvs:get('Profile', Phone), %% Check that the Profile hasn't change in the DB ok. @@ -121,14 +121,14 @@ test_creation_of_account_with_taken_username() -> {reply, {bert, {io, {error, nick}, P_UUID}}, _, _} = roster_client:test_info(micro_profile, #'Profile'{status = create, phone = P_UUID, - rosters = [#'Roster'{status = add, + rosters = [#'Roster'{status = add, nick = Nick, %% Use the same nick for the New Roster (This should cause an error of used nick) - id = R_UUID, - phone = P_UUID}]}, #cx{params = <<"sys_test">>}), + id = R_UUID, + phone = P_UUID}]}, #cx{params = <<"sys_test">>}), case length(kvs:all('Roster')) =:= RosterAmount of false -> throw("Unwanted Roster!"); true -> ok - end, + end, ok. test_micro() -> @@ -147,7 +147,7 @@ test_micro() -> [#'LinkProfile'{phone = APhone, id = ALinkedPId}] = kvs:index('LinkProfile', phone, APhone), [#'LinkRoster'{phone_id = APhoneId, id = ALinkedAId}] = kvs:index('LinkRoster', phone_id, APhoneId), ALinkedAIdBin = micro:norm_uuid(ALinkedAId), - ALinkedPIdBin = micro:norm_uuid(ALinkedPId), + _ALinkedPIdBin = micro:norm_uuid(ALinkedPId), OldModule = application:get_env(roster, validate_token, ?MODULE), application:set_env(roster, validate_token, ?MODULE), {ok, ALinkedAIdBin} = validate_token(<>), @@ -301,7 +301,7 @@ test_account2() -> {ok, #'Profile'{rosters = [RosterId]}} = kvs:get('Profile', APhoneUuid), {ok, #'Roster'{}} = kvs:get('Roster', RosterId), APhoneId = roster:phone_id(APhoneUuid, RosterId), - [#'LinkRoster'{id = ARosterLnkId, phone_id = APhoneId}] + [#'LinkRoster'{phone_id = APhoneId}] = kvs:index('LinkRoster', phone_id, roster:phone_id(APhoneUuid, RosterId)), [roster_client:stop_client(ClientId) || ClientId <- [BridgeClient, AClientId]], ok. @@ -323,37 +323,40 @@ suite() -> check() -> roster:check(?MODULE). add_users(UIDs)-> - BridgeClient = ?SYS_BRIDGE_TEST_CLIENT, - roster_client:start_client(BridgeClient), - L=[begin - UUID = micro:norm_uuid(micro:to_uuid(phone, U)), - micro:purge_user(UUID), - kvs:put(#'Whitelist'{phone = UUID, created = roster:now_msec()}), - ClientId = roster_client:gen_name(UUID), - #'Profile'{status = create, rosters=[#'Roster'{id = RUUID, phone = UUID}]} = roster_client:send_receive(BridgeClient, - #'Profile'{phone = UUID, rosters = [#'Roster'{id = micro:norm_uuid(uuid:uuid4()), phone = UUID}], status = create}), - {ok, #'Profile'{rosters = [RId]}} = kvs:get('Profile', UUID), - {UUID, RId, ClientId,MStatus} - end || {U,MStatus} <- UIDs] + BridgeClient = ?SYS_BRIDGE_TEST_CLIENT, + roster_client:start_client(BridgeClient), + [begin + UUID = micro:norm_uuid(micro:to_uuid(phone, U)), + micro:purge_user(UUID), + kvs:put(#'Whitelist'{phone = UUID, created = roster:now_msec()}), + ClientId = roster_client:gen_name(UUID), + #'Profile'{status = create, rosters=[#'Roster'{phone = UUID}]} = + roster_client:send_receive(BridgeClient, + #'Profile'{phone = UUID, + rosters = [#'Roster'{id = micro:norm_uuid(uuid:uuid4()), phone = UUID}], + status = create}), + {ok, #'Profile'{rosters = [RId]}} = kvs:get('Profile', UUID), + {UUID, RId, ClientId,MStatus} + end || {U,MStatus} <- UIDs] . del_rosters() -> BridgeClient = ?SYS_BRIDGE_TEST_CLIENT, - Users=[{Auuid, AR, AClientId,AS}, {Buuid, BR, BClientId,BS}, {Cuuid, CR, CClientId,CS} | _ ]= - add_users(Phones=[APhone|_]=[{<<"773">>,admin},{<<"5333">>,admin},{ <<"377">>,user}]), + Users=[{Auuid, AR, AClientId, _AS}, {_Buuid, _BR, BClientId, _BS}, {_Cuuid, _CR, CClientId, _CS} | _ ] = + add_users(Phones = [{<<"773">>,admin},{<<"5333">>,admin},{ <<"377">>,user}]), TokenPattern = <<"valid,">>, - RoomNames = [<<"test_room_A">>, <<"test_room_B">>, <<"test_room_C">>, - <<"test_room_D">>, <<"test_room_E">>, <<"test_room_F">>], - Counter = length(Users), - [{A,MA},{B,MB},{C,MC}|_]= [begin application:set_env(roster, validate_token, ?MODULE), - [#'LinkRoster'{id = RosterLnkId, phone_id = PhoneId}] = kvs:index('LinkRoster', phone_id, PhoneId=roster:phone_id(UUID, R)), - {ok, AccId} = validate_token(<>), + %% RoomNames = [<<"test_room_A">>, <<"test_room_B">>, <<"test_room_C">>, + %% <<"test_room_D">>, <<"test_room_E">>, <<"test_room_F">>], + [{A, _MA}, {B, _MB}, {C, _MC} | _] = + [begin application:set_env(roster, validate_token, ?MODULE), + [#'LinkRoster'{id = RosterLnkId, phone_id = PhoneId}] = kvs:index('LinkRoster', phone_id, PhoneId=roster:phone_id(UUID, R)), + {ok, AccId} = validate_token(<>), #'Profile'{phone = UUID, rosters = [#'Roster'{id = RosterId, phone = UUID}]} - = start_cli_receive(ClientId, Token2 = <>), + = start_cli_receive(ClientId, <>), {roster:phone_id(UUID, RosterId),#'Member'{presence = online, phone_id = PhoneId, status = Status}} end || {UUID,R,ClientId,Status}<-Users], roster_client:set_filer([AClientId, BClientId, CClientId], filter_friend), - OldModule = application:get_env(roster, validate_token, ?MODULE), + application:get_env(roster, validate_token, ?MODULE), {ok, #'Profile'{phone = Auuid, rosters=[AR]}} = kvs:get('Profile', Auuid), % {ok, #'LinkProfile'{id = ABinPhoneUuid, phone = APhoneUuid}} = kvs:get('LinkProfile', ABinPhoneUuid), #'Roster'{id=Auuid2, status = add} = roster_client:send_receive(BridgeClient, @@ -371,11 +374,11 @@ del_rosters() -> timer:sleep(1000), #'Profile'{settings = Settings, status=update}= roster_client:send_receive(BridgeClient,3, #'Profile'{status =update, phone = Auuid, settings = Settings = [#'Feature'{id = <<"dummy_id">>, key = <<"dummy_key">>}]}), - JobDelay = application:get_env(roster, job_delay, 1), + %% JobDelay = application:get_env(roster, job_delay, 1), application:set_env(roster, job_delay, 1), #'History'{} = roster_client:send_receive(AClientId, 3,#'History'{roster_id = A, feed = {act, <<"publish">>, A}, size = [], status = get}), %[_, ARId]=roster:parts_phone_id(A), [_, BRId]=roster:parts_phone_id(B) - #'Job'{proc = Proc} = roster_client:send_receive(BClientId, #'Job'{time = roster:now_msec() + 70000, feed_id = {act, <<"publish">>, B}, data = [ + #'Job'{} = roster_client:send_receive(BClientId, #'Job'{time = roster:now_msec() + 70000, feed_id = {act, <<"publish">>, B}, data = [ #'Message'{feed_id = roster:feed_key(p2p, A, B), from = B, to = A, files = [#'Desc'{id = <<"3">>, payload = <<"TestA!">>}], status = []}] , status = init}), timer:sleep(1000), @@ -421,7 +424,7 @@ del_rosters() -> %% #'Job'{proc = Proc} = roster_client:send_receive(BClientId, #'Job'{id = UpId, time = roster:now_msec() + 70000, feed_id = {act, <<"publish">>, B}, settings = ["New"], data = [ %% #'Message'{feed_id = roster:feed_key(p2p, A, B), from = B, to = A, files = [#'Desc'{id = <<"7">>, payload = <<"Next 3 TestA!">>}], status = []}] %% , status = update}), - [#'LinkRoster'{id = Auuid1Link, phone_id = PhoneId}] = kvs:index('LinkRoster', phone_id, roster:phone_id(Auuid, AR)), + [#'LinkRoster'{id = Auuid1Link}] = kvs:index('LinkRoster', phone_id, roster:phone_id(Auuid, AR)), roster_client:receive_drop(), %% [roster_client:send_receive(AClientId, Counter+1, #'Room'{id = Room, status = create, type = group, %% create room with 4 members %% name = Room, admins = [Owner#'Member'{alias = <<"Rick">>} | TAdmins], members = Members}) @@ -441,4 +444,4 @@ del_rosters() -> validate_token(<<"valid,", UserId/binary>>) -> {ok, UserId}; validate_token(<<"expired,", UserId/binary>>) -> {expired, UserId}; -validate_token(_) -> error. \ No newline at end of file +validate_token(_) -> error. diff --git a/apps/roster/src/test/rest_test.erl b/apps/roster/test/rest_test.erl similarity index 95% rename from apps/roster/src/test/rest_test.erl rename to apps/roster/test/rest_test.erl index 5019b979ea7c76530ac0705fec43ebb04ffa2a42..70b75bb808d0459d9901eae3eaeda5cf2c8ddb98 100644 --- a/apps/roster/src/test/rest_test.erl +++ b/apps/roster/test/rest_test.erl @@ -1,5 +1,6 @@ -module(rest_test). -compile(export_all). +-include_lib("kernel/include/logger.hrl"). -include_lib("n2o/include/n2o.hrl"). -include_lib("kvs/include/metainfo.hrl"). -include("roster.hrl"). @@ -13,12 +14,12 @@ suite() -> ]. check() -> case lists:all(fun(X) -> X == ok end, suite()) of - true -> roster:info("ALL REST TESTS PASSED", []), true; - false -> roster:info("TEST ERRORS", []), false end. + true -> ?LOG_INFO("ALL REST TESTS PASSED", []), true; + false -> ?LOG_INFO("TEST ERRORS", []), false end. test_rest_message_from_sys_user() -> Message = <<"Message from system User">>, - + %% Create User1 FakeProfileUUID1 = list_to_binary(uuid:to_string(uuid:uuid4())), FakeRosterUUID1 = list_to_binary(uuid:to_string(uuid:uuid4())), @@ -26,7 +27,7 @@ test_rest_message_from_sys_user() -> kvs:put(#'LinkProfile'{id = FakeProfileUUID1, phone = FakeProfileUUID1}), kvs:put(#'LinkRoster'{id = FakeRosterUUID1, phone_id = roster:roster_id(FakeProfileUUID1)}), FakePhoneId1 = roster:phone_id(FakeProfileUUID1), - + %% Create User2 FakeProfileUUID2 = list_to_binary(uuid:to_string(uuid:uuid4())), FakeRosterUUID2 = list_to_binary(uuid:to_string(uuid:uuid4())), @@ -46,7 +47,7 @@ test_rest_message_from_sys_user() -> test_rest_message_from_sys_user2() -> Message = <<"Message from system User">>, - + %% Create User1 FakeProfileUUID1 = list_to_binary(uuid:to_string(uuid:uuid4())), FakeRosterUUID1 = list_to_binary(uuid:to_string(uuid:uuid4())), @@ -54,14 +55,14 @@ test_rest_message_from_sys_user2() -> kvs:put(#'LinkProfile'{id = FakeProfileUUID1, phone = FakeProfileUUID1}), kvs:put(#'LinkRoster'{id = FakeRosterUUID1, phone_id = roster:roster_id(FakeProfileUUID1)}), FakePhoneId1 = roster:phone_id(FakeProfileUUID1), - + %% Create User2 ProfileUUID = list_to_binary(uuid:to_string(uuid:uuid4())), AccountId = list_to_binary(uuid:to_string(uuid:uuid4())), - LinkProfile = #'Profile'{status = create, phone = ProfileUUID, + LinkProfile = #'Profile'{status = create, phone = ProfileUUID, rosters = [#'Roster'{id = AccountId, phone = ProfileUUID, names = <<"Peshou">>}]}, roster_client:start_client(?SYS_BRIDGE_TEST_CLIENT), - Res = roster_client:send_receive(?SYS_BRIDGE_TEST_CLIENT, LinkProfile), + roster_client:send_receive(?SYS_BRIDGE_TEST_CLIENT, LinkProfile), {ok, #'LinkRoster'{phone_id = FakePhoneId2}} = kvs:get('LinkRoster', AccountId), ok = rest_gw:add_as_friend(<<"sys">>, FakePhoneId1, FakePhoneId2), %% Send friend request, where FakePhoneId1 is set as a system user, and accept automatically @@ -74,7 +75,7 @@ test_rest_message_from_sys_user2() -> test_rest_message_from_regular_user() -> Message = <<"What' up my man!!">>, - + %% Create User1 FakeProfileUUID1 = list_to_binary(uuid:to_string(uuid:uuid4())), FakeRosterUUID1 = list_to_binary(uuid:to_string(uuid:uuid4())), @@ -82,7 +83,7 @@ test_rest_message_from_regular_user() -> kvs:put(#'LinkProfile'{id = FakeProfileUUID1, phone = FakeProfileUUID1}), kvs:put(#'LinkRoster'{id = FakeRosterUUID1, phone_id = roster:roster_id(FakeProfileUUID1)}), FakePhoneId1 = roster:phone_id(FakeProfileUUID1), - + %% Create User2 FakeProfileUUID2 = list_to_binary(uuid:to_string(uuid:uuid4())), FakeRosterUUID2 = list_to_binary(uuid:to_string(uuid:uuid4())), @@ -103,4 +104,4 @@ test_rest_message_from_regular_user() -> ok = case roster_db:get_chain('Message', roster:feed_key(#p2p{from = FakePhoneId1, to = FakePhoneId2})) of %% Message is successful [#'Message'{type = [sys]}, #'Message'{files = [#'Desc'{payload = Message}]} | _] -> ok; [] -> error - end. \ No newline at end of file + end. diff --git a/apps/roster/src/test/room_test.erl b/apps/roster/test/room_test.erl similarity index 95% rename from apps/roster/src/test/room_test.erl rename to apps/roster/test/room_test.erl index de839c777dee93adee0fccc57b20f8403563560f..4c008832eb608cdf53faf04ba312d0934d6edd83 100644 --- a/apps/roster/src/test/room_test.erl +++ b/apps/roster/test/room_test.erl @@ -7,6 +7,7 @@ check_alias_increment/0, add_many_members/0, link/0, + suite/0, test_joinlink/0 ]). -define(BLANK_LINK, <<"UNDEFINED">>). @@ -74,19 +75,19 @@ add_many_members() -> status = add, type = group, members = to_member(lists:nthtail(2, TestClients))}), %% check what all members aliases is equal to their first name - [ReceivedAlias == SentFName || {#'Member'{alias = ReceivedAlias}, #'Member'{names = SentFName}} <- lists:zip(Members, to_member(lists:nthtail(2, TestClients)))], + [true] = lists:usort([ReceivedAlias == SentFName || {#'Member'{alias = ReceivedAlias}, #'Member'{names = SentFName}} <- lists:zip(Members, to_member(lists:nthtail(2, TestClients)))]), [roster_client:stop_client(ClientId) || {_, ClientId, _} <- TestClients], ok. link() -> RoomName = Room = <<"test_room_link">>, - Phones = [{APhone = <<"1">>, admin}, {BPhone = <<"2">>, admin}, {<<"3">>, member}, {<<"4">>, member}], + Phones = [{APhone = <<"1">>, admin}, {_BPhone = <<"2">>, admin}, {<<"3">>, member}, {<<"4">>, member}], %% Room2 = get_room_id(APhone, RoomName), Counter = length(Phones), roster:purge_room(RoomName), Feed = #muc{name = Room}, PCs = [{_, AClientId, _, _} | [{BPhoneId, BClientId, _, _}, - {CPhoneId, _, CM, _}, {DPhoneId, _, _, _}] = _] = + {CPhoneId, _, CM, _}, {_DPhoneId, _, _, _}] = _] = [begin roster:purge_user(Phone), {ClientId, Token} = roster_client:reg_fake_user(Phone), roster_client:start_cli_receive(ClientId, Token), @@ -120,7 +121,7 @@ link() -> roster:purge_user(EPhone = <<"5">>), {EClientId, EToken} = roster_client:reg_fake_user(EPhone), roster_client:start_cli_receive(EClientId, EToken), - EM = #'Member'{presence = online, feed_id = Feed, phone_id = EPhoneId = roster:phone_id(EPhone), status = member}, + EM = #'Member'{presence = online, feed_id = Feed, phone_id = _EPhoneId = roster:phone_id(EPhone), status = member}, #'Room'{status = add, unread = U} = roster_client:send_receive(AClientId, Counter + 1, #'Room'{status = add, id = RoomID, admins = [EM]}), true = is_integer(U), @@ -145,7 +146,7 @@ link() -> #'History'{feed = #muc{name = RoomID}, roster_id = FPhoneId, entity_id = MesID, size = -30, status = get}), true = length(Msgs) == SIZE + 1, %% Send message from user F - #'Message'{id = MesID2} = roster_client:send_receive(FClientId, Counter + 2, #'Message'{feed_id = #muc{name = Room}, from = FPhoneId, to = Room, + #'Message'{id = _MesID2} = roster_client:send_receive(FClientId, Counter + 2, #'Message'{feed_id = #muc{name = Room}, from = FPhoneId, to = Room, files = [#'Desc'{id = <<"9as120">>, payload = <<"Some Text From F user">>}], status = []}), #'Room'{readers = [_ | _]} = roster_client:send_receive(FClientId, #'Room'{id = RoomID, status = get}), @@ -281,4 +282,4 @@ suite() -> test_joinlink() ]. -check() -> roster:check(?MODULE). \ No newline at end of file +check() -> roster:check(?MODULE). diff --git a/apps/roster/src/test/roster_test.erl b/apps/roster/test/roster_test.erl similarity index 96% rename from apps/roster/src/test/roster_test.erl rename to apps/roster/test/roster_test.erl index 46c6aec4aa63ac81d97d50f7e24f4aa333e26c95..fd20c90f817b5ec91e4be5ae07623e00406a8c19 100644 --- a/apps/roster/src/test/roster_test.erl +++ b/apps/roster/test/roster_test.erl @@ -1,5 +1,6 @@ -module(roster_test). -compile(export_all). +-include_lib("kernel/include/logger.hrl"). -include_lib("n2o/include/n2o.hrl"). -include_lib("kvs/include/metainfo.hrl"). -include_lib("emqttc/include/emqttc_packet.hrl"). @@ -100,14 +101,14 @@ test_friend_multi() -> roster_client:stop_client(AClientId2), roster_client:stop_client(AClientId), roster_client:stop_client(BClientId). test_friend_introduction_msg() -> - Phones = [APhone, _BPhone] = [<<"7741">>, <<"3341">>], + Phones = [<<"7741">>, <<"3341">>], Counter = length(Phones), [{From, AClientId}, {To, BClientId}] = [begin roster:purge_user(Phone), {ClientId, Token} = roster_client:reg_fake_user(Phone), roster_client:start_cli_receive(ClientId, Token), [PhoneId] = roster_client:rosters(ClientId, Phone), {PhoneId, ClientId} - end || Phone <- Phones], %% create and connect fake users + end || Phone <- Phones], %% create and connect fake users roster_client:set_filer([AClientId, BClientId], filter_friend), Setting = #'Feature'{id = list_to_binary(uuid:to_string(uuid:uuid4())), key = <<"CONTACT_INTRODUCTION">>, @@ -124,15 +125,14 @@ test_friend_introduction_msg() -> = roster_client:receive_last2(BClientId, Counter + 1, #'Friend'{phone_id = To, friend_id = From, status = confirm}, 2). test_ack_message() -> - Phones = [APhone, _BPhone] = [<<"7741">>, <<"3341">>], - Counter = length(Phones), + Phones = [<<"7741">>, <<"3341">>], [{From, AClientId}, {To, BClientId}] = [begin roster:purge_user(Phone), {ClientId, Token} = roster_client:reg_fake_user(Phone), roster_client:start_cli_receive(ClientId, Token), [PhoneId] = roster_client:rosters(ClientId, Phone), {PhoneId, ClientId} end || Phone <- Phones], %% create and connect fake users \ - + roster_client:set_filer([AClientId, BClientId], filter_friend), Setting = #'Feature'{id = list_to_binary(uuid:to_string(uuid:uuid4())), key = <<"CONTACT_INTRODUCTION">>, @@ -141,7 +141,7 @@ test_ack_message() -> roster_client:send(AClientId, #'Friend'{phone_id = From, friend_id = To, settings = [Setting], status = request}), roster_client:send(BClientId, #'Friend'{phone_id = To, friend_id = From, status = confirm}), roster_client:send_receive(AClientId, last_res), - + roster_client:send(AClientId, #'Message'{msg_id = <<"some_msg_id1">>, feed_id = roster:feed_key(p2p, From, To), from = From, @@ -183,7 +183,7 @@ test_roster() -> [kvs:put(#'Whitelist'{phone = Phone, created = roster:now_msec()}) || Phone <- Phones], Counter = length(Phones), [roster:purge_user(Phone) || Phone <- Phones], - [{APhoneId, AClientId, AToken}, {BPhoneId, BClientId, BToken}] = + [{APhoneId, AClientId, AToken}, {BPhoneId, BClientId, _BToken}] = [begin {ClientId, Token} = roster_client:reg_fake_user(Phone), roster_client:start_cli_receive(ClientId, Token), @@ -359,8 +359,12 @@ test_roster() -> #'Contact'{reader = _LastId}]}] }] = roster_client:send_receive(AClientId, last_res), - #'Message'{id = ImageId2} = roster_client:send_receive(BClientId, 2, #'Message'{feed_id = Feed, from = BPhoneId, to = APhoneId, - files = [#'Desc'{id = <<"71">>, mime = <<"image">>, payload = <<"Image mime test!">>}, #'Desc'{id = <<"72">>, mime = <<"thumb">>, payload = <<"Image thumb test!">>, parentid = <<"71">>}], status = []}), + #'Message'{id = _ImageId2} = + roster_client:send_receive(BClientId, 2, + #'Message'{feed_id = Feed, from = BPhoneId, to = APhoneId, + files = [#'Desc'{id = <<"71">>, mime = <<"image">>, payload = <<"Image mime test!">>}, + #'Desc'{id = <<"72">>, mime = <<"thumb">>, payload = <<"Image thumb test!">>, parentid = <<"71">>}], + status = []}), #'Message'{id = ImageId} = roster_client:send_receive(BClientId, 2, #'Message'{feed_id = Feed, from = BPhoneId, to = APhoneId, files = [#'Desc'{id = <<"711">>, mime = <<"image">>, payload = <<"Image mime test2!">>}, #'Desc'{id = <<"712">>, mime = <<"thumb">>, payload = <<"Image mime test2!">>, parentid = <<"711">>}], status = []}), @@ -464,7 +468,7 @@ test_roster() -> %% add short audio test message for subscribe SpeechUrl = <<"https://www.googleapis.com/storage/v1/b/cloud-samples-tests/o/speech%2fbrooklyn.flac?alt=media">>, - SpeechText = <<"how old is the Brooklyn Bridge">>, + %% SpeechText = <<"how old is the Brooklyn Bridge">>, %% SpeechUrl = <<"https://nynja-defaults.s3.us-west-2.amazonaws.com/3cf97e8e0f1fe6001c9c1f01e90e7f25e29cdcc4ac4ef1df9658ae341a87526c_record_1561477824464.m4a">>, %% SpeechText = <<"don't know why I don't know how to pay pay pay pay people from their how to send it to another wallet">>, @@ -507,9 +511,10 @@ test_roster() -> #io{code = #ok{code = transcribe}, data = LongAudioSubscribeId} = roster_client:send_receive(AClientId, 1, - UpdLongMsg = UpdMessage#'Message'{id = LongAudioSubscribeId, - files = [UpdDesc#'Desc'{data = lists:keystore(<<"TYPE">>, #'Feature'.key, UpdData, UpdFeature#'Feature'{value = <<"long">>})}]}, - 15000), + UpdMessage#'Message'{ + id = LongAudioSubscribeId, + files = [UpdDesc#'Desc'{data = lists:keystore(<<"TYPE">>, #'Feature'.key, UpdData, UpdFeature#'Feature'{value = <<"long">>})}]}, + 15000), % This test doesn't pass because of problem in google account (gs_bucket in sys_config) % #'Message'{status = update, @@ -608,8 +613,7 @@ test_reg(Phone) when ?HOST == ?LOC -> roster_client:start_cli_receive(ClientId, Token3), roster_client:send_receive(ClientId, #'Profile'{status = get}), {ok, #'Auth'{type = [], token = Token3}} = kvs:get('Auth', ClientId), - roster_client:stop_client(ClientId), roster_client:stop_client(RegClientId); -test_reg(Phone) -> roster:info("test_reg(~p) skipped", [Phone]), ok. + roster_client:stop_client(ClientId), roster_client:stop_client(RegClientId). test_reg() when ?HOST == ?LOC -> roster:purge_user(Phone = <<"7727">>), @@ -661,8 +665,7 @@ test_reg() when ?HOST == ?LOC -> #io{code = #ok{code = logout}} = roster_client:send_receive(ClientId, #'Auth'{client_id = ClientId, type = logout}), roster_client:stop_client(ClientId), timer:sleep(500), - {error, not_found} = kvs:get('Auth', ClientId), ok; -test_reg() -> roster:info("test_reg() skipped", []), ok. + {error, not_found} = kvs:get('Auth', ClientId), ok. test_reg_jwt() when ?HOST == ?LOC -> kvs:put(#'Whitelist'{phone = <<"22">>, created = roster:now_msec()}), @@ -682,8 +685,7 @@ test_reg_jwt() when ?HOST == ?LOC -> roster_client:start_cli_receive(ClientId, Token), roster_client:receive_drop(), 1 = length(kvs:index('Auth', phone, Phone)), - roster_client:stop_client(ClientId); -test_reg_jwt() -> roster:info("test_reg_jwt() skipped", []), ok. + roster_client:stop_client(ClientId). %% Remote test for Staging ad Prod test_p2p_staging()-> test_latency("35.198.110.223", [], 70). @@ -694,11 +696,11 @@ test_latency() -> test_latency(?HOST, [], 10). test_latency(Server, Opts, MsgNumber) -> Phones = [_PhoneA, _] = [<<"71077">>, <<"72077">>], Counter = length(Phones), - [{FromRoster = #'Roster'{id = FromId, userlist = [_|Fs]}, AClientId, _}, {ToRoster = #'Roster'{id = ToId}, BClientId, _}] = + [{#'Roster'{id = FromId, userlist = [_|Fs]}, AClientId, _}, {#'Roster'{id = ToId}, BClientId, _}] = [begin roster:purge_user(Phone), {ClientId, Token} = roster_client:reg_fake_user(Phone, <<"DevKey_", Phone/binary>>, 1000, [{host, Server}|Opts]), - #'Profile'{rosters = [Roster = #'Roster'{id = RosteId}]} = roster_client:start_cli_receive(ClientId, Token, 0, [{host, Server}|Opts]), + #'Profile'{rosters = [Roster = #'Roster'{}]} = roster_client:start_cli_receive(ClientId, Token, 0, [{host, Server}|Opts]), {Roster, ClientId, Token} end || Phone <- Phones], [From, To]= [roster:phone_id(Phone, Id)||{Phone, Id} <-lists:zip(Phones, [FromId, ToId])], @@ -710,26 +712,28 @@ test_latency(Server, Opts, MsgNumber) -> _ -> ok end, Feed = roster:feed_key(p2p, From, To), - {Mx, Mn, Average} = lists:foldl(fun(_, {Max, Min, Av}) -> - T = erlang:timestamp(), - roster_client:send_receive(AClientId, Counter, #'Message'{feed_id = Feed, from = From, to = To, - files = [#'Desc'{id = <<"7">>, payload = <<"TestA!">>}], status = []}), - roster:info(?MODULE, "timestamp:A->B: ~p ms", [TS = timer:now_diff(erlang:timestamp(), T) / 1000]), - T2 = erlang:timestamp(), - roster_client:send_receive(BClientId, Counter, #'Message'{feed_id = Feed, from = To, to = From, - files = [#'Desc'{id = <<"3">>, payload = <<"TestB!">>}], status = []}), - roster:info(?MODULE, "timestamp:B->A: ~p ms", [TS2 = timer:now_diff(erlang:timestamp(), T2) / 1000]), - {max(max(Max, TS), TS2), min(min(Min, TS), TS2), Av + TS + TS2} - end, {0, 5000, 0}, lists:seq(1, MsgNumber)), - roster:info(?MODULE, "time summary:~p messages sent, max time: ~p, min time: ~p, average time: ~p", - [MsgNumber * 2, Mx, Mn, Average / (MsgNumber * 2)]), + {Mx, Mn, Average} = + lists:foldl(fun(_, {Max, Min, Av}) -> + T = erlang:timestamp(), + roster_client:send_receive(AClientId, Counter, #'Message'{feed_id = Feed, from = From, to = To, + files = [#'Desc'{id = <<"7">>, payload = <<"TestA!">>}], status = []}), + TS = timer:now_diff(erlang:timestamp(), T), + ?LOG_INFO("timestamp:A->B: ~p ms", [ TS / 1000]), + T2 = erlang:timestamp(), + roster_client:send_receive(BClientId, Counter, #'Message'{feed_id = Feed, from = To, to = From, + files = [#'Desc'{id = <<"3">>, payload = <<"TestB!">>}], status = []}), + TS2 = timer:now_diff(erlang:timestamp(), T2), + ?LOG_INFO("timestamp:B->A: ~p ms", [ TS2 / 1000]), + {max(max(Max, TS), TS2), min(min(Min, TS), TS2), Av + TS + TS2} + end, {0, 5000, 0}, lists:seq(1, MsgNumber)), + ?LOG_INFO("time summary:~p messages sent, max time: ~p, min time: ~p, average time: ~p", + [MsgNumber * 2, Mx, Mn, Average / (MsgNumber * 2)]), roster_client:stop_client(AClientId), roster_client:stop_client(BClientId). test_update_contacts() -> PhoneA = <<"357">>, AClientId = roster_client:gen_name(PhoneA), Phones = [<<"53533">>, <<"53774">>], - length(Phones), [roster:purge_user(Phone) || Phone <- [PhoneA | Phones]], PCs = [{B, BClientId, _}, {C, CClientId, TC}, {From, AClientId, TA}] = [begin {ClientId, Token} = roster_client:reg_fake_user(Phone), @@ -795,7 +799,7 @@ test_multi_muc() -> Phones = [{APhone = <<"001">>, admin}, {<<"002">>, admin}, {CPhone = <<"003">>, member}, {<<"004">>, member}], [roster:purge_room(R) || R <- RoomNames], Counter = length(Phones), - PCs = [{APhoneId, AClientId, _, _} | [{_, _, _, _}, {CPhoneId, CClientId, _, _}, {_, _, _, _}]] = + PCs = [{APhoneId, AClientId, _, _} | [{_, _, _, _}, {_CPhoneId, CClientId, _, _}, {_, _, _, _}]] = [begin roster:purge_user(Phone), {ClientId, Token} = roster_client:reg_fake_user(Phone), roster_client:start_cli_receive(ClientId, Token), @@ -901,7 +905,7 @@ test_muc() -> Counter = 1 + length(emqttd_server:subscribers(roster:room_topic(Room))), [] = [C||C<-emqttd_server:subscribers(roster:room_topic(Room)), C == BClientId], - [ok] = [ok||#'Member'{id = BMId, status = removed}<-roster:members(#muc{name = Room})], + [ok] = [ok|| #'Member'{status = removed} <- roster:members(#muc{name = Room})], {BClientId2, BToken2} = roster_client:reg_fake_user(BPhone, <<"DevKey2_", BPhone/binary>>), %% reg other session for B user roster_client:start_cli_receive(BClientId2, BToken2), @@ -967,7 +971,7 @@ test_muc() -> end, Counter, lists:nthtail(2, PCs)), #'Room'{status = add, admins = - [#'Member'{status = admin, alias = AAlias, presence = online, reader = ALastMsgId}, + [#'Member'{status = admin, alias = AAlias, presence = online}, #'Member'{status = admin, presence = online}]} = roster_client:send_receive(AClientId, Counter, #'Room'{status = add, id = Room, admins = Admins, members = Members}), @@ -1126,7 +1130,7 @@ test_muc() -> #'Profile'{rosters = [#'Roster'{roomlist = [#'Room'{members = Mmbrs, admins = Admns}]}]} = roster_client:send_receive(ClientId, #'Profile'{status = get}), [#'Member'{}, #'Member'{}] = Admns++Mmbrs - end || {PhoneId, ClientId, _, Token} <- TPCs], + end || {_PhoneId, ClientId, _, Token} <- TPCs], %% [begin start_cli_receive(ClientId, Token), %% #'Profile'{rosters = [#'Roster'{roomlist = @@ -1221,7 +1225,7 @@ test_muc() -> #'History'{data = [#'Message'{id = LastreadId2 = LastRoomMsgId}]} = roster_client:send_receive(EClientId, #'History'{feed = #muc{name = Room}, roster_id = EPhoneId, entity_id = LastRoomMsgId, size = [], status = get}), - #'History'{data = [#'Message'{id = LastreadId2} | Msgs2] = History2} = roster_client:send_receive(EClientId, + #'History'{data = [#'Message'{id = LastreadId2} | _Msgs2] = History2} = roster_client:send_receive(EClientId, #'History'{feed = #muc{name = Room}, roster_id = EPhoneId, entity_id = 0, size = 30, status = get}), UserHistLimit = length(History2) - 1, @@ -1253,7 +1257,7 @@ test_muc() -> test_muc_msgs() -> RoomName = Room = <<"test_room_777">>, - Phones = [{APhone = <<"73717">>, admin}, {<<"73727">>, admin}, {<<"73737">>, member}, {<<"73747">>, member}], + Phones = [{<<"73717">>, admin}, {<<"73727">>, admin}, {<<"73737">>, member}, {<<"73747">>, member}], %% Room2 = get_room_id(APhone, RoomName), Counter = length(Phones), roster:purge_room(Room), @@ -1482,10 +1486,10 @@ test_muc_msgs() -> test_call_room() -> RoomName = Room = <<"test_room_call">>, - Phones = [{APhone = <<"ACall">>, admin}, {<<"BCall">>, admin}, {<<"CCall">>, member}, {<<"DCall">>, member}], + Phones = [{<<"ACall">>, admin}, {<<"BCall">>, admin}, {<<"CCall">>, member}, {<<"DCall">>, member}], Counter = length(Phones), roster:purge_room(Room), - PCs = [{APhoneId, AClientId, AAdmin} | [{BPhoneId, BClientId, _}, {CPhoneId, CClientId, CMember}, {DPhoneId, DClientId, DMember} | _] = TPCs] = + PCs = [{APhoneId, AClientId, AAdmin}, {BPhoneId, _BClientId, _}, {CPhoneId, _CClientId, CMember}, {DPhoneId, DClientId, DMember} | _] = [begin roster:purge_user(Phone), {ClientId, Token} = roster_client:reg_fake_user(Phone), @@ -1516,7 +1520,7 @@ test_call_room() -> #'Room'{status = create, type = group, id = Room, name = RoomName, admins = [AAdmin]}), #'Room'{status = join, id = Room, type = group, - members = [#'Member'{id = CId, status = member, phone_id = CPhoneId}], admins = []} + members = [#'Member'{status = member, phone_id = CPhoneId}], admins = []} = roster_client:send_receive(ClientId, 2, #'Room'{status = join, type = group, id = Room, name = RoomName, admins = [], members = [CMember]}), #'Room'{status = add, id = Room, type = group, @@ -1562,7 +1566,7 @@ test_call_room() -> #'Room'{type = group} = lists:keyfind(Room, #'Room'.id, Rooms), 2 = length(ets:lookup(mqtt_subscriber, roster:room_topic(Room))), - [roster_client:stop_client(ClientId) || {_, ClientId, _} <- PCs], + [roster_client:stop_client(CId) || {_, CId, _} <- PCs], roster_client:stop_client(ClientId). test_messages() -> @@ -1658,8 +1662,8 @@ suite() -> ]. check() -> case lists:all(fun(X) -> X == ok end, suite()) of - true -> roster:info("ALL TESTS PASSED", []), true; - false -> roster:info("TEST ERRORS", []), false end. + true -> ?LOG_INFO("ALL TESTS PASSED", []), true; + false -> ?LOG_INFO("TEST ERRORS", []), false end. limit('Message', _) -> 2000000. forbid('Message') -> 20. @@ -1687,10 +1691,10 @@ test_migrate() -> migresia:migrate(roster), Arity = record_info(size, 'MigresiaTest'), Arity = tuple_size(element(2, kvs:get('MigresiaTest', 1))), ok; - Res -> roster:info(?MODULE, "~p = migresia:check(roster)", [Res]), + Res -> ?LOG_INFO("~p = migresia:check(roster)", [Res]), {error, test_migration} end catch Err:Rea -> - n2o:error(?MODULE, "Catch:~p~n", [n2o:stack_trace(Err, Rea)]), + ?LOG_ERROR("Catch:~p~n", [n2o:stack_trace(Err, Rea)]), throw({error, migrate}) after catch kvs:delete('schema_migrations', 30171023145947), file:delete(TestFile2) end. @@ -1700,11 +1704,12 @@ bpe() -> [kvs:put(#'Whitelist'{phone = Phone, created = roster:now_msec()}) || Phone <- Phones ++ [PhoneA]], [ begin try roster:purge_user(Phone) catch _:_ -> skip end end || Phone <- [PhoneA | Phones]], [{B, BClientId, _}, {C, _, _}, {A, AClientId, _}] = - [begin C = roster_client:gen_name_reg(Phone), - {ClientId, Token} = roster_client:reg_fake_user(Phone), - roster_client:receive_drop(), %roster_client:stop_client(C), - roster_client:start_cli_receive(ClientId, Token), - [{PhoneId, _, _} | _] = roster:list_rosters(Phone), {PhoneId, ClientId, Token} + [begin + roster_client:gen_name_reg(Phone), + {ClientId, Token} = roster_client:reg_fake_user(Phone), + roster_client:receive_drop(), %roster_client:stop_client(C), + roster_client:start_cli_receive(ClientId, Token), + [{PhoneId, _, _} | _] = roster:list_rosters(Phone), {PhoneId, ClientId, Token} end || Phone <- Phones ++ [PhoneA]], roster_client:set_filer([AClientId, BClientId], filter_friend), [roster_client:send_receive(AClientId, 1, #'Friend'{phone_id = A, friend_id = To, status = request}) || To <- [B, C]], @@ -1777,16 +1782,16 @@ test_bubble() -> roster:purge_room(RoomName), Feed = #muc{name = RoomName}, Users = [{APhoneId, AClientId, _}, - {BPhoneId, BClientId, _}, - {CPhoneId, CClientId, _}, - {DPhoneId, DClientId, _}] = + {_BPhoneId, _BClientId, _}, + {_CPhoneId, _CClientId, _}, + {_DPhoneId, _DClientId, _}] = [begin roster:purge_user(Phone), {ClientId, Token} = roster_client:reg_fake_user(Phone), roster_client:start_cli_receive(ClientId, Token), [PhoneId] = roster_client:rosters(ClientId, Phone), {PhoneId, ClientId, #'Member'{presence = online, feed_id = Feed, phone_id = PhoneId, status = MemberStatus}} - end || {Phone, MemberStatus} <- Phones], %% create and connect fake users + end || {Phone, MemberStatus} <- Phones], %% create and connect fake users ClientIds = [ClientId || {_, ClientId, _} <- Users], roster_client:set_filer(ClientIds, filter), [Admin1, Admin2 | Members] = [Member || {_, _, Member} <- Users], @@ -1804,8 +1809,8 @@ test_bubble() -> name = RoomName, admins = [Admin1#'Member'{alias = <<"Jared_Owner">>}, Admin2#'Member'{alias = <<"Sara_Admin">>}], - members = Members}), %% create room with 4 members - [#'Message'{id = Entity_ID}] = kvs:index('Message', to, <<"test_room_bubble">>), + members = Members}), %% create room with 4 members + [#'Message'{}] = kvs:index('Message', to, <<"test_room_bubble">>), CallBubble1 = #'CallBubbleJSON'{feed_id = RoomName, call_id = <<"bubble_test_call_id">>, type = <<"conference">>, @@ -1884,7 +1889,8 @@ test_room() -> roster:purge_user(EPhone = <<"5">>), {EClientId, EToken} = roster_client:reg_fake_user(EPhone), roster_client:start_cli_receive(EClientId, EToken), - EM = #'Member'{presence = online, feed_id = Feed, phone_id = EPhoneId = roster:phone_id(EPhone), status = member}, + EPhoneId = roster:phone_id(EPhone), + EM = #'Member'{presence = online, feed_id = Feed, phone_id = EPhoneId, status = member}, #'Room'{status = add, unread = U} = roster_client:send_receive(AClientId, Counter + 1, #'Room'{status = add, id = RoomID, admins = [EM]}), true = is_integer(U), @@ -1926,7 +1932,7 @@ test_room() -> clear_room_msg_test() -> RoomName = Room = <<"Room_clear_msgs">>, - Phones = [{APhone = <<"33123">>, admin}, {<<"4141">>, admin}, {<<"412414">>, member}], + Phones = [{<<"33123">>, admin}, {<<"4141">>, admin}, {<<"412414">>, member}], %% Room2 = get_room_id(APhone, RoomName), Counter = length(Phones), roster:purge_room(RoomName), @@ -1976,7 +1982,7 @@ clear_room_msg_test() -> history_update_test() -> RoomName = Room = <<"Room_update_test">>, - Phones = [{APhone = <<"3345">>, admin}, {<<"1236">>, admin},{<<"6697">>, member}], + Phones = [{<<"3345">>, admin}, {<<"1236">>, admin},{<<"6697">>, member}], %% Room2 = get_room_id(APhone, RoomName), Counter = length(Phones), roster:purge_room(Room), @@ -2039,11 +2045,11 @@ history_update_test() -> clear_test_only_admin() -> RoomName = Room = <<"Room_clear_admin_msgs2">>, - Phones = [{APhone = <<"33123000">>, admin}, {<<"4141000">>, admin}], + Phones = [{<<"33123000">>, admin}, {<<"4141000">>, admin}], %% Room2 = get_room_id(APhone, RoomName), Counter = length(Phones), roster:purge_room(Room), - PCs = [{APhoneId, AClientId, _, _}, {BPhoneId, BClientId, _, _}] = + PCs = [{APhoneId, AClientId, _, _}, {_BPhoneId, BClientId, _, _}] = [begin roster:purge_user(Phone), {ClientId, Token} = roster_client:reg_fake_user(Phone), roster_client:start_cli_receive(ClientId, Token), @@ -2304,7 +2310,7 @@ transcribe_test() -> %user B send transcribe MM= #'Message'{id = MsgId, files = [#'Desc'{}, #'Desc'{}] = Descs, status = update} = roster_client:send_receive(BClientId, 2, #'Message'{id = MsgId, feed_id = P2P, from = AUserId, to = BUserId, files = [DescTranscribe#'Desc'{data = [ F1, F2#'Feature'{value = BUserId}]}], status = update}), - roster:info(?MODULE, "@@~p", [MM]), + ?LOG_INFO("@@~p", [MM]), [[PhoneId] = lists:flatten([[P || P <- binary:split((roster:get_data(?USERS_KEY, TData))#'Feature'.value, <<",">>, [global]), P == PhoneId] || #'Desc'{data = TData, mime = <<"transcribe">>} <- Descs, (roster:get_data(?USERS_KEY, TData))#'Feature'.value /= []]) || PhoneId <- [AUserId, BUserId]], @@ -2394,27 +2400,27 @@ pagination_contact_test(N, UserCount) -> end, [roster:purge_user(<>) || I <- lists:seq(1, UserCount)], - Users2 = [{ClientId, PhoneId, Token}|TUsers] = + Users2 = [{_ClientId, PhoneId, _Token} | TUsers] = [roster_data:reg_fake_user(<>)|| I <- lists:seq(1, UserCount)], Phone = roster:phone(PhoneId), - roster:info(?MODULE, "friendsip is started", []), + ?LOG_INFO("friendsip is started", []), [begin roster_client:test_info(roster_friend, #'Friend'{phone_id = PhoneId, friend_id = PId, status = request}, Phone), roster_client:test_info(roster_friend, #'Friend'{phone_id = PId, friend_id = PhoneId, status = confirm}, roster:phone(PId)) end|| {_C, PId, _} <- TUsers], - roster:info(?MODULE, "friends are added", []), + ?LOG_INFO("friends are added", []), #'Roster'{userlist = Users} = roster:roster(roster:roster_id(PhoneId)), UserCount = length(Users), - roster:info(?MODULE, "roster is got", []), + ?LOG_INFO("roster is got", []), {ok, #'Roster'{} = StoredRoster} = kvs:get('Roster', roster:roster_id(PhoneId)), - PageCounter = case UserCount div N of + PageCounter = case UserCount div N of Count when UserCount/N == Count -> Count; Count -> - Count + 1 + Count + 1 end, [LastContactsPage | TContactsPages] = TotalPages = roster:objlist(#'Roster'.userlist, StoredRoster, 0, N), - roster:info(?MODULE, "roster is splitted", []), + ?LOG_INFO("roster is splitted", []), Rem = UserCount rem N, Rem = length(LastContactsPage), PageCounter = length(TotalPages), @@ -2464,7 +2470,7 @@ pagination_test() -> = roster:objlist(#'Roster'.userlist, StoredRoster, LastMsgCreated, N), [[#'ExtendedStar'{from = #'Contact'{phone_id = PhoneId}}]] = roster:objlist(#'Roster'.favorite, StoredRoster, LastMsgCreated, N), - + MsgCreated = lists:nth(5, Creates), [[#'Contact'{}, _ ], [_, _, _]] = roster:objlist(#'Roster'.userlist, StoredRoster, MsgCreated - 1, N), @@ -2500,16 +2506,16 @@ pagination_test() -> #'Profile'{} = roster_client:start_cli_receive(ClientId, Token), ExStars = [#'ExtendedStar'{} | _] = roster_client:send_receive(ClientId, #'ExtendedStar'{}), UserCount = length(ExStars) - RoomCount + 1, - roster:info(?MODULE, "LastMsgCreated = ~p", [LastMsgCreated]), + ?LOG_INFO("LastMsgCreated = ~p", [LastMsgCreated]), #'Profile'{status = get, rosters = [#'Roster'{}]} = roster_client:send_receive(ClientId, #'Profile'{status = get, update = LastMsgCreated}), roster_client:send_receive(ClientId, unsort_last_res), % ??? LastMsgCreated changed to 0 ??? roster_profile.erl:90 - [#'Profile'{status = get} |_] + [#'Profile'{status = get} |_] = roster_client:send_receive(ClientId, 13, #'Profile'{status = get, update = 0, settings = [#'Feature'{key= <<"size">>, value = <<"3">>, group = <<"PAGINATION">>}]}), - %roster:info(?MODULE, "SplittedProfile = ~p", [SplittedProfile]), + %?LOG_INFO("SplittedProfile = ~p", [SplittedProfile]), SplittedProfile = [#'Profile'{status = get, rosters = [#'Roster'{}]} | _] = roster_client:send_receive(ClientId, unsort_last_res), 13 = length(SplittedProfile), @@ -2586,25 +2592,25 @@ test_expired(Host) -> Phone = <<"EXPIRETOKEN">>, {ClientId, Token} = roster_client:reg_fake_user(Phone, <<"DevKey_", Phone/binary>>, 1000, [{host, Host}]), ExpTimeout = case Host of ?LOC -> application:get_env(roster, auth_ttl, 60*15); _ -> 900 end, - Timeout = case Host of ?LOC -> application:set_env(roster, auth_ttl, 1), 1; _-> ExpTimeout end, + case Host of ?LOC -> application:set_env(roster, auth_ttl, 1); _-> ok end, {Time, _} = roster:depicle(Token), - roster:info(?MODULE, "first token: ~p", [{Time, Token}]), - roster:info(?MODULE, "timeout: ~p", [(Time - roster:now_msec())/1000]), + ?LOG_INFO("first token: ~p", [{Time, Token}]), + ?LOG_INFO("timeout: ~p", [(Time - roster:now_msec())/1000]), timer:sleep(Time - roster:now_msec()+1000), - roster:info(?MODULE, "after timeout", []), + ?LOG_INFO("after timeout", []), #'Auth'{type = update, token = Token2} = roster_client:start_cli_receive(ClientId, Token, 0, [{host, Host}]), {Time2, _} = roster:depicle(Token2), - roster:info(?MODULE, "update new token: ~p", [{Time2, Token2}]), + ?LOG_INFO("update new token: ~p", [{Time2, Token2}]), case catch roster_client:send_receive(ClientId, 1, #'Auth'{type = push}, 1000) of {error, _} -> ok; - R -> roster:info(?MODULE, "invalid result: ~p", [R]) + R -> ?LOG_INFO("invalid result: ~p", [R]) end, roster_client:stop_client(ClientId), case Host of ?LOC -> application:set_env(roster, auth_ttl, ExpTimeout); _-> ok end, timer:sleep(1000), roster_client:receive_drop(), #'Profile'{} = roster_client:start_cli_receive(ClientId, Token2, 0, [{host, Host}]), - roster:info(?MODULE, "connect with new token: ~p", [Time2]), + ?LOG_INFO("connect with new token: ~p", [Time2]), roster_client:stop_client(ClientId) end), ok. @@ -2612,7 +2618,7 @@ test_security(Host) -> Phones = [_APhone, _BPhone, _CPhone] = [<<"securetestA">>, <<"securetestB">>, <<"securetestC">>], [kvs:put(#'Whitelist'{phone = Phone, created = roster:now_msec()}) || Phone <- Phones], %% [roster:purge_user(Phone) || Phone <- Phones], - [{APhoneId, AClientId, #'Roster'{userlist = Fs}}, {BPhoneId, BClientId, BRoster},{_CPhoneId, CClientId, CRoster}] = + [{APhoneId, AClientId, #'Roster'{userlist = Fs}}, {BPhoneId, BClientId, _BRoster},{_CPhoneId, CClientId, _CRoster}] = [begin {ClientId, Token} = roster_client:reg_fake_user(Phone, <<"DevKey_", Phone/binary>>, 1000, [{host, Host}]), #'Profile'{rosters = [Roster = #'Roster'{id = RosterId}]} = roster_client:start_cli_receive(ClientId, Token, 0, [{host, Host}]), @@ -2656,7 +2662,7 @@ test_security(Host) -> case catch roster_client:send_receive(CClientId, 1, {callback, SendFun}, 1000) of {error, {timeout, _}} -> ok; #'Message'{} -> throw({error, unsecurity_publish}); - R -> roster:info(?MODULE, "unexpected result: ~p", [R]), + R -> ?LOG_INFO("unexpected result: ~p", [R]), throw({error, unexpected}) end, %% #'History'{data = [_, _]} %% only two message in history @@ -2679,7 +2685,7 @@ test_quick_disconnect(Host) -> [begin %timer:sleep(1), #'Profile'{} = roster_client:start_cli_receive(ClientId, Token, 0, [{host, Host}]), roster_client:stop_client(ClientId), - roster:info(?MODULE, "connect counter: ~p", [I]) + ?LOG_INFO("connect counter: ~p", [I]) end || I <- lists:seq(1, 10)], ok. diff --git a/apps/service/rebar.config b/apps/service/rebar.config deleted file mode 100644 index 3af62d09d982540eee7491d120627685fa701a14..0000000000000000000000000000000000000000 --- a/apps/service/rebar.config +++ /dev/null @@ -1 +0,0 @@ -{deps,[{bert, ".*", {git, "git://github.com/synrc/bert",[]}}]}. \ No newline at end of file diff --git a/apps/service/src/service.app.src b/apps/service/src/service.app.src index 817f38f8592724cb044f45a05c7ee77037f95169..127eb62416e0c91082f9c9909334386dc1f150f5 100644 --- a/apps/service/src/service.app.src +++ b/apps/service/src/service.app.src @@ -1,6 +1,6 @@ {application, service, [{description, "NYNJA Protocol 2.0"}, - {vsn, "1"}, + {vsn, "1.0.1"}, {registered, []}, {applications, [kernel,stdlib,roster,n2o,bert]}, {mod, { service, []}}, diff --git a/noam_console.sh b/noam_console.sh new file mode 100755 index 0000000000000000000000000000000000000000..0e57950e8a21efcaf6fd1d89a6a0a35bf933c1f6 --- /dev/null +++ b/noam_console.sh @@ -0,0 +1,4 @@ +#/bin/sh + +read NC __ <<< `ls -1t _build/local/rel/server/lib/nynja_oam*/ebin/noam_console.beam` +escript ${NC} $* diff --git a/otp.mk b/otp.mk deleted file mode 100644 index 2b74dc0b770eea8de329f9747b101df8c5178ff4..0000000000000000000000000000000000000000 --- a/otp.mk +++ /dev/null @@ -1,33 +0,0 @@ -VM := vm.args -SYS := sys.config -ERL_ARGS := -args_file $(VM) -config $(SYS) -RUN_DIR ?= . -LOG_DIR ?= ./log -empty := -ROOTS := deps apps -space := $(empty) $(empty) -comma := $(empty),$(empty) -VSN := $(shell git rev-parse HEAD | head -c 6) -DATE := $(shell date "+%Y%m%d-%H%M%S") -ERL_LIBS := $(subst $(space),:,$(ROOTS)) - -clean: - rm -f .applist - mad $@ -compile: - mad $@ -.applist: - mad plan -$(RUN_DIR) $(LOG_DIR): - mkdir -p $(RUN_DIR) & mkdir -p $(LOG_DIR) -attach: - to_erl $(RUN_DIR)/ -console: .applist - ERL_LIBS=$(ERL_LIBS) erl +pc unicode $(ERL_ARGS) -eval '[application:ensure_started(A) || A <- $(shell cat .applist)]' -start: $(RUN_DIR) $(LOG_DIR) .applist - RUN_ERL_LOG_GENERATIONS=1000 RUN_ERL_LOG_MAXSIZE=20000000 \ - ERL_LIBS=$(ERL_LIBS) run_erl -daemon $(RUN_DIR)/ $(LOG_DIR)/ "exec $(MAKE) console" -stop: - @kill -9 $(shell ps ax -o pid= -o command=|grep $(RELEASE)|grep $(COOKIE)|awk '{print $$1}') - -.PHONY: compile clean console start diff --git a/priv/protobuf/service_auth/1compile.sh b/priv/protobuf/service_auth/1compile.sh deleted file mode 100755 index 6ad626dee21b25f4fa0b9b918f9631377fd75f88..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/1compile.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -protoc *.proto --java_out=java diff --git a/priv/protobuf/service_auth/Auth.proto b/priv/protobuf/service_auth/Auth.proto deleted file mode 100644 index f5c0b18ecc1ce9a535bbccc356df41f8165da1e0..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/Auth.proto +++ /dev/null @@ -1,30 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Auth.grpc"; -option java_outer_classname = "AuthCls"; -import public "authOs.proto"; -import public "Feature.proto"; -import public "authType.proto"; -import public "Service.proto"; - -message Auth { - string client_id = 1; - string dev_key = 2; - string user_id = 3; - string token = 4; - string data = 5; - authType type = 6; - int64 attempts = 7; - repeated Feature settings = 8; - repeated Service services = 9; - string push = 10; - authOs os = 11; - int64 created = 12; - int64 last_online = 13; -} diff --git a/priv/protobuf/service_auth/AuthError.proto b/priv/protobuf/service_auth/AuthError.proto deleted file mode 100644 index c75a62ed642c9674ff611ca1eb5e0887e805a646..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/AuthError.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "AuthError.grpc"; -option java_outer_classname = "AuthErrorCls"; -import public "Auth.proto"; -import public "authStatus.proto"; - -message AuthError { - repeated authStatus codes = 1; - Auth data = 2; -} diff --git a/priv/protobuf/service_auth/AuthService.proto b/priv/protobuf/service_auth/AuthService.proto deleted file mode 100644 index e5022911c823c2ef613d6a5c57f5266cf7467334..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/AuthService.proto +++ /dev/null @@ -1,19 +0,0 @@ -syntax = "proto3"; -package service_auth; - -import "Auth.proto"; -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "AuthService.grpc"; -option java_outer_classname = "AuthServiceCls"; - -service AuthService { - rpc login (Auth) returns (Auth); - rpc resendSMS (Auth) returns (Auth); - rpc call (Auth) returns (Auth); - rpc confirm (Auth) returns (Auth); - rpc updatePushToken (Auth) returns (Auth); - rpc getSessions (Auth) returns (Auth); - rpc deleteSession (Auth) returns (Auth); - rpc deleteAllSessions (Auth) returns (Auth); -} diff --git a/priv/protobuf/service_auth/Desc.proto b/priv/protobuf/service_auth/Desc.proto deleted file mode 100644 index a1bdff122639882de16d2365361829b1f23827dc..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/Desc.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Desc.grpc"; -option java_outer_classname = "DescCls"; -import public "Feature.proto"; - -message Desc { - string id = 1; - string mime = 2; - string payload = 3; - string parentid = 4; - repeated Feature data = 5; -} diff --git a/priv/protobuf/service_auth/Feature.proto b/priv/protobuf/service_auth/Feature.proto deleted file mode 100644 index 44e5d5a25170e7dd0f9c778227de975185b59a61..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/Feature.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Feature.grpc"; -option java_outer_classname = "FeatureCls"; - -message Feature { - string id = 1; - string key = 2; - string value = 3; - string group = 4; -} diff --git a/priv/protobuf/service_auth/Service.proto b/priv/protobuf/service_auth/Service.proto deleted file mode 100644 index 5255c11383fad0751fad42cd288351aea27fd6ba..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/Service.proto +++ /dev/null @@ -1,22 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Service.grpc"; -option java_outer_classname = "ServiceCls"; -import public "Feature.proto"; -import public "serverType.proto"; -import public "serverStatus.proto"; - -message Service { - string id = 1; - string data = 2; - serverType type = 3; - repeated Feature setting = 4; - int64 expiration = 5; - serverStatus service_status = 6; -} diff --git a/priv/protobuf/service_auth/Tag.proto b/priv/protobuf/service_auth/Tag.proto deleted file mode 100644 index fb29d81cb4aea95a05444d71d1cd7e9419de1629..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/Tag.proto +++ /dev/null @@ -1,18 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Tag.grpc"; -option java_outer_classname = "TagCls"; -import public "tagType.proto"; - -message Tag { - int64 roster_id = 1; - string name = 2; - string color = 3; - tagType tag_status = 4; -} diff --git a/priv/protobuf/service_auth/authOs.proto b/priv/protobuf/service_auth/authOs.proto deleted file mode 100644 index 4308091b1208cd3bf560d0cf6e99d940110c4fad..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/authOs.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "authOs.grpc"; -option java_outer_classname = "authOsCls"; - -enum authOs { - ios = 0; - android = 1; - web = 2; -} - diff --git a/priv/protobuf/service_auth/authStatus.proto b/priv/protobuf/service_auth/authStatus.proto deleted file mode 100644 index 241324b6bfe8c83a39916370f4ae5aa6e6ec79b5..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/authStatus.proto +++ /dev/null @@ -1,23 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "authStatus.grpc"; -option java_outer_classname = "authStatusCls"; - -enum authStatus { - invalid_version = 0; - mismatch_user_data = 1; - number_not_allowed = 2; - session_not_found = 3; - attempts_expired = 4; - invalid_sms_code = 5; - invalid_jwt_code = 6; - permission_denied = 7; - invalid_data = 8; -} - diff --git a/priv/protobuf/service_auth/authType.proto b/priv/protobuf/service_auth/authType.proto deleted file mode 100644 index 0dbed28f069f7bd7c5bf9fbee8e348af63cca828..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/authType.proto +++ /dev/null @@ -1,26 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "authType.grpc"; -option java_outer_classname = "authTypeCls"; - -enum authType { - google_auth = 0; - facebook_auth = 1; - mobile_auth = 2; - email_auth = 3; - voice = 4; - resend = 5; - verify = 6; - push = 7; - logout = 8; - get = 9; - delete = 10; - clear = 11; -} - diff --git a/priv/protobuf/service_auth/presence.proto b/priv/protobuf/service_auth/presence.proto deleted file mode 100644 index 00f79c3f14b24783674e06115917a035e3410fe9..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/presence.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "presence.grpc"; -option java_outer_classname = "presenceCls"; - -enum presence { - online = 0; - offline = 1; -} - diff --git a/priv/protobuf/service_auth/serverStatus.proto b/priv/protobuf/service_auth/serverStatus.proto deleted file mode 100644 index 97d7dd002854abc3904040d78c3a5bc9e9c58a00..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/serverStatus.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverStatus.grpc"; -option java_outer_classname = "serverStatusCls"; - -enum serverStatus { - servie_verified = 0; - service_not_verified = 1; -} - diff --git a/priv/protobuf/service_auth/serverType.proto b/priv/protobuf/service_auth/serverType.proto deleted file mode 100644 index 56279a32238e9ae7b34a68269defae2d99553080..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/serverType.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverType.grpc"; -option java_outer_classname = "serverTypeCls"; - -enum serverType { - email = 0; - wallet = 1; - google_type = 2; - fb = 3; - phone = 4; -} - diff --git a/priv/protobuf/service_auth/tagType.proto b/priv/protobuf/service_auth/tagType.proto deleted file mode 100644 index 18503cdcac81525aa4dd9345f0f0bec4ee577e8e..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_auth/tagType.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_auth; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "tagType.grpc"; -option java_outer_classname = "tagTypeCls"; - -enum tagType { - tag_create = 0; - tag_remove = 1; - tag_edit = 2; -} - diff --git a/priv/protobuf/service_friend/1compile.sh b/priv/protobuf/service_friend/1compile.sh deleted file mode 100755 index 6ad626dee21b25f4fa0b9b918f9631377fd75f88..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_friend/1compile.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -protoc *.proto --java_out=java diff --git a/priv/protobuf/service_friend/Desc.proto b/priv/protobuf/service_friend/Desc.proto deleted file mode 100644 index cfc8b92b45579fc7ec7572b847c4cc8eb984ef3e..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_friend/Desc.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_friend; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Desc.grpc"; -option java_outer_classname = "DescCls"; -import public "Feature.proto"; - -message Desc { - string id = 1; - string mime = 2; - string payload = 3; - string parentid = 4; - repeated Feature data = 5; -} diff --git a/priv/protobuf/service_friend/Feature.proto b/priv/protobuf/service_friend/Feature.proto deleted file mode 100644 index 21240771b56a93cd866c61e8229d5eaf0c5febc4..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_friend/Feature.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_friend; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Feature.grpc"; -option java_outer_classname = "FeatureCls"; - -message Feature { - string id = 1; - string key = 2; - string value = 3; - string group = 4; -} diff --git a/priv/protobuf/service_friend/FriendService.proto b/priv/protobuf/service_friend/FriendService.proto deleted file mode 100644 index 8af2e80293cd9d2075003dc07d08493a3e71aa88..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_friend/FriendService.proto +++ /dev/null @@ -1,19 +0,0 @@ -syntax = "proto3"; -package service_friend; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "FriendService.grpc"; -option java_outer_classname = "FriendServiceCls"; -import public "Friend.proto"; -import public "Contact.proto"; - -service FriendService { - rpc banUser(Friend) returns (Contact); - rpc unbanContact(Friend) returns (Contact); - rpc friendRequest(Friend) returns (Contact); - rpc confirmFrienship(Friend) returns (Contact); - rpc muteContact(Friend) returns (Contact); - rpc unmuteContact(Friend) returns (Contact); - rpc ignoreRequest(Friend) returns (Contact); -} \ No newline at end of file diff --git a/priv/protobuf/service_friend/Service.proto b/priv/protobuf/service_friend/Service.proto deleted file mode 100644 index fa6c1583279207312a127499c6a9ab9a14896fc7..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_friend/Service.proto +++ /dev/null @@ -1,22 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_friend; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Service.grpc"; -option java_outer_classname = "ServiceCls"; -import public "Feature.proto"; -import public "serverType.proto"; -import public "serverStatus.proto"; - -message Service { - string id = 1; - string data = 2; - serverType type = 3; - repeated Feature setting = 4; - int64 expiration = 5; - serverStatus service_status = 6; -} diff --git a/priv/protobuf/service_friend/Tag.proto b/priv/protobuf/service_friend/Tag.proto deleted file mode 100644 index 0bf0dea199c090e36023ba8dd3eaeae432bff754..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_friend/Tag.proto +++ /dev/null @@ -1,18 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_friend; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Tag.grpc"; -option java_outer_classname = "TagCls"; -import public "tagType.proto"; - -message Tag { - int64 roster_id = 1; - string name = 2; - string color = 3; - tagType tag_status = 4; -} diff --git a/priv/protobuf/service_friend/presence.proto b/priv/protobuf/service_friend/presence.proto deleted file mode 100644 index a340e10d6a903ab1b8212b4b99d777c61c697029..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_friend/presence.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_friend; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "presence.grpc"; -option java_outer_classname = "presenceCls"; - -enum presence { - online = 0; - offline = 1; -} - diff --git a/priv/protobuf/service_friend/serverStatus.proto b/priv/protobuf/service_friend/serverStatus.proto deleted file mode 100644 index 837e7ce36722e7f4d149d9151de6cc2e7c0b5219..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_friend/serverStatus.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_friend; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverStatus.grpc"; -option java_outer_classname = "serverStatusCls"; - -enum serverStatus { - servie_verified = 0; - service_not_verified = 1; -} - diff --git a/priv/protobuf/service_friend/serverType.proto b/priv/protobuf/service_friend/serverType.proto deleted file mode 100644 index eccfaae8a537cd46934708252dc7152a7b6349f9..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_friend/serverType.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_friend; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverType.grpc"; -option java_outer_classname = "serverTypeCls"; - -enum serverType { - email = 0; - wallet = 1; - google_type = 2; - fb = 3; - phone = 4; -} - diff --git a/priv/protobuf/service_friend/tagType.proto b/priv/protobuf/service_friend/tagType.proto deleted file mode 100644 index 05a0f94a92505082f9284fb0dfc54891fd2205b6..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_friend/tagType.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_friend; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "tagType.grpc"; -option java_outer_classname = "tagTypeCls"; - -enum tagType { - tag_create = 0; - tag_remove = 1; - tag_edit = 2; -} - diff --git a/priv/protobuf/service_message/1compile.sh b/priv/protobuf/service_message/1compile.sh deleted file mode 100755 index 6ad626dee21b25f4fa0b9b918f9631377fd75f88..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/1compile.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -protoc *.proto --java_out=java diff --git a/priv/protobuf/service_message/Desc.proto b/priv/protobuf/service_message/Desc.proto deleted file mode 100644 index 08869717e1da6dc410f2c897ef46978c0a6cf8e6..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/Desc.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Desc.grpc"; -option java_outer_classname = "DescCls"; -import public "Feature.proto"; - -message Desc { - string id = 1; - string mime = 2; - string payload = 3; - string parentid = 4; - repeated Feature data = 5; -} diff --git a/priv/protobuf/service_message/Feature.proto b/priv/protobuf/service_message/Feature.proto deleted file mode 100644 index c525cc9148869e2e972e7383845d2f915f531222..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/Feature.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Feature.grpc"; -option java_outer_classname = "FeatureCls"; - -message Feature { - string id = 1; - string key = 2; - string value = 3; - string group = 4; -} diff --git a/priv/protobuf/service_message/History.proto b/priv/protobuf/service_message/History.proto deleted file mode 100644 index 9ec7743622212e77f87beff198fedfc2e48e2387..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/History.proto +++ /dev/null @@ -1,25 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "History.grpc"; -option java_outer_classname = "HistoryCls"; -import public "muc.proto"; -import public "p2p.proto"; -import public "historyType.proto"; - -message History { - string roster_id = 1; - oneof feed { - p2p feed20 = 20; - muc feed21 = 21; - } - int64 size = 3; - int64 entity_id = 4; - int64 data = 5; - historyType status = 6; -} diff --git a/priv/protobuf/service_message/Job.proto b/priv/protobuf/service_message/Job.proto deleted file mode 100644 index 0b9faf28ad69b5eac4a34ec4b869d51efbd234c0..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/Job.proto +++ /dev/null @@ -1,34 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Job.grpc"; -option java_outer_classname = "JobCls"; -import public "act.proto"; -import public "jobType.proto"; -import public "Feature.proto"; -import public "Message.proto"; -import public "messageEvent.proto"; -import public "container.proto"; - -message Job { - int64 id = 1; - container container = 2; - act feed_id = 3; - int64 prev = 4; - int64 next = 5; - oneof context { - int64 context60 = 60; - string context61 = 61; - } - int64 proc = 7; - int64 time = 8; - repeated Message data = 9; - repeated messageEvent events = 10; - repeated Feature settings = 11; - jobType status = 12; -} diff --git a/priv/protobuf/service_message/Message.proto b/priv/protobuf/service_message/Message.proto deleted file mode 100644 index 838663d199308c44a6966bd3daf582877143e87f..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/Message.proto +++ /dev/null @@ -1,42 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -import "google/protobuf/Any.proto"; -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Message.grpc"; -option java_outer_classname = "MessageCls"; -import public "messageType.proto"; -import public "messageStatus.proto"; -import public "muc.proto"; -import public "p2p.proto"; -import public "Desc.proto"; -import public "container.proto"; - -message Message { - int64 id = 1; - container container = 2; - oneof feed_id { - muc feed_id30 = 30; - p2p feed_id31 = 31; - } - int64 prev = 4; - int64 next = 5; - string msg_id = 6; - string from = 7; - string to = 8; - int64 created = 9; - repeated Desc files = 10; - messageType type = 11; - oneof link { - int64 link120 = 120; - Message link121 = 121; - } - repeated google.protobuf.Any seenby = 13; - repeated int64 repliedby = 14; - repeated int64 mentioned = 15; - messageStatus mstatus = 16; -} diff --git a/priv/protobuf/service_message/MessageService.proto b/priv/protobuf/service_message/MessageService.proto deleted file mode 100644 index 2663f92a57d3394a2348e01160c70a8d103e1369..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/MessageService.proto +++ /dev/null @@ -1,39 +0,0 @@ -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "MessageService.grpc"; -option java_outer_classname = "MessageServiceCls"; -import public "Message.proto"; -import public "History.proto"; -import public "Job.proto"; - -service MessageService { - //Message - rpc sendMessage(Message) returns (Message); - rpc delete(Message) returns (Message); - rpc deleteForAll(Message) returns (Message); - rpc replyMessage(Message) returns (Message); - rpc editMessage(Message) returns (Message); - rpc updateMessage(Message) returns (Message); - rpc translateMessage(Message) returns (Message); - rpc trancribeMessage(Message) returns (Message); - - //Job - rpc forwardMessage(Job) returns (Job); - rpc createScheduledMessage(Job) returns (Job); - rpc deleteScheduledMessage(Job) returns (Job); - rpc editScheduledMessage(Job) returns (Job); - - //History - rpc getMessage(History) returns (History); - rpc getMessagesToEndFromMessageId(History) returns (History); - rpc getAllMessages(History) returns (History); - rpc readMessage(History) returns (History); - rpc clearMessageHistory(History) returns (History); - rpc getMessageByType(History) returns (History); - rpc getMessageByTypeWithPagination(History) returns (History); - rpc getRepliedMessages(History) returns (History); - rpc getMessagesBetweenIds(History) returns (History); -} \ No newline at end of file diff --git a/priv/protobuf/service_message/Service.proto b/priv/protobuf/service_message/Service.proto deleted file mode 100644 index fd98188cc7be5d125995a0b2e392b519a4d3b882..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/Service.proto +++ /dev/null @@ -1,22 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Service.grpc"; -option java_outer_classname = "ServiceCls"; -import public "Feature.proto"; -import public "serverType.proto"; -import public "serverStatus.proto"; - -message Service { - string id = 1; - string data = 2; - serverType type = 3; - repeated Feature setting = 4; - int64 expiration = 5; - serverStatus service_status = 6; -} diff --git a/priv/protobuf/service_message/Tag.proto b/priv/protobuf/service_message/Tag.proto deleted file mode 100644 index 9a1632c0de26e45ecd7114e468b20d90054a0aea..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/Tag.proto +++ /dev/null @@ -1,18 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Tag.grpc"; -option java_outer_classname = "TagCls"; -import public "tagType.proto"; - -message Tag { - int64 roster_id = 1; - string name = 2; - string color = 3; - tagType tag_status = 4; -} diff --git a/priv/protobuf/service_message/act.proto b/priv/protobuf/service_message/act.proto deleted file mode 100644 index bbeec857a6062d34b966211d79f9bf6cc7f45ce5..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/act.proto +++ /dev/null @@ -1,18 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "act.grpc"; -option java_outer_classname = "actCls"; - -message act { - string name = 1; - oneof data { - string data20 = 20; - int64 data21 = 21; - } -} diff --git a/priv/protobuf/service_message/container.proto b/priv/protobuf/service_message/container.proto deleted file mode 100644 index 3012039d1edb6c9ce5098fc83143936696900912..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/container.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "container.grpc"; -option java_outer_classname = "containerCls"; - -enum container { - chain = 0; - cur = 1; -} - diff --git a/priv/protobuf/service_message/historyType.proto b/priv/protobuf/service_message/historyType.proto deleted file mode 100644 index e9a7eebdbf952e7a17d7d1aac53aa29bbfaac378..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/historyType.proto +++ /dev/null @@ -1,20 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "historyType.grpc"; -option java_outer_classname = "historyTypeCls"; - -enum historyType { - hupdated = 0; - hget = 1; - hupdate = 2; - hlast_loaded = 3; - hlast_msg = 4; - hget_reply = 5; -} - diff --git a/priv/protobuf/service_message/jobType.proto b/priv/protobuf/service_message/jobType.proto deleted file mode 100644 index c64277dc63f1f63d2ad892fc9a3e1378595a1170..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/jobType.proto +++ /dev/null @@ -1,20 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "jobType.grpc"; -option java_outer_classname = "jobTypeCls"; - -enum jobType { - jinit = 0; - jupdate = 1; - jdelete = 2; - jpending = 3; - jstop = 4; - jcomplete = 5; -} - diff --git a/priv/protobuf/service_message/messageEvent.proto b/priv/protobuf/service_message/messageEvent.proto deleted file mode 100644 index 72715dd79e30d0e2ca85dd30115592c0c77bf63b..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/messageEvent.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "messageEvent.grpc"; -option java_outer_classname = "messageEventCls"; - -message messageEvent { - string name = 1; - string payload = 2; - int64 timeout = 3; - string module = 4; -} diff --git a/priv/protobuf/service_message/messageStatus.proto b/priv/protobuf/service_message/messageStatus.proto deleted file mode 100644 index b514a8a72708b351b1aa00604f15bdee1ca8c475..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/messageStatus.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "messageStatus.grpc"; -option java_outer_classname = "messageStatusCls"; - -enum messageStatus { - masync = 0; - mdelete = 1; - mclear = 2; - mupdate = 3; - medit = 4; -} - diff --git a/priv/protobuf/service_message/messageType.proto b/priv/protobuf/service_message/messageType.proto deleted file mode 100644 index 5f38169131c0ff79eb9d795399262ad1c61291d9..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/messageType.proto +++ /dev/null @@ -1,20 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "messageType.grpc"; -option java_outer_classname = "messageTypeCls"; - -enum messageType { - sys = 0; - reply = 1; - forward = 2; - read = 3; - edited = 4; - cursor = 5; -} - diff --git a/priv/protobuf/service_message/muc.proto b/priv/protobuf/service_message/muc.proto deleted file mode 100644 index 8eb84eb4f0b68c7a58c269f215f2368d7c954896..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/muc.proto +++ /dev/null @@ -1,14 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "muc.grpc"; -option java_outer_classname = "mucCls"; - -message muc { - string name = 1; -} diff --git a/priv/protobuf/service_message/p2p.proto b/priv/protobuf/service_message/p2p.proto deleted file mode 100644 index 3290d952615c1f2286bb6053b11172fa81b5943f..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/p2p.proto +++ /dev/null @@ -1,15 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "p2p.grpc"; -option java_outer_classname = "p2pCls"; - -message p2p { - string from = 1; - string to = 2; -} diff --git a/priv/protobuf/service_message/presence.proto b/priv/protobuf/service_message/presence.proto deleted file mode 100644 index 0b33ae3cc4fa1ae3b83aac2d041653e2f4d83306..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/presence.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "presence.grpc"; -option java_outer_classname = "presenceCls"; - -enum presence { - online = 0; - offline = 1; -} - diff --git a/priv/protobuf/service_message/serverStatus.proto b/priv/protobuf/service_message/serverStatus.proto deleted file mode 100644 index 41e3eeac5f149c3de2e8d3adfb543d0b2b75894e..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/serverStatus.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverStatus.grpc"; -option java_outer_classname = "serverStatusCls"; - -enum serverStatus { - servie_verified = 0; - service_not_verified = 1; -} - diff --git a/priv/protobuf/service_message/serverType.proto b/priv/protobuf/service_message/serverType.proto deleted file mode 100644 index afa4a3716aaf865eaf24b1241f35a945d7eadaca..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/serverType.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverType.grpc"; -option java_outer_classname = "serverTypeCls"; - -enum serverType { - email = 0; - wallet = 1; - google_type = 2; - fb = 3; - phone = 4; -} - diff --git a/priv/protobuf/service_message/tagType.proto b/priv/protobuf/service_message/tagType.proto deleted file mode 100644 index f21e09717a572d79caee5933d6731fcfbc5f43e8..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_message/tagType.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_message; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "tagType.grpc"; -option java_outer_classname = "tagTypeCls"; - -enum tagType { - tag_create = 0; - tag_remove = 1; - tag_edit = 2; -} - diff --git a/priv/protobuf/service_profile/1compile.sh b/priv/protobuf/service_profile/1compile.sh deleted file mode 100755 index 6ad626dee21b25f4fa0b9b918f9631377fd75f88..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/1compile.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -protoc *.proto --java_out=java diff --git a/priv/protobuf/service_profile/Desc.proto b/priv/protobuf/service_profile/Desc.proto deleted file mode 100644 index 9a5a2dd166166bd8cc75b78d3b673fc6b454fd92..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/Desc.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_profile; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Desc.grpc"; -option java_outer_classname = "DescCls"; -import public "Feature.proto"; - -message Desc { - string id = 1; - string mime = 2; - string payload = 3; - string parentid = 4; - repeated Feature data = 5; -} diff --git a/priv/protobuf/service_profile/Feature.proto b/priv/protobuf/service_profile/Feature.proto deleted file mode 100644 index 64e1725871b5a1ad01018c1343521fbc56f1d608..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/Feature.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_profile; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Feature.grpc"; -option java_outer_classname = "FeatureCls"; - -message Feature { - string id = 1; - string key = 2; - string value = 3; - string group = 4; -} diff --git a/priv/protobuf/service_profile/Profile.proto b/priv/protobuf/service_profile/Profile.proto deleted file mode 100644 index db8e1b3d233548f4adb1942ad1131edce8d6793b..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/Profile.proto +++ /dev/null @@ -1,25 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_profile; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Profile.grpc"; -option java_outer_classname = "ProfileCls"; -import public "Feature.proto"; -import public "profileStatus.proto"; -import public "presence.proto"; -import public "Service.proto"; - -message Profile { - string phone = 1; - repeated Service services = 2; - repeated int64 rosters = 3; - repeated Feature settings = 4; - int64 update = 5; - int64 balance = 6; - presence presence = 7; - profileStatus profileStatus = 8; -} diff --git a/priv/protobuf/service_profile/ProfileService.proto b/priv/protobuf/service_profile/ProfileService.proto deleted file mode 100644 index 64dab5f5bb7bed7414ace485b13f6d79abdb026b..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/ProfileService.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax = "proto3"; -package service_profile; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "ProfileService.grpc"; -option java_outer_classname = "ProfileServiceCls"; -import public "Profile.proto"; - -service FriendService { - rpc getProfile(Profile) returns (Profile); - rpc deleteProfile(Profile) returns (Profile); -} \ No newline at end of file diff --git a/priv/protobuf/service_profile/Service.proto b/priv/protobuf/service_profile/Service.proto deleted file mode 100644 index d3af3a6ee25ff6fb7e1487a132371178c04525fc..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/Service.proto +++ /dev/null @@ -1,22 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_profile; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Service.grpc"; -option java_outer_classname = "ServiceCls"; -import public "Feature.proto"; -import public "serverType.proto"; -import public "serverStatus.proto"; - -message Service { - string id = 1; - string data = 2; - serverType type = 3; - repeated Feature setting = 4; - int64 expiration = 5; - serverStatus service_status = 6; -} diff --git a/priv/protobuf/service_profile/Tag.proto b/priv/protobuf/service_profile/Tag.proto deleted file mode 100644 index db4d5d5b4198c748ca798bac521a4aeb5afe9918..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/Tag.proto +++ /dev/null @@ -1,18 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_profile; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Tag.grpc"; -option java_outer_classname = "TagCls"; -import public "tagType.proto"; - -message Tag { - int64 roster_id = 1; - string name = 2; - string color = 3; - tagType tag_status = 4; -} diff --git a/priv/protobuf/service_profile/presence.proto b/priv/protobuf/service_profile/presence.proto deleted file mode 100644 index f37d175ce43b465495fedd5f147c8d6594e1eed0..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/presence.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_profile; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "presence.grpc"; -option java_outer_classname = "presenceCls"; - -enum presence { - online = 0; - offline = 1; -} - diff --git a/priv/protobuf/service_profile/profileStatus.proto b/priv/protobuf/service_profile/profileStatus.proto deleted file mode 100644 index 44277b57cb56553088551c39fc443c206352e7cd..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/profileStatus.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_profile; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "profileStatus.grpc"; -option java_outer_classname = "profileStatusCls"; - -enum profileStatus { - remove_profile = 0; - get_profile = 1; - patch_profile = 2; -} - diff --git a/priv/protobuf/service_profile/serverStatus.proto b/priv/protobuf/service_profile/serverStatus.proto deleted file mode 100644 index 1883e26bcadb3ff4d6af50a51fada0f85131b11b..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/serverStatus.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_profile; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverStatus.grpc"; -option java_outer_classname = "serverStatusCls"; - -enum serverStatus { - servie_verified = 0; - service_not_verified = 1; -} - diff --git a/priv/protobuf/service_profile/serverType.proto b/priv/protobuf/service_profile/serverType.proto deleted file mode 100644 index af48113f40d7fe3996486c58dab53fb2270e36ed..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/serverType.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_profile; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverType.grpc"; -option java_outer_classname = "serverTypeCls"; - -enum serverType { - email = 0; - wallet = 1; - google_type = 2; - fb = 3; - phone = 4; -} - diff --git a/priv/protobuf/service_profile/tagType.proto b/priv/protobuf/service_profile/tagType.proto deleted file mode 100644 index e7673153547d894925b862651330c2e887d9e37f..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_profile/tagType.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_profile; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "tagType.grpc"; -option java_outer_classname = "tagTypeCls"; - -enum tagType { - tag_create = 0; - tag_remove = 1; - tag_edit = 2; -} - diff --git a/priv/protobuf/service_room/1compile.sh b/priv/protobuf/service_room/1compile.sh deleted file mode 100755 index 6ad626dee21b25f4fa0b9b918f9631377fd75f88..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/1compile.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -protoc *.proto --java_out=java diff --git a/priv/protobuf/service_room/Desc.proto b/priv/protobuf/service_room/Desc.proto deleted file mode 100644 index e9a4db9b74f83347aab34670ddca9f9eaed8017b..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/Desc.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Desc.grpc"; -option java_outer_classname = "DescCls"; -import public "Feature.proto"; - -message Desc { - string id = 1; - string mime = 2; - string payload = 3; - string parentid = 4; - repeated Feature data = 5; -} diff --git a/priv/protobuf/service_room/Feature.proto b/priv/protobuf/service_room/Feature.proto deleted file mode 100644 index 2de076cb5d05aa33adb8684bcffda5b71e7ef28a..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/Feature.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Feature.grpc"; -option java_outer_classname = "FeatureCls"; - -message Feature { - string id = 1; - string key = 2; - string value = 3; - string group = 4; -} diff --git a/priv/protobuf/service_room/Link.proto b/priv/protobuf/service_room/Link.proto deleted file mode 100644 index a3cf002d4507f96e4b4bc9bec4f9773b354fabf1..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/Link.proto +++ /dev/null @@ -1,21 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Link.grpc"; -option java_outer_classname = "LinkCls"; - -message Link { - string id = 1; - string name = 2; - string room_id = 3; - int64 created = 4; - oneof type { - } - oneof status { - } -} diff --git a/priv/protobuf/service_room/Member.proto b/priv/protobuf/service_room/Member.proto deleted file mode 100644 index fa750d3ba8e902f87262133e69f543c46618ab61..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/Member.proto +++ /dev/null @@ -1,41 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -import "google/protobuf/Any.proto"; -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Member.grpc"; -option java_outer_classname = "MemberCls"; -import public "Feature.proto"; -import public "presence.proto"; -import public "muc.proto"; -import public "p2p.proto"; -import public "memberStatus.proto"; -import public "Service.proto"; -import public "container.proto"; - -message Member { - int64 id = 1; - container container = 2; - oneof feed_id { - muc feed_id30 = 30; - p2p feed_id31 = 31; - } - int64 prev = 4; - int64 next = 5; - repeated google.protobuf.Any feeds = 6; - string phone_id = 7; - string avatar = 8; - string names = 9; - string surnames = 10; - string alias = 11; - int64 reader = 12; - int64 update = 13; - repeated Feature settings = 14; - repeated Service services = 15; - presence presence = 16; - memberStatus member_status = 17; -} diff --git a/priv/protobuf/service_room/Room.proto b/priv/protobuf/service_room/Room.proto deleted file mode 100644 index 13d1f556a2853b23fdd041862cb6917accca0740..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/Room.proto +++ /dev/null @@ -1,40 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Room.grpc"; -option java_outer_classname = "RoomCls"; -import public "Member.proto"; -import public "Feature.proto"; -import public "Message.proto"; -import public "Desc.proto"; - -message Room { - string id = 1; - string name = 2; - string links = 3; - string description = 4; - repeated Feature settings = 5; - repeated Member members = 6; - repeated Member admins = 7; - repeated Desc data = 8; - oneof type { - } - string tos = 10; - int64 tos_update = 11; - int64 unread = 12; - repeated int64 mentions = 13; - repeated int64 readers = 14; - oneof last_msg { - int64 last_msg150 = 150; - Message last_msg151 = 151; - } - int64 update = 16; - int64 created = 17; - oneof status { - } -} diff --git a/priv/protobuf/service_room/RoomService.proto b/priv/protobuf/service_room/RoomService.proto deleted file mode 100644 index 034e92c38f7b1de6c52e5b39ceedad72167f1b04..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/RoomService.proto +++ /dev/null @@ -1,23 +0,0 @@ -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "RoomService.grpc"; -option java_outer_classname = "RoomServiceCls"; -import public "Room.proto"; -import public "Member.proto"; - -service RoomService { - rpc createRoom(Room) returns (Room); - rpc leaveRoom(Room) returns (Room); - rpc kickFromRoom(Room) returns (Room); - rpc updatemRoom(Room) returns (Room); - rpc addUserToRoom(Room) returns (Room); - rpc changeUserRoleInRoom(Room) returns (Room); - rpc getRoomMembers(Room) returns (Room); - rpc clearRoomHistory(Room) returns (Room); - rpc muteRoom(Member) returns (Member); - rpc unmuteRoom(Member) returns (Member); - rpc updateMemberDetails(Member) returns (Member); -} \ No newline at end of file diff --git a/priv/protobuf/service_room/Service.proto b/priv/protobuf/service_room/Service.proto deleted file mode 100644 index 596afa5735d66b3a6f8aa3009ed201821af995c5..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/Service.proto +++ /dev/null @@ -1,22 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Service.grpc"; -option java_outer_classname = "ServiceCls"; -import public "Feature.proto"; -import public "serverType.proto"; -import public "serverStatus.proto"; - -message Service { - string id = 1; - string data = 2; - serverType type = 3; - repeated Feature setting = 4; - int64 expiration = 5; - serverStatus service_status = 6; -} diff --git a/priv/protobuf/service_room/Tag.proto b/priv/protobuf/service_room/Tag.proto deleted file mode 100644 index e2d1b28d2f59fa50b598c111781af2bd5c855afa..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/Tag.proto +++ /dev/null @@ -1,18 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Tag.grpc"; -option java_outer_classname = "TagCls"; -import public "tagType.proto"; - -message Tag { - int64 roster_id = 1; - string name = 2; - string color = 3; - tagType tag_status = 4; -} diff --git a/priv/protobuf/service_room/container.proto b/priv/protobuf/service_room/container.proto deleted file mode 100644 index 917329a001dc795736917f2a83318a846c92b0a0..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/container.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "container.grpc"; -option java_outer_classname = "containerCls"; - -enum container { - chain = 0; - cur = 1; -} - diff --git a/priv/protobuf/service_room/linkStatus.proto b/priv/protobuf/service_room/linkStatus.proto deleted file mode 100644 index 1ff436f8b7cc12476bbee9b61013d146eb310a6e..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/linkStatus.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "linkStatus.grpc"; -option java_outer_classname = "linkStatusCls"; - -enum linkStatus { - lgen = 0; - lcheck = 1; - ladd = 2; - ldelete = 3; - lupdate = 4; -} - diff --git a/priv/protobuf/service_room/memberStatus.proto b/priv/protobuf/service_room/memberStatus.proto deleted file mode 100644 index 34bee9bab1e829d093d204aa6d228ad6b7030e16..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/memberStatus.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "memberStatus.grpc"; -option java_outer_classname = "memberStatusCls"; - -enum memberStatus { - admin = 0; - member = 1; - removed = 2; - patch = 3; - owner = 4; -} - diff --git a/priv/protobuf/service_room/muc.proto b/priv/protobuf/service_room/muc.proto deleted file mode 100644 index 57ddef8afd34c4bac4fbe95eb665189b4acecd64..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/muc.proto +++ /dev/null @@ -1,14 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "muc.grpc"; -option java_outer_classname = "mucCls"; - -message muc { - string name = 1; -} diff --git a/priv/protobuf/service_room/p2p.proto b/priv/protobuf/service_room/p2p.proto deleted file mode 100644 index e0d183961c5592bde7c90b761c88bc723657d7b8..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/p2p.proto +++ /dev/null @@ -1,15 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "p2p.grpc"; -option java_outer_classname = "p2pCls"; - -message p2p { - string from = 1; - string to = 2; -} diff --git a/priv/protobuf/service_room/presence.proto b/priv/protobuf/service_room/presence.proto deleted file mode 100644 index bb9958e1c220bc8359b1bc504a478ae785d2330a..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/presence.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "presence.grpc"; -option java_outer_classname = "presenceCls"; - -enum presence { - online = 0; - offline = 1; -} - diff --git a/priv/protobuf/service_room/roomStatus.proto b/priv/protobuf/service_room/roomStatus.proto deleted file mode 100644 index d6c14cfdefc7abea14e4b0934d48b5b4c3df184e..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/roomStatus.proto +++ /dev/null @@ -1,22 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "roomStatus.grpc"; -option java_outer_classname = "roomStatusCls"; - -enum roomStatus { - room_create = 0; - room_leave = 1; - room_add = 2; - room_remove = 3; - room_patch = 4; - room_get = 5; - room_delete = 6; - room_last_msg = 7; -} - diff --git a/priv/protobuf/service_room/roomType.proto b/priv/protobuf/service_room/roomType.proto deleted file mode 100644 index 1ce70af7b7bc0aaa2f9e1d0e679bff39eda61df1..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/roomType.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "roomType.grpc"; -option java_outer_classname = "roomTypeCls"; - -enum roomType { - group = 0; - channel = 1; -} - diff --git a/priv/protobuf/service_room/serverStatus.proto b/priv/protobuf/service_room/serverStatus.proto deleted file mode 100644 index 045d8ed0e6398ae96a789eb4be7fe94a18eea87b..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/serverStatus.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverStatus.grpc"; -option java_outer_classname = "serverStatusCls"; - -enum serverStatus { - servie_verified = 0; - service_not_verified = 1; -} - diff --git a/priv/protobuf/service_room/serverType.proto b/priv/protobuf/service_room/serverType.proto deleted file mode 100644 index eeefc6b5cf0c7636f541b841362890e13bdc18a4..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/serverType.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverType.grpc"; -option java_outer_classname = "serverTypeCls"; - -enum serverType { - email = 0; - wallet = 1; - google_type = 2; - fb = 3; - phone = 4; -} - diff --git a/priv/protobuf/service_room/tagType.proto b/priv/protobuf/service_room/tagType.proto deleted file mode 100644 index c0f05076e5560cb938673108fc12ea211649a296..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_room/tagType.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_room; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "tagType.grpc"; -option java_outer_classname = "tagTypeCls"; - -enum tagType { - tag_create = 0; - tag_remove = 1; - tag_edit = 2; -} - diff --git a/priv/protobuf/service_roster/1compile.sh b/priv/protobuf/service_roster/1compile.sh deleted file mode 100755 index 6ad626dee21b25f4fa0b9b918f9631377fd75f88..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/1compile.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -protoc *.proto --java_out=java diff --git a/priv/protobuf/service_roster/Contact.proto b/priv/protobuf/service_roster/Contact.proto deleted file mode 100644 index 8fcb3eec68396638ffd6bec97b6d78ec62432370..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/Contact.proto +++ /dev/null @@ -1,32 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -import "google/protobuf/Any.proto"; -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Contact.grpc"; -option java_outer_classname = "ContactCls"; -import public "Feature.proto"; -import public "contactStatus.proto"; -import public "presence.proto"; -import public "Service.proto"; - -message Contact { - string user_id = 1; - repeated int64 avatar = 2; - string names = 3; - string surnames = 4; - string nick = 5; - repeated google.protobuf.Any reader = 6; - int64 unread = 7; - int64 last_msg = 8; - int64 update = 9; - int64 created = 10; - repeated Feature settings = 11; - repeated Service services = 12; - presence presence = 13; - contactStatus status = 14; -} diff --git a/priv/protobuf/service_roster/Desc.proto b/priv/protobuf/service_roster/Desc.proto deleted file mode 100644 index 99c421db4a2cc404394a5ca6f59c76a9ff1968d8..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/Desc.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Desc.grpc"; -option java_outer_classname = "DescCls"; -import public "Feature.proto"; - -message Desc { - string id = 1; - string mime = 2; - string payload = 3; - string parentid = 4; - repeated Feature data = 5; -} diff --git a/priv/protobuf/service_roster/Feature.proto b/priv/protobuf/service_roster/Feature.proto deleted file mode 100644 index 3859dcf675244820ed48253043efa88c789b12ca..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/Feature.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Feature.grpc"; -option java_outer_classname = "FeatureCls"; - -message Feature { - string id = 1; - string key = 2; - string value = 3; - string group = 4; -} diff --git a/priv/protobuf/service_roster/Roster.proto b/priv/protobuf/service_roster/Roster.proto deleted file mode 100644 index 956afea8e247d9b5f5918227e60199e76cc2b77e..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/Roster.proto +++ /dev/null @@ -1,28 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Roster.grpc"; -option java_outer_classname = "RosterCls"; -import public "Contact.proto"; -import public "rosterStatus.proto"; - -message Roster { - int64 id = 1; - string names = 2; - string surnames = 3; - string email = 4; - string nick = 5; - repeated Contact userlist = 6; - repeated int64 roomlist = 7; - repeated int64 favorite = 8; - repeated int64 tags = 9; - string phone = 10; - string avatar = 11; - int64 update = 12; - rosterStatus rosterStatus = 13; -} diff --git a/priv/protobuf/service_roster/RosterService.proto b/priv/protobuf/service_roster/RosterService.proto deleted file mode 100644 index abf822cc920a3757c7a074fcfd3c87f96a439345..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/RosterService.proto +++ /dev/null @@ -1,14 +0,0 @@ -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "RosterService.grpc"; -option java_outer_classname = "RosterServiceCls"; -import public "Roster.proto"; -import public "Contact.proto"; - -service RosterService { - rpc update(Roster) returns (Roster); - rpc updateNick(Roster) returns (Roster); -} \ No newline at end of file diff --git a/priv/protobuf/service_roster/Service.proto b/priv/protobuf/service_roster/Service.proto deleted file mode 100644 index 180788cd3120cf232a2ae3d76bfd8b31d2512d8c..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/Service.proto +++ /dev/null @@ -1,22 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Service.grpc"; -option java_outer_classname = "ServiceCls"; -import public "Feature.proto"; -import public "serverType.proto"; -import public "serverStatus.proto"; - -message Service { - string id = 1; - string data = 2; - serverType type = 3; - repeated Feature setting = 4; - int64 expiration = 5; - serverStatus service_status = 6; -} diff --git a/priv/protobuf/service_roster/Tag.proto b/priv/protobuf/service_roster/Tag.proto deleted file mode 100644 index 8fc287a05eaaad8fd0c198be1a9957ddd4e3e28f..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/Tag.proto +++ /dev/null @@ -1,18 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "Tag.grpc"; -option java_outer_classname = "TagCls"; -import public "tagType.proto"; - -message Tag { - int64 roster_id = 1; - string name = 2; - string color = 3; - tagType tag_status = 4; -} diff --git a/priv/protobuf/service_roster/contactStatus.proto b/priv/protobuf/service_roster/contactStatus.proto deleted file mode 100644 index be8237beaf778b4d4c5886301b1b5b8a5105f1ef..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/contactStatus.proto +++ /dev/null @@ -1,23 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "contactStatus.grpc"; -option java_outer_classname = "contactStatusCls"; - -enum contactStatus { - conact_request = 0; - authorization = 1; - contact_ignore = 2; - conatct_internal = 3; - friend = 4; - contact_last_msg = 5; - contact_ban = 6; - conact_banned = 7; - contact_deleted = 8; -} - diff --git a/priv/protobuf/service_roster/muc.proto b/priv/protobuf/service_roster/muc.proto deleted file mode 100644 index 039c4d5224bc7eb1da2776ef8655526ef97bc6de..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/muc.proto +++ /dev/null @@ -1,14 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "muc.grpc"; -option java_outer_classname = "mucCls"; - -message muc { - string name = 1; -} diff --git a/priv/protobuf/service_roster/p2p.proto b/priv/protobuf/service_roster/p2p.proto deleted file mode 100644 index 46a95b911cc13c55a16713e889979ea363ab62c1..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/p2p.proto +++ /dev/null @@ -1,15 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "p2p.grpc"; -option java_outer_classname = "p2pCls"; - -message p2p { - string from = 1; - string to = 2; -} diff --git a/priv/protobuf/service_roster/presence.proto b/priv/protobuf/service_roster/presence.proto deleted file mode 100644 index 3c2a5c119cf0afea9d4a01b3029a5cd329a15002..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/presence.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "presence.grpc"; -option java_outer_classname = "presenceCls"; - -enum presence { - online = 0; - offline = 1; -} - diff --git a/priv/protobuf/service_roster/rosterStatus.proto b/priv/protobuf/service_roster/rosterStatus.proto deleted file mode 100644 index 65c391024ba14d93be03838e546653f8e0955635..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/rosterStatus.proto +++ /dev/null @@ -1,24 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "rosterStatus.grpc"; -option java_outer_classname = "rosterStatusCls"; - -enum rosterStatus { - get_roster = 0; - create_roster = 1; - del_roster = 2; - remove_roster = 3; - nick = 4; - add_roster = 5; - update_roster = 6; - list_loster = 7; - patch_roster = 8; - roster_last_msg = 9; -} - diff --git a/priv/protobuf/service_roster/serverStatus.proto b/priv/protobuf/service_roster/serverStatus.proto deleted file mode 100644 index 57d0314df734039ae7f2f61934c06d9bde997567..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/serverStatus.proto +++ /dev/null @@ -1,16 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverStatus.grpc"; -option java_outer_classname = "serverStatusCls"; - -enum serverStatus { - servie_verified = 0; - service_not_verified = 1; -} - diff --git a/priv/protobuf/service_roster/serverType.proto b/priv/protobuf/service_roster/serverType.proto deleted file mode 100644 index 7ca622a890869f2b71d6dafec6d7f9cdac7bfc7d..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/serverType.proto +++ /dev/null @@ -1,19 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "serverType.grpc"; -option java_outer_classname = "serverTypeCls"; - -enum serverType { - email = 0; - wallet = 1; - google_type = 2; - fb = 3; - phone = 4; -} - diff --git a/priv/protobuf/service_roster/tagType.proto b/priv/protobuf/service_roster/tagType.proto deleted file mode 100644 index 73981ba16a735570949d6021bd25098ad4fcc555..0000000000000000000000000000000000000000 --- a/priv/protobuf/service_roster/tagType.proto +++ /dev/null @@ -1,17 +0,0 @@ -// Generated by https://github.com/synrc/bert -// DO NOT EDIT - -syntax = "proto3"; -package service_roster; - -option java_generic_services = true; -option java_multiple_files = true; -option java_package = "tagType.grpc"; -option java_outer_classname = "tagTypeCls"; - -enum tagType { - tag_create = 0; - tag_remove = 1; - tag_edit = 2; -} - diff --git a/rebar.config b/rebar.config index 7f954532fc4db2833c18e91916ec641029b71b32..66432ede512660b532dad620769b71abeddffa80 100644 --- a/rebar.config +++ b/rebar.config @@ -1,51 +1,123 @@ -{sub_dirs,["apps"]}. -{lib_dirs,["apps","deps"]}. -{deps_dir,"deps"}. +%% -*- mode:erlang; erlang-indent-level: 4; indent-tabs-mode: nil -*- +{erl_opts, [{parse_transform, oc_span_transform}, + debug_info, + warnings_as_errors, + nowarn_export_all + ]}. + {deps, [ - {bert, ".*", {git, "git://github.com/synrc/bert",{tag,"2.4"}}}, - {active, ".*", {git, "git://github.com/synrc/active",{tag,"master"}}}, - {esockd, ".*", {git, "https://github.com/voxoz/esockd",{ref, "a80634b961c315ffe5f020d73236473b53ae5dc9"}}}, - {bpe, ".*", {git, "git://github.com/synrc/bpe", {tag,"4.4"}}}, - {emqttd, ".*", {git, "git://github.com/NYNJA-MC/emqttd",{tag,"master"}}}, - {n2o, ".*", {git, "git://github.com/synrc/n2o", {tag,"v6.4"}}}, - {emqttc, ".*", {git, "git://github.com/NYNJA-MC/emqttc",{tag,"master"}}}, - {rest, ".*", {git, "git://github.com/synrc/rest",{tag,"5.10"}}}, - {gen_smtp, ".*", {git, "git://github.com/voxoz/gen_smtp",{tag,"master"}}}, - {emq_dashboard, ".*", {git, "https://github.com/synrc/emq_dashboard",{tag,"master"}}}, - {'opencensus-erlang', ".*", {git, "https://github.com/voxoz/opencensus-erlang",{tag, "v0.4.0"}}}, - {libphonenumber_erlang, ".*", {git, "https://github.com/marinakr/libphonenumber_erlang.git",{tag,"master"}}}, - {gproc, ".*", {git, "https://github.com/uwiger/gproc","0.6.1"}}, - {erlydtl, ".*", {git, "git://github.com/voxoz/erlydtl",{tag,"master"}}}, - {mini_s3, ".*", {git, "https://github.com/chef/mini_s3.git",{tag,"master"}}}, - {jwt, ".*", {git, "https://github.com/artemeff/jwt.git",{tag, "0.1.8"}}}, - {jsx, ".*", {git, "https://github.com/talentdeficit/jsx.git","v2.9.0"}}, - {base64url, ".*", {git, "https://github.com/dvv/base64url.git","v1.0"}}, - {migresia, ".*", {git, "https://github.com/yoonka/migresia.git",{tag,"master"}}}, - {counters, ".*", {git, "https://github.com/deadtrickster/counters.erl.git", {tag, "v0.2.0"}}}, - {ctx, ".*", {git, "https://github.com/tsloughter/ctx.git", []}}, - {wts, ".*", {git, "https://github.com/tsloughter/wts.git", []}}, - {rfc3339, ".*", {git, "https://github.com/talentdeficit/rfc3339",{tag,"0.2.2"}}}, - {locus, ".*", {git, "https://github.com/g-andrade/locus.git",{ref,"0ea9079ce5686573e0e70e2b1311343dd25feef8"}}}, - {prometheus, ".*", {git, "https://github.com/deadtrickster/prometheus.erl",{tag,"master"}}}, - {cowboy, ".*", {git, "git://github.com/voxoz/cowboy", {tag,"master"}}}, - {'jose-erlang', ".*", {git, "https://github.com/manifest/jose-erlang.git", {tag,"master"}}}, - {json_rec, ".*", {git, "https://github.com/justinkirby/json_rec.git", {tag,"master"}}}, - {'erlang-uuid', ".*", {git, "https://github.com/avtobiff/erlang-uuid.git",{tag,"master"}}}, - {enenra, ".*", {git, "https://github.com/nlfiedler/enenra", {tag, "0.3.0"}}}, - {'qdate', ".*", {git, "https://github.com/enterprizing/qdate.git",{ref,"fba988fc54214bb37a3ce11d5c5a3cc68752c3ce"}}} + {active, {git, "git://github.com/synrc/active", {branch,"master"}}}, + {bert, {git, "git://github.com/NYNJA-MC/bert.git", {branch, master}}}, + {esockd, {git, "https://github.com/voxoz/esockd", {branch, "master"}}}, + {bpe, {git, "git://github.com/synrc/bpe", {tag,"4.4"}}}, + {emqttd, {git, "git://github.com/NYNJA-MC/emqttd", {branch,"master"}}}, + {n2o, {git, "git://github.com/synrc/n2o", {tag,"6.4"}}}, + {emqttc, {git, "git://github.com/NYNJA-MC/emqttc", {branch,"master"}}}, + {rest, {git, "git://github.com/synrc/rest", {tag,"5.10"}}}, + {gen_smtp, {git, "git://github.com/voxoz/gen_smtp", {branch,"master"}}}, + {emq_dashboard, {git, "https://github.com/synrc/emq_dashboard", {branch,"master"}}}, + {opencensus, {git, "https://github.com/census-instrumentation/opencensus-erlang", {ref, "7fb276f"}}}, + {libphonenumber_erlang, {git, "https://github.com/marinakr/libphonenumber_erlang.git", {branch,"master"}}}, + {gproc, {git, "https://github.com/uwiger/gproc", {tag, "0.6.1"}}}, + {erlydtl, {git, "git://github.com/voxoz/erlydtl", {branch,"master"}}}, + {mini_s3, {git, "https://github.com/chef/mini_s3.git", {branch,"master"}}}, + {jwt, {git, "https://github.com/artemeff/jwt.git", {tag, "0.1.8"}}}, + {jsx, {git, "https://github.com/talentdeficit/jsx.git",{tag, "v2.9.0"}}}, + {base64url, {git, "https://github.com/dvv/base64url.git", {tag, "v1.0"}}}, + {migresia, {git, "https://github.com/yoonka/migresia.git", {branch,"master"}}}, + {counters, {git, "https://github.com/deadtrickster/counters.erl.git", {tag, "v0.2.2"}}}, + {ctx, {git, "https://github.com/tsloughter/ctx.git"}}, + {wts, {git, "https://github.com/tsloughter/wts.git"}}, + {rfc3339, {git, "https://github.com/talentdeficit/rfc3339",{tag,"0.2.2"}}}, + {locus, {git, "https://github.com/g-andrade/locus.git", {ref,"0ea9079ce5686573e0e70e2b1311343dd25feef8"}}}, + {prometheus, {git, "https://github.com/deadtrickster/prometheus.erl", {branch,"master"}}}, + {cowboy, {git, "git://github.com/ninenines/cowboy", {tag,"2.7.0"}}}, + {jose, {git, "https://github.com/NYNJA-MC/jose-erlang.git", {ref, "7094018"}}}, + {uuid, {git, "https://github.com/avtobiff/erlang-uuid.git",{branch,"master"}}}, + {enenra, {git, "https://github.com/nlfiedler/enenra", {tag, "0.3.0"}}}, + {'qdate', {git, "https://github.com/enterprizing/qdate.git", {ref,"fba988fc54214bb37a3ce11d5c5a3cc68752c3ce"}}} ]}. -{erl_opts, [{parse_transform,lager_transform},{parse_transform, oc_transform},debug_info]}. -{relx, [{release, {server, "1.0.0"}, - [roster, service]}, - {sys_config, "./sys.config"}, - {vm_args, "./vm.args"}, - {dev_mode, true}, - {include_erts, false}, - {extended_start_script, true} - ] - }. - {profiles, [ - {prod, [{relx, [{dev_mode, false}, - {include_erts, true}]} - ]} - ]}. + + +{profiles, [{local, [{relx, [{dev_mode, true}, + {include_erts, false}, + {include_src, true}]} + ]}, + {prod, [{relx, [{dev_mode, false}, + {include_erts, true}, + {include_src, true}, + {overlay, []} + ]} + ]}, + {test, [{deps, [{gun, {git, "https://github.com/ninenines/gun", {tag, "1.3.2"}}}, + {uuid, {git, "https://github.com/okeuday/uuid.git", {tag, "v1.7.5"}}}, + {jesse, {git, "https://github.com/for-GET/jesse.git", {branch, "master"}}} + ]} + ]} + ]}. + +{ct_opts, [{sys_config, ["./sys.config"]}]}. + +%% Generate json, javascript and swift in following way +{overrides, [{add, service, [{erl_opts, [{bert_proto_dir, "priv"}, + {bert_disallowed, ['feed', 'Whitelist', 'FakeNumbers','Schedule','Index','process']} + ]}]}, + {add, roster, [{erl_opts, [{bert_erl, "priv/src"}, + {bert_js, "priv/macbert"}, + {bert_swift, "priv/macbert"}, + {bert_allowed_hrl, ["roster"]}, + {bert_disallowed, ['feed', 'Whitelist', 'FakeNumbers','Schedule','Index','process']} + ]}]} + ]}. + +{artifacts, ["lib/roster/src/roster_validator.erl", "lib/roster/ebin/roster_validator.beam"]}. + +{relx, [{release, {server, "1.0.3"}, + %% Copied from .applist in older version. Should be cleaned up. + [kernel, stdlib, sasl, crypto, inets, os_mon, + fs, gproc, gen_logger, compiler, + mnesia, kvs, + esockd, + prometheus,bert,n2o, + metrics,mimerl,unicode_util_compat,base64url,jsx,tools, + certifi,ibrowse,asn1,xmerl,counters,ctx, + wts,syntax_tools,qdate_localtime, + libphonenumber_erlang,syn,cowlib,jiffy,idna,parse_trans, + goldrush, public_key,bpe,lager,ssl,ranch, + ssl_verify_fun,locus,emqttd,hackney,roster,service,active, + cowboy,emq_dashboard,emqttc,enenra,envy,uuid,erlydtl,forms, + gen_smtp, jwt, migresia, mini_s3, nitro, opencensus, + qdate,rest,rfc3339,sh,stacktrace_compat,nynja_oam]}, + {sys_config, "./sys.config"}, + {vm_args_src, "./vm.args.src"}, + {dev_mode, true}, + {include_erts, false}, + {extended_start_script, true}, + {extended_start_script_extensions, + [{db_sync, "extensions/db_sync.sh"}, + {db_sync_cont, "extensions/db_sync_cont.sh"}, + {db_snapshot, "extensions/db_snapshot.sh"}, + {db_restore, "extensions/db_restore.sh"}, + {db_install_fallback, "extensions/db_install_fallback.sh"} + ]}, + {overlay, [{copy, "admin", "./admin"}, + {copy, "etc", "./etc"}, + {copy, "priv", "./priv"}, + {copy, "asserts", "./asserts"}, + {copy, + "apps/nynja_oam/priv/extensions/db_sync.sh", + "bin/extensions/db_sync.sh"}, + {copy, + "apps/nynja_oam/priv/extensions/db_sync_cont.sh", + "bin/extensions/db_sync_cont.sh"}, + {copy, + "apps/nynja_oam/priv/extensions/db_snapshot.sh", + "bin/extensions/db_snapshot.sh"}, + {copy, + "apps/nynja_oam/priv/extensions/db_restore.sh", + "bin/extensions/db_restore.sh"}, + {copy, + "apps/nynja_oam/priv/extensions/db_install_fallback.sh", + "bin/extensions/db_install_fallback.sh"} + ]} + ]}. diff --git a/rebar.lock b/rebar.lock index 4c14c39199282f1b3a75df2473dd89363df8636a..934fd3af0df0ac4c60d9b7eb265578a2ad736381 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,68 +1,78 @@ {"1.1.0", -[{<<"base64url">>, +[{<<"active">>, + {git,"git://github.com/synrc/active", + {ref,"cdd8f2b0f62b9785673bdbea7be90e1ae1ca1c02"}}, + 0}, + {<<"base64url">>, {git,"https://github.com/dvv/base64url.git", {ref,"f2c64ed8b9bebc536fad37ad97243452b674b837"}}, 0}, {<<"bert">>, - {git,"git://github.com/synrc/bert", - {ref,"6cde41b32448e85ecd48225221d6f978943a1bad"}}, + {git,"git://github.com/NYNJA-MC/bert.git", + {ref,"9a0cd97b6db86852811947888424236b25b451c9"}}, 0}, {<<"bpe">>, {git,"git://github.com/synrc/bpe", {ref,"356c9e621c38e927a8611ecac592bcdc8d689026"}}, 0}, {<<"certifi">>,{pkg,<<"certifi">>,<<"2.4.2">>},1}, + {<<"cf">>,{pkg,<<"cf">>,<<"0.3.1">>},2}, {<<"counters">>, {git,"https://github.com/deadtrickster/counters.erl.git", - {ref,"c3f4aa3acdf71c3db0a4b3fc1343aa45de2c5df0"}}, + {ref,"70e709ef43ba0f6ee28e31efe5894eaa475211e1"}}, 0}, {<<"cowboy">>, - {git,"git://github.com/voxoz/cowboy", - {ref,"c1cfbfa5dc6b5f6ecd9591ad5bf642b6a107a7f5"}}, + {git,"git://github.com/ninenines/cowboy", + {ref,"63b17e4edf666d995ec86cdcda17a62ba5ebc423"}}, 0}, {<<"cowlib">>, - {git,"git://github.com/voxoz/cowlib", - {ref,"5cc0038d0a3ae6a829646ddfff998d8c491969ca"}}, + {git,"https://github.com/ninenines/cowlib", + {ref,"c6553f8308a2ca5dcd69d845f0a7d098c40c3363"}}, 1}, {<<"ctx">>, {git,"https://github.com/tsloughter/ctx.git", - {ref,"a5a6b0948708e02bc7b9cb6248807c9ff8327940"}}, + {ref,"91c892b51d3340fc004099e15e4e029a5ec0e098"}}, 0}, {<<"emq_dashboard">>, {git,"https://github.com/synrc/emq_dashboard", - {ref,"f711e8d2b0a992f5540123f3504eebabc324a684"}}, + {ref,"3317c7dd47b07d28d7dbea4de971b48fc042fefd"}}, 0}, {<<"emqttc">>, - {git,"git://github.com/voxoz/emqttc", - {ref,"141fd2e925be854321d22005313762adb1195f48"}}, + {git,"git://github.com/NYNJA-MC/emqttc", + {ref,"1ffeaf64791b76faa18dad9e9f40686814d5dd10"}}, 0}, {<<"emqttd">>, - {git,"git://github.com/synrc/emqttd", - {ref,"c3d0b7b092fffe7bebe27af27dbbfe1714757d1c"}}, + {git,"git://github.com/NYNJA-MC/emqttd", + {ref,"5e2342bf7ae597804a6f9234a54cad2f6b5761d1"}}, + 0}, + {<<"enenra">>, + {git,"https://github.com/nlfiedler/enenra", + {ref,"ccc2553b8df5a188cdc77aeec7b856a1e5250e67"}}, 0}, {<<"envy">>, {git,"https://github.com/markan/envy.git", {ref,"0148fb4b7ed0e188511578e98b42d6e7dde0ebd1"}}, 1}, - {<<"erlang-uuid">>, - {git,"https://github.com/avtobiff/erlang-uuid.git", - {ref,"cb02a2039a9b29dd2eef0446039c9c6e164df9ef"}}, - 0}, + {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"1.3.1">>},1}, {<<"erlydtl">>, {git,"git://github.com/voxoz/erlydtl", {ref,"bdebe6f87d8f989018facbbf8dc6320936ffe98f"}}, 0}, {<<"esockd">>, {git,"https://github.com/voxoz/esockd", - {ref,"a80634b961c315ffe5f020d73236473b53ae5dc9"}}, + {ref,"817a4f059698a349aac9037fc0600b3928036e3d"}}, 0}, {<<"forms">>, {git,"git://github.com/synrc/forms", {ref,"845feb45a46dfc2e0e9a156da9c01c218d8fd6cc"}}, 1}, + {<<"fs">>, + {git,"git://github.com/synrc/fs", + {ref,"45ca2003b208f461ef4acd56c5fdecd2e98e1f33"}}, + 1}, {<<"gen_logger">>, - {git,"git://github.com/voxoz/gen_logger", - {ref,"5b14530363feb0b049c4f5c7c606f815aec781d2"}}, + {git,"git://github.com/NYNJA-MC/gen_logger", + {ref,"edc9b0aa1202cd4129725d3962a8b992fcf0e29c"}}, 1}, {<<"gen_smtp">>, {git,"git://github.com/voxoz/gen_smtp", @@ -76,10 +86,23 @@ {git,"https://github.com/uwiger/gproc", {ref,"1d16f5e6d7cf616eec4395f2385e3a680a4ffc9f"}}, 0}, + {<<"hackney">>, + {git,"https://github.com/benoitc/hackney", + {ref,"f2ac65700ef7918eb4e827892f1a7bb01e826026"}}, + 1}, {<<"ibrowse">>, {git,"git://github.com/cmullaparthi/ibrowse.git", {ref,"c97136cfb61fcc6f39d4e7da47372a64f7fca04e"}}, 1}, + {<<"idna">>,{pkg,<<"idna">>,<<"6.0.0">>},2}, + {<<"jiffy">>, + {git,"https://github.com/davisp/jiffy", + {ref,"c942525130ff0271bd318715406f234c0dc9bc5a"}}, + 1}, + {<<"jose">>, + {git,"https://github.com/NYNJA-MC/jose-erlang.git", + {ref,"709401825367c029986a3bc688de3ed016b914cd"}}, + 0}, {<<"jsx">>, {git,"https://github.com/talentdeficit/jsx.git", {ref,"fc2a001073f2300ba38427c23e83d5673c020542"}}, @@ -94,23 +117,29 @@ 1}, {<<"lager">>, {git,"git://github.com/voxoz/lager", - {ref,"1ecf4c17e9e39dd7a1943140477f632d17518f0c"}}, + {ref,"9d657ab5acc9e82354f945ef819dab3760fe63cb"}}, 1}, {<<"libphonenumber_erlang">>, {git,"https://github.com/marinakr/libphonenumber_erlang.git", - {ref,"3a6be75ef4f6fdd40fa8afee2a7fc5cf2ddb8601"}}, + {ref,"a78037e6f06f0aeeed9eaf4b8abaf378d24a0d01"}}, 0}, {<<"locus">>, {git,"https://github.com/g-andrade/locus.git", {ref,"0ea9079ce5686573e0e70e2b1311343dd25feef8"}}, 0}, + {<<"mad">>, + {git,"git://github.com/synrc/mad", + {ref,"0cd4d9e709d0ca70ec6d01b9d434692eff51222d"}}, + 1}, + {<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},2}, {<<"migresia">>, {git,"https://github.com/yoonka/migresia.git", {ref,"40e11825e01502d045e87bf8b5d7dc5a9d6e3f73"}}, 0}, + {<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.0.2">>},2}, {<<"mini_s3">>, {git,"https://github.com/chef/mini_s3.git", - {ref,"df0c68ea901343b8e0c647142d88d7f3aae27e7b"}}, + {ref,"73c1be787dfe590113419091c531564e58592478"}}, 0}, {<<"mochiweb">>, {git,"git://github.com/voxoz/mochiweb", @@ -125,18 +154,26 @@ {git,"git://github.com/synrc/nitro", {ref,"1aeb421c332f94b135563f8e855b139c1b067f59"}}, 1}, - {<<"opencensus-erlang">>, - {git,"https://github.com/voxoz/opencensus-erlang", - {ref,"8dc9ae86f5c1ef69593fe37b49e649afed6bd201"}}, + {<<"opencensus">>, + {git,"https://github.com/census-instrumentation/opencensus-erlang", + {ref,"7fb276ff73d677c00458922c9180df634f45e018"}}, 0}, - {<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.0">>},2}, + {<<"parse_trans">>, + {git,"https://github.com/uwiger/parse_trans.git", + {ref,"e61ab8e09eee791222ba49a5706162c4985f24b0"}}, + 1}, {<<"prometheus">>, {git,"https://github.com/deadtrickster/prometheus.erl", - {ref,"46ea4b487baf4f6cc44495eae07582808e0369d4"}}, + {ref,"39c6595728041fa075561c03f3d45881065cc7e7"}}, + 0}, + {<<"qdate">>, + {git,"https://github.com/enterprizing/qdate.git", + {ref,"fba988fc54214bb37a3ce11d5c5a3cc68752c3ce"}}, 0}, + {<<"qdate_localtime">>,{pkg,<<"qdate_localtime">>,<<"1.1.0">>},1}, {<<"ranch">>, - {git,"git://github.com/voxoz/ranch", - {ref,"1a75038c82ede22efec46a7dca192b8ce26309e0"}}, + {git,"https://github.com/ninenines/ranch", + {ref,"3190aef88aea04d6dce8545fe9b4574288903f44"}}, 1}, {<<"rest">>, {git,"git://github.com/synrc/rest", @@ -146,20 +183,35 @@ {git,"https://github.com/talentdeficit/rfc3339", {ref,"90effc078c5e673d025b2c1269a153ad4748d3df"}}, 0}, + {<<"sh">>, + {git,"git://github.com/synrc/sh", + {ref,"a4e646aba9a4c18a34d19089e8b9391076b2bff8"}}, + 2}, {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.4">>},1}, {<<"stacktrace_compat">>,{pkg,<<"stacktrace_compat">>,<<"1.0.2">>},1}, {<<"syn">>, {git,"git://github.com/ostinelli/syn", {ref,"9964eb8969b6e1e712249d3aed4f3dfafd3aaac3"}}, 1}, + {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.4.1">>},3}, + {<<"uuid">>, + {git,"https://github.com/avtobiff/erlang-uuid.git", + {ref,"cb02a2039a9b29dd2eef0446039c9c6e164df9ef"}}, + 0}, {<<"wts">>, {git,"https://github.com/tsloughter/wts.git", - {ref,"5613b6c4354867fd2b02fde5ef15bf80190b8586"}}, + {ref,"09c039e4d9f7ab9b984c9ffb45dd0db0add58f96"}}, 0}]}. [ {pkg_hash,[ {<<"certifi">>, <<"75424FF0F3BAACCFD34B1214184B6EF616D89E420B258BB0A5EA7D7BC628F7F0">>}, - {<<"parse_trans">>, <<"09765507A3C7590A784615CFD421D101AEC25098D50B89D7AA1D66646BC571C1">>}, + {<<"cf">>, <<"5CB902239476E141EA70A740340233782D363A31EEA8AD37049561542E6CD641">>}, + {<<"erlware_commons">>, <<"0CE192AD69BC6FD0880246D852D0ECE17631E234878011D1586E053641ED4C04">>}, + {<<"idna">>, <<"689C46CBCDF3524C44D5F3DDE8001F364CD7608A99556D8FBD8239A5798D4C10">>}, + {<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>}, + {<<"mimerl">>, <<"993F9B0E084083405ED8252B99460C4F0563E41729AB42D9074FD5E52439BE88">>}, + {<<"qdate_localtime">>, <<"5F6C3ACF10ECC5A7E2EFA3DCD2C863102B962188DBD9E086EC01D29FE029DA29">>}, {<<"ssl_verify_fun">>, <<"F0EAFFF810D2041E93F915EF59899C923F4568F4585904D010387ED74988E77B">>}, - {<<"stacktrace_compat">>, <<"8AD31C32C9A0EADB1EB298F04DC8B0C8D79BCC6233A638B02791FFCA4F331275">>}]} + {<<"stacktrace_compat">>, <<"8AD31C32C9A0EADB1EB298F04DC8B0C8D79BCC6233A638B02791FFCA4F331275">>}, + {<<"unicode_util_compat">>, <<"D869E4C68901DD9531385BB0C8C40444EBF624E60B6962D95952775CAC5E90CD">>}]} ]. diff --git a/rebar3 b/rebar3 new file mode 100755 index 0000000000000000000000000000000000000000..865ec59000c95fec9d6adf6c0e24f42c88f5014c Binary files /dev/null and b/rebar3 differ diff --git a/sys.config b/sys.config index 74771f3c109a7ee9da41beab6a443ded50ff2c9e..4cc69d0e2c9f4b524b8c5e1708f71c5295a8d812 100644 --- a/sys.config +++ b/sys.config @@ -1,43 +1,64 @@ +%% -*- mode:erlang; erlang-indent-level: 2; indent-tabs-mode: nil -*- [ - {review,[{host,"ns.synrc.com"}]}, - {bert,[{js,"apps/roster/priv/macbert/"}, - {erl,"apps/roster/src"}, - {google,"apps/service/priv/"}, - {disallowed,['feed', 'Whitelist', 'FakeNumbers','Schedule','Index','process']}, - {allowed_hrl, ["roster"]}, - {custom_validate, {roster, validate}}, - {swift,"apps/roster/priv/macbert/"}]}, - {roster,[ - {freeze_time, 1000}, - {upload,"./storage"}, - {email_api,[ - {from,<<"Nynja App">>}, - {host,<<"smtp.gmail.com">>}, - {login,<<"sandbox.nynja.app@gmail.com">>}, - {password,<<"tecSynt_nynja110917">>}]}, - {vox_api,[ - {account_id,<<"1152724">>}, - {api_key,<<"94b9f0f0-7d93-4e63-9061-aef62c30182b">>}, - {application_name,<<"videoconf.nynja.voximplant.com">>}, - {conference_rule_id,<<"294893">>}]}, - {telesign_api,[ - {customer_id,<<"CF6E2918-89A9-417F-8074-382908FEDCD9">>}, - {api_key,<<"JE1zC3gjYg1fITkPK4xYtAGk0uVpbsWSqi7e54wg+sLbMGAbKoJSw0/BRJaAezZpVZzEQfHYyL3PKutGfZ38Gg==">>}]}, - {amazon_api,[ - {access_key_id,<<"AKIAITNYSJXI2NUDRRXA">>}, - {secret_access_key,<<"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">>}, - {sts, [ - {host, <<"sts.amazonaws.com">>}, - {region, <<"us-east-1">>}, - {service, <<"sts">>}, - {action, <<"GetSessionToken">>}, - {duration_seconds, <<"43200">>}, - {version, <<"2011-06-15">>}, - {http_method, <<"GET">>}, - {endpoint, <<"https://sts.amazonaws.com">>} - ]}, - {s3, [ - {default_bucket_name,<<"nynja-defaults">>} + {kernel, + [{logger_level, info}, + {logger, + [ {handler, default, logger_std_h, + #{level => error, + config => #{file => "log/erlang.log"}, + formatter => {logger_formatter, + #{template => [time," [",level,"] ",pid," [", module, "] ", msg,"\n"]}} + }}, + {handler, info_handler, logger_std_h, + #{level => info, + config => #{file => "log/info.log"}, + formatter => {logger_formatter, + #{template => [time," [",level,"] ",pid," [", module, "] ", msg,"\n"]}} + }}, + {handler, debug_handler, logger_std_h, + #{level => debug, + config => #{file => "log/debug.log"}, + formatter => {logger_formatter, + #{template => [time," [",level,"] ",pid," [", module, "] ", msg,"\n"]}} + }} + ]}]}, + {review,[{host,"ns.synrc.com"}]}, + {roster, + [ + {freeze_time, 1000}, + {upload,"./storage"}, + {email_api, + [ + {from,<<"Nynja App">>}, + {host,<<"smtp.gmail.com">>}, + {login,<<"sandbox.nynja.app@gmail.com">>}, + {password,<<"tecSynt_nynja110917">>}]}, + {vox_api, + [ + {account_id,<<"1152724">>}, + {api_key,<<"94b9f0f0-7d93-4e63-9061-aef62c30182b">>}, + {application_name,<<"videoconf.nynja.voximplant.com">>}, + {conference_rule_id,<<"294893">>}]}, + {telesign_api, + [ + {customer_id,<<"CF6E2918-89A9-417F-8074-382908FEDCD9">>}, + {api_key,<<"JE1zC3gjYg1fITkPK4xYtAGk0uVpbsWSqi7e54wg+sLbMGAbKoJSw0/BRJaAezZpVZzEQfHYyL3PKutGfZ38Gg==">>}]}, + {amazon_api, + [ + {access_key_id,<<"AKIAITNYSJXI2NUDRRXA">>}, + {secret_access_key,<<"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">>}, + {sts, [ + {host, <<"sts.amazonaws.com">>}, + {region, <<"us-east-1">>}, + {service, <<"sts">>}, + {action, <<"GetSessionToken">>}, + {duration_seconds, <<"43200">>}, + {version, <<"2011-06-15">>}, + {http_method, <<"GET">>}, + {endpoint, <<"https://sts.amazonaws.com">>} + ]}, + {s3, [ + {default_bucket_name,<<"nynja-defaults">>} ]} ]}, {google_api, [ @@ -57,118 +78,125 @@ {fake_numbers_check, true}, {validate_token, micro_auth} ]}, - {rest, [{port,8888}, - {basic_auth,[ - {username,<<"nynja">>}, - {password,<<"nynjaTS">>}]}, -%% api which can be opened without basic auth - {open_api_list, [<<"/test">>, <<"/metrics">>]} - ]}, - {n2o, [{port,8000}, - {upload,"./storage"}, - {app,review}, - {tables,[ cookies, file, caching, ring, async, nick, names, surnames, system]}, - {pickler,nitro_pickle}, - {formatter,n2o_bert}, - {auth_ttl, 86400}, %% 24 h - {protocols,[n2o_nitro,n2o_ftp,roster_proto]}, - {log_modules,[roster, n2o_vnode]}, - {log_level,roster}, - {vnode, {roster, get_vnode}}, - {validate, {roster, validate}} + {rest, [{port, 8888}, + {start_cowboy, true}, + {basic_auth,[ + {username,<<"nynja">>}, + {password,<<"nynjaTS">>}]}, + %% api which can be opened without basic auth + {open_api_list, [<<"/test">>, <<"/metrics">>]} ]}, - {emq_dashboard, [ - {listeners_dash,[ - {http,18083,[ - {acceptors,4},{max_clients,512}] - }, - {https,18084,[ - {acceptors,4},{max_clients,512},{handshake_timeout, 15}, -% {certfile, "etc/certs/cert.pem"}, - {keyfile, "etc/certs/privkey.pem"}, - {cacertfile ,"etc/certs/fullchain.pem"}, - {verify,verify_peer}, - {fail_if_no_peer_cert, false}]} + {n2o, [{port,8000}, + {upload,"./storage"}, + {app,review}, + {tables,[ cookies, file, caching, ring, async, nick, names, surnames, system]}, + {pickler,nitro_pickle}, + {formatter,n2o_bert}, + {auth_ttl, 86400}, %% 24 h + {protocols,[n2o_nitro,n2o_ftp,roster_proto]}, + {log_modules,[roster, n2o_vnode]}, + {log_level,roster}, + {vnode, {roster, get_vnode}}, + {validate, {roster, validate}} ]}, - {default_admin, [ - {name, <<"nynja">>}, - {password, <<"nynjaAdmin">>} - ]}]}, - {emq_modules, [{modules,[{emq_mod_presence,[{qos,1}]}, - {emq_mod_subscription,[{<<"%u/%c/#">>,2}]}, - {emq_mod_rewrite,[{rewrite,"x/#","^x/y/(.+)$","z/y/$1"}, - {rewrite,"y/+/z/#","^y/(.+)/z/(.+)$","y/z/$2"}]}]}]}, -{emqttd, - [{plugins_loaded_file,"etc/loaded_plugins"}, - {plugins_etc_dir,"etc/plugins/"}, - {broker_sys_interval,60}, - {conn_force_gc_count,100}, - {cache_acl,true}, - {acl_file,"etc/acl.conf"}, - {allow_anonymous,true}, - {protocol,[{max_clientid_len,1024},{max_packet_size,25165824}]}, - {client,[{client_idle_timeout,30000},{client_enable_stats,false}]}, - {session, - [{upgrade_qos,false}, - {max_inflight,32}, - {retry_interval,20000}, - {max_awaiting_rel,100000}, - {await_rel_timeout,60000}, - {enable_stats,false}, - {expiry_interval,7200000}]}, - {queue, - [{priority,[]}, - {type,simple}, - {max_length,infinity}, - {low_watermark,0.2}, - {high_watermark,0.6}, - {queue_qos0,true}]}, - {pubsub,[{pool_size,8},{by_clientid,true},{async,true}]}, - {bridge,[{max_queue_len,10000},{ping_down_interval,1}]}, - {listeners, - [{tcp,1883, - [{connopts,[]}, - {sockopts,[{backlog,1024},{nodelay,true}]}, - {acceptors,8}, - {max_clients,1048576}, - {tune_buffer,false}]}, - {ssl,8883, - [{sslopts, - [{versions,['tlsv1.2','tlsv1.1',tlsv1]}, - {cacertfile,"etc/certs/cert.pem"}, - {keyfile,"etc/certs/privkey.pem"}, - {certfile,"etc/certs/fullchain.pem"}]}, - {connopts,[]}, - {sockopts,[{nodelay,true}]}, - {acceptors,8}, - {max_clients,1048576}]}, - {http,8083, - [{connopts,[]}, - {sockopts,[{nodelay,true}]}, - {acceptors,8}, - {max_clients,1048576}]}, - {https,8084, - [{sslopts, - [{cacertfile,"etc/certs/cert.pem"}, - {keyfile,"etc/certs/privkey.pem"}, - {certfile,"etc/certs/fullchain.pem"}]}, - {connopts,[]}, - {sockopts,[{nodelay,true}]}, - {acceptors,8}, - {max_clients,1048576}]}]}, - {sysmon, - [{long_gc,false}, - {long_schedule,2400}, - {large_heap,67108864}, - {busy_port,false}, - {busy_dist_port,true}]}]}, - {kvs, [{dba,store_mnesia}, + {emq_dashboard, + [ + {listeners_dash, + [ + {http,18083, + [ + {acceptors,4},{max_clients,512}] + }, + {https,18084, + [ + {acceptors,4},{max_clients,512},{handshake_timeout, 15}, + %% {certfile, "etc/certs/cert.pem"}, + {keyfile, "etc/certs/privkey.pem"}, + {cacertfile ,"etc/certs/fullchain.pem"}, + {verify,verify_peer}, + {fail_if_no_peer_cert, false}]} + ]}, + {default_admin, [ + {name, <<"nynja">>}, + {password, <<"nynjaAdmin">>} + ]}]}, + {emq_modules, + [{modules,[{emq_mod_presence,[{qos,1}]}, + {emq_mod_subscription,[{<<"%u/%c/#">>,2}]}, + {emq_mod_rewrite, + [{rewrite,"x/#","^x/y/(.+)$","z/y/$1"}, + {rewrite,"y/+/z/#","^y/(.+)/z/(.+)$","y/z/$2"}]}]}]}, + {emqttd, + [{plugins_loaded_file,"etc/loaded_plugins"}, + {plugins_etc_dir,"etc/plugins/"}, + {broker_sys_interval,60}, + {conn_force_gc_count,100}, + {cache_acl,true}, + {acl_file,"etc/acl.conf"}, + {allow_anonymous,true}, + {protocol,[{max_clientid_len,1024},{max_packet_size,25165824}]}, + {client,[{client_idle_timeout,30000},{client_enable_stats,false}]}, + {session, + [{upgrade_qos,false}, + {max_inflight,32}, + {retry_interval,20000}, + {max_awaiting_rel,100000}, + {await_rel_timeout,60000}, + {enable_stats,false}, + {expiry_interval,7200000}]}, + {queue, + [{priority,[]}, + {type,simple}, + {max_length,infinity}, + {low_watermark,0.2}, + {high_watermark,0.6}, + {queue_qos0,true}]}, + {pubsub,[{pool_size,8},{by_clientid,true},{async,true}]}, + {bridge,[{max_queue_len,10000},{ping_down_interval,1}]}, + {listeners, + [{tcp,1883, + [{connopts,[]}, + {sockopts,[{backlog,1024},{nodelay,true}]}, + {acceptors,8}, + {max_clients,1048576}, + {tune_buffer,false}]}, + {ssl,8883, + [{sslopts, + [{versions,['tlsv1.2','tlsv1.1',tlsv1]}, + {cacertfile,"etc/certs/cert.pem"}, + {keyfile,"etc/certs/privkey.pem"}, + {certfile,"etc/certs/fullchain.pem"}]}, + {connopts,[]}, + {sockopts,[{nodelay,true}]}, + {acceptors,8}, + {max_clients,1048576}]}, + {http,8083, + [{connopts,[]}, + {sockopts,[{nodelay,true}]}, + {acceptors,8}, + {max_clients,1048576}]}, + {https,8084, + [{sslopts, + [{cacertfile,"etc/certs/cert.pem"}, + {keyfile,"etc/certs/privkey.pem"}, + {certfile,"etc/certs/fullchain.pem"}]}, + {connopts,[]}, + {sockopts,[{nodelay,true}]}, + {acceptors,8}, + {max_clients,1048576}]}]}, + {sysmon, + [{long_gc,false}, + {long_schedule,2400}, + {large_heap,67108864}, + {busy_port,false}, + {busy_dist_port,true}]}]}, + {kvs, [{dba,store_mnesia}, {schema, [kvs_user, kvs_acl, kvs_feed, kvs_subscription, roster, micro, emqttd_kvs, bpe_metainfo]} -%% ,{generation, {roster_test, limit}} -%% ,{forbidding, {roster_test, forbid}} - ]}, - {bpe,[{process_worker,{job,worker}} - ,{ttl, 8640000} - ]}, - {locus, [{url, "http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz"}]} + %% ,{generation, {roster_test, limit}} + %% ,{forbidding, {roster_test, forbid}} + ]}, + {bpe,[{process_worker,{job,worker}} + ,{ttl, 8640000} + ]}, + {locus, [{url, "http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz"}]} ]. diff --git a/test/api_nynja.erl b/test/api_nynja.erl new file mode 100644 index 0000000000000000000000000000000000000000..d3f08de15027af8905674d3fee572f44b095a692 --- /dev/null +++ b/test/api_nynja.erl @@ -0,0 +1,685 @@ +%% coding: latin-1 +%% This code is generated from nynja_rest.yaml +%% on 2020-04-14 9:18:48 UTC +%% Using openapi rebar3 plugin version: #4dcf370 +%% Do not manually change this code! +%% +%% json_schema/0 implements a JSON Schema for the definitions +%% Reference should be fixed! +%% At the moment, ref with definition is not folowwed by jesse +%% Use jsx:prettify(jsx:encode(json_schema())) to get a JSON string. + +-module(api_nynja). + +-export([json_schema/0]). +-export([operation/1, operations/0, definitions/0, baseurl/1]). + +-export([request/3, http_request/4]). +-export([validate_request/2, validate_response/4]). + +baseurl(Config) -> + proplists:get_value(base_url, Config, <<"http://localhost:8888/">>). + +definitions() -> + [{"/components/schemas/RoomId",#{<<"type">> => <<"object">>}}, + {"/components/schemas/MemberEntry", + #{<<"properties">> => + #{<<"error">> => #{<<"type">> => <<"string">>}, + <<"is_member">> => #{<<"type">> => <<"string">>}, + <<"phone_id">> => #{<<"type">> => <<"string">>}}, + <<"required">> => [<<"is_member">>,<<"phone_id">>], + <<"type">> => <<"object">>}}, + {"/components/schemas/PhoneEntry", + #{<<"properties">> => + #{<<"created">> => + #{<<"formst">> => <<"date-time">>,<<"type">> => <<"string">>}, + <<"phone">> => #{<<"type">> => <<"integer">>}}, + <<"type">> => <<"object">>}}, + {"/components/schemas/Session", + #{<<"properties">> => #{<<"created">> => #{<<"type">> => <<"integer">>}}, + <<"type">> => <<"object">>}}]. + +operations() -> + #{'DeleteCRIRoom' => + #{method => delete, + parameters => + [[{"in","header"}, + {"name","phoneid"}, + {"schema",#{<<"type">> => <<"string">>}}], + [{"in","query"}, + {"name","room_id"}, + {"schema",#{<<"type">> => <<"string">>}}]], + path => <<"/cri/rooms">>, + responses => + #{200 => + [{"application/json", + #{schema => + #{<<"properties">> => + #{<<"data">> => + #{<<"properties">> => + #{<<"type">> => + #{<<"type">> => + <<"string">>}}, + <<"type">> => <<"object">>}, + <<"status">> => + #{<<"type">> => <<"integer">>}}, + <<"type">> => <<"object">>}}}], + 400 => [{"text/plain",undefined}], + 401 => [{"text/plain",undefined}]}, + tags => []}, + 'DeleteMembersCRIRoom' => + #{method => delete, + parameters => + [[{"in","header"}, + {"name","phoneid"}, + {"schema",#{<<"type">> => <<"string">>}}], + [{"in","query"}, + {"name","room_id"}, + {"schema",#{<<"type">> => <<"string">>}}], + [{"in","query"}, + {"name","phone_ids"}, + {"description","comma separated phone ids"}, + {"schema",#{<<"type">> => <<"string">>}}]], + path => <<"/cri/rooms/members">>, + responses => + #{200 => + [{"application/json", + #{schema => + #{<<"properties">> => + #{<<"data">> => + #{<<"items">> => + #{<<"$ref">> => + <<"/components/schemas/MemberEntry">>}, + <<"type">> => <<"array">>}, + <<"status">> => + #{<<"type">> => <<"integer">>}}, + <<"type">> => <<"object">>}}}], + 400 => [{"text/plain",undefined}], + 401 => [{"text/plain",undefined}], + 403 => [{"text/plain",undefined}]}, + tags => []}, + 'GetCRIRoom' => + #{method => get, + parameters => + [[{"in","header"}, + {"name","phoneid"}, + {"schema",#{<<"type">> => <<"string">>}}], + [{"in","query"}, + {"name","room_id"}, + {"schema",#{<<"type">> => <<"string">>}}]], + path => <<"/cri/rooms/type">>, + responses => + #{200 => + [{"application/json", + #{schema => + #{<<"properties">> => + #{<<"data">> => + #{<<"properties">> => + #{<<"type">> => + #{<<"type">> => + <<"string">>}}, + <<"type">> => <<"object">>}, + <<"status">> => + #{<<"type">> => <<"integer">>}}, + <<"type">> => <<"object">>}}}], + 400 => [{"text/plain",undefined}], + 401 => [{"text/plain",undefined}]}, + tags => []}, + 'GetFakeNumbers' => + #{method => get,parameters => [],path => <<"/fake_numbers">>, + responses => #{200 => [{"text/html",undefined}]}, + tags => []}, + 'GetMembersCRIRoom' => + #{method => get, + parameters => + [[{"in","header"}, + {"name","phoneid"}, + {"schema",#{<<"type">> => <<"string">>}}], + [{"in","query"}, + {"name","room_id"}, + {"schema",#{<<"type">> => <<"string">>}}], + [{"in","query"}, + {"name","phone_ids"}, + {"description","comma separated phone ids"}, + {"schema",#{<<"type">> => <<"string">>}}]], + path => <<"/cri/rooms/members">>, + responses => + #{200 => + [{"application/json", + #{schema => + #{<<"properties">> => + #{<<"data">> => + #{<<"items">> => + #{<<"$ref">> => + <<"/components/schemas/MemberEntry">>}, + <<"type">> => <<"array">>}, + <<"status">> => + #{<<"type">> => <<"integer">>}}, + <<"type">> => <<"object">>}}}], + 400 => [{"text/plain",undefined}], + 401 => [{"text/plain",undefined}]}, + tags => []}, + 'GetSessions' => + #{method => get, + parameters => + [[{"in","query"}, + {"name","phone"}, + {"schema",#{<<"type">> => <<"string">>}}]], + path => <<"/sessions">>, + responses => + #{200 => + [{"application/json", + #{schema => + #{<<"items">> => + #{<<"$ref">> => + <<"/components/schemas/Session">>}, + <<"type">> => <<"array">>}}}], + 400 => [{"text/plain",undefined}], + 401 => [{"text/plain",undefined}], + 404 => [{"text/plain",undefined}]}, + tags => []}, + 'GetWhiteList' => + #{method => get, + parameters => + [[{"in","query"}, + {"name","phone"}, + {"schema",#{<<"type">> => <<"string">>}}]], + path => <<"/admin_whitelist">>, + responses => + #{200 => + [{"application/json", + #{schema => #{<<"type">> => <<"string">>}}}, + {"application/html",undefined}], + 404 => []}, + tags => []}, + 'IsAlive' => + #{method => get,parameters => [],path => <<"/test">>, + responses => + #{200 => + [{"application/json", + #{schema => + #{<<"properties">> => + #{<<"Current Time">> => + #{<<"format">> => <<"date-time">>, + <<"type">> => <<"string">>}}, + <<"type">> => <<"object">>}}}]}, + tags => []}, + 'Metrics' => + #{method => get,parameters => [],path => <<"/metrics">>, + responses => #{200 => [{"text/plain",undefined}]}, + tags => []}, + 'PostCRIRoom' => + #{method => post, + parameters => + [[{"in","header"}, + {"name","phoneid"}, + {"schema",#{<<"type">> => <<"string">>}}], + [{"in","query"}, + {"name","room_id"}, + {"schema",#{<<"type">> => <<"string">>}}]], + path => <<"/cri/rooms">>, + responses => + #{200 => + [{"application/json", + #{schema => + #{<<"properties">> => + #{<<"data">> => + #{<<"properties">> => + #{<<"type">> => + #{<<"type">> => + <<"string">>}}, + <<"type">> => <<"object">>}, + <<"status">> => + #{<<"type">> => <<"integer">>}}, + <<"type">> => <<"object">>}}}], + 400 => [{"text/plain",undefined}], + 401 => [{"text/plain",undefined}]}, + tags => []}, + delete_fake_numbers => + #{method => delete, + parameters => + [[{"in","query"}, + {"name","phone"}, + {"schema",#{<<"type">> => <<"string">>}}]], + path => <<"/fn">>, + responses => + #{200 => + [{"application/json", + #{schema => #{<<"type">> => <<"object">>}}}]}, + tags => []}, + delete_whitelist => + #{method => delete, + parameters => + [[{"in","query"}, + {"name","phone"}, + {"schema",#{<<"type">> => <<"string">>}}]], + path => <<"/whitelist">>, + responses => + #{200 => + [{"application/json", + #{schema => #{<<"type">> => <<"object">>}}}]}, + tags => []}, + fake_numbers => + #{method => get,parameters => [],path => <<"/fn">>, + responses => + #{200 => + [{"application/json", + #{schema => + #{<<"properties">> => + #{<<"Data">> => + #{<<"items">> => + #{<<"$ref">> => + <<"/components/schemas/PhoneEntry">>}, + <<"type">> => <<"array">>}, + <<"Status">> => + #{<<"type">> => <<"string">>}}, + <<"type">> => <<"object">>}}}], + 400 => [{"text/plain",undefined}], + 401 => [{"text/plain",undefined}]}, + tags => []}, + put_fake_numbers => + #{method => put,parameters => [],path => <<"/fn">>, + responses => + #{200 => + [{"application/json", + #{schema => #{<<"type">> => <<"object">>}}}]}, + tags => []}, + put_whitelist => + #{method => put,parameters => [],path => <<"/whitelist">>, + responses => + #{200 => + [{"application/json", + #{schema => #{<<"type">> => <<"object">>}}}]}, + tags => []}, + whitelist => + #{method => get,parameters => [],path => <<"/whitelist">>, + responses => + #{200 => + [{"application/json", + #{schema => + #{<<"properties">> => + #{<<"Data">> => + #{<<"items">> => + #{<<"$ref">> => + <<"/components/schemas/PhoneEntry">>}, + <<"type">> => <<"array">>}, + <<"Status">> => + #{<<"type">> => <<"string">>}}, + <<"type">> => <<"object">>}}}], + 401 => [{"text/plain",undefined}], + 404 => [{"text/plain",undefined}]}, + tags => []}}. + +json_schema() -> + #{<<"$schema">> => + <<"http://json-schema.org/draft-04/schema#">>, + <<"properties">> => + maps:from_list([{iolist_to_binary(K), V} + || {K, V} <- definitions()])}. + +operation(Id) -> maps:get(Id, operations()). + + + +request(OpId, Params, Config) -> + request(api_nynja, OpId, Params, Config). + +validate_request(OperationId, Args) -> + validate_request(api_nynja, OperationId, Args). + +validate_response(OperationId, StatusCode, Headers, + RawBody) -> + validate_response(api_nynja, OperationId, StatusCode, + Headers, RawBody). + +request(Mod, OpId, ParamsIn, Config) -> + begin + Params = if is_list(ParamsIn) -> + maps:from_list(ParamsIn); + true -> ParamsIn + end, + Op = Mod:operation(OpId), + Method = maps:get(method, Op), + BaseUrl = string:trim(Mod:baseurl(Config), trailing, + "/"), + {ParamHeaders, HKeys} = header(Op, Params), + {Path, PKeys} = path(Op, Params), + {Query, QKeys} = query(Op, Params), + Headers = ParamHeaders ++ + proplists:get_value(headers, Config, []) ++ + case proplists:get_value(security, Config) of + undefined -> []; + {authorization_basic, + [#{username := User, password := Password}]} -> + Token = + base64:encode(unicode:characters_to_binary([User, + ":", + Password])), + [{"Authorization", + io_lib:format("Basic ~s", [Token])}]; + {authorization_bearer, [#{accessToken := Token}]} -> + [{"Authorization", + io_lib:format("Bearer ~s", [Token])}] + end, + Keys = HKeys ++ PKeys ++ QKeys, + BodyParams = maps:get(body, Params, + maps:without(Keys, Params)), + case Method of + get -> + #{method => Method, + url => iolist_to_binary([BaseUrl, Path, Query]), + headers => Headers}; + _ -> + {ContentType, Body} = make_body(BodyParams, Path), + #{method => Method, + url => iolist_to_binary([BaseUrl, Path, Query]), + headers => Headers, body => Body, + content_type => ContentType} + end + end. + +header(#{path := Endpoint, parameters := Parameters}, + Args) + when is_map(Args) -> + begin + InHeader = [Param + || Param <- Parameters, + lists:member({"in", "header"}, Param)], + {lists:foldl(fun (Param, Headers) -> + Name = proplists:get_value("name", Param), + case {proplists:get_value("required", Param, false), + get_by_similar_key(Name, Args)} + of + {false, undefined} -> Headers; + {true, undefined} -> + error({required, Name, Param, Endpoint}); + {_, {Key, Value}} -> + [{to_str(Key), to_str(Value)} | Headers] + end + end, + [], InHeader), + InHeader} + end. + +path(#{path := Endpoint, parameters := Parameters}, + Args) + when is_map(Args) -> + begin + InPath = [Param + || Param <- Parameters, + lists:member({"in", "path"}, Param)], + {lists:foldl(fun (Param, Path) -> + Name = proplists:get_value("name", Param), + case {proplists:get_value("required", Param, false), + get_by_similar_key(Name, Args)} + of + {false, undefined} -> Path; + {true, undefined} -> + throw({error, + {required, Name, Param, Endpoint}}); + {_, {_Key, Value}} -> + iolist_to_binary(string:replace(Path, + "{" ++ + Name ++ "}", + to_str(Value))) + end + end, + Endpoint, InPath), + InPath} + end. + +query(#{path := Endpoint, parameters := Parameters}, + Args) + when is_map(Args) -> + begin + InQuery = [Param + || Param <- Parameters, + lists:member({"in", "query"}, Param)], + Query = lists:foldr(fun (Param, Query) -> + Name = proplists:get_value("name", Param), + Explode = proplists:get_value("explode", + Param, false), + case {proplists:get_value("required", Param, + false), + get_by_similar_key(Name, Args, Explode)} + of + {false, undefined} -> Query; + {true, undefined} -> + throw({error, + {required, Name, Param, + Endpoint}}); + {_, {_, Value}} -> + query_serialize(Name, Value, Param) ++ + Query + end + end, + [], InQuery), + {case [[K, "=", V] || {K, V} <- Query] of + [] -> <<>>; + Qs -> iolist_to_binary(["?" | lists:join("&", Qs)]) + end, + InQuery} + end. + +query_serialize(Name, Value, Param) -> + begin + Style = proplists:get_value("style", Param, "form"), + Explode = proplists:get_value("explode", Param, true), + Array = is_array(proplists:get_value("schema", Param)), + AllowReserved = proplists:get_value("allowReserved", + Param, false), + EncodedValue = case AllowReserved of + true -> Value; + false when Array -> + [http_uri:encode(to_str(V)) || V <- Value]; + false -> http_uri:encode(to_str(Value)) + end, + case {Array, Explode} of + {false, _} -> [{Name, EncodedValue}]; + {true, true} -> [{Name, V} || V <- EncodedValue]; + {true, false} -> + case Style of + "form" -> + [{Name, + iolist_to_binary(lists:join(",", EncodedValue))}]; + "spaceDelimited" -> + [{Name, + iolist_to_binary(lists:join("%20", EncodedValue))}]; + "pipeDelimited" -> + [{Name, + iolist_to_binary(lists:join("|", EncodedValue))}] + end + end + end. + +is_array(Schema) -> maps:is_key(<<"items">>, Schema). + +prepare_validation(Mod) -> + [jesse:add_schema(Def, Schema) + || {Def, Schema} <- Mod:definitions()]. + +validate(Schema, Term) -> + try jesse:validate_with_schema(Schema, Term) catch + Error -> {error, Error} + end. + +validate_request(Mod, OperationId, Args) + when is_list(Args) -> + validate_request(Mod, OperationId, + maps:from_list(Args)); +validate_request(Mod, OperationId, Args) + when is_map(Args) -> + begin + prepare_validation(Mod), + #{parameters := Parameters} = + Mod:operation(OperationId), + ToCheck = [Param + || Param <- Parameters, + not lists:member({"in", "path"}, Param)], + Errors = lists:foldl(fun (Param, Errs) -> + Name = proplists:get_value("name", Param), + case {proplists:get_value("required", Param, + false), + get_by_similar_key(Name, Args)} + of + {false, undefined} -> Errs; + {true, undefined} -> + [{required, Name, Param, OperationId} + | Errs]; + {_, {_, Value}} -> + case + validate(proplists:get_value("schema", + Param, + #{}), + Value) + of + {error, E} -> [E | Errs]; + _ -> Errs + end + end + end, + [], ToCheck), + case Errors of + [] -> ok; + _ -> {errors, {Mod, OperationId, Args, Errors}} + end + end. + +validate_response(Mod, OperationId, StatusCode, Headers, + RawBody) -> + begin + ContentType = + content_type(proplists:get_value("content-type", + Headers)), + {ok, Body} = parse_body(ContentType, + iolist_to_binary(RawBody)), + #{responses := Resps} = Mod:operation(OperationId), + prepare_validation(Mod), + case maps:get(StatusCode, Resps, error) of + error -> {error, {StatusCode, unspecified}}; + [] -> {ok, StatusCode, Body}; + ContentTypes -> + case get_matching(ContentType, ContentTypes) of + #{schema := Schema} -> + case validate(Schema, Body) of + {ok, _} -> {ok, StatusCode, Body}; + {error, E} -> {error, {validation, E}} + end; + undefined -> {ok, StatusCode, Body}; + no_match -> {error, {content_type, ContentType}} + end + end + end. + +http_request(#{url := Url} = Req, HttpOpts, Opts, Cfg) + when is_binary(Url) -> + http_request(Req#{url := binary_to_list(Url)}, HttpOpts, + Opts, Cfg); +http_request(#{method := Method, url := Url, + headers := Headers} = + Req, + HttpOpts, Opts, Cfg) -> + begin + {ok, ClientPid} = inets:start(httpc, + [{profile, test_browser}], stand_alone), + Resp = case Method of + get -> + log("GET ~p", [Url], Cfg), + httpc:request(Method, {Url, Headers}, HttpOpts, + [{socket_opts, [{reuseaddr, true}]} | Opts], + ClientPid); + _ -> + ContentType = maps:get(content_type, Req, ""), + Body = maps:get(body, Req, ""), + log("~p ~p~nContentType ~p~n~p", + [Method, Url, ContentType, Body], Cfg), + httpc:request(Method, {Url, Headers, ContentType, Body}, + HttpOpts, + [{socket_opts, [{reuseaddr, true}]} | Opts], + ClientPid) + end, + ok = gen_server:stop(ClientPid, normal, infinity), + process_response(Resp, Cfg) + end. + +process_response({ok, {{_, Code, Msg}, Headers, Body}}, + Cfg) -> + begin + log("Return code: ~p (~p), Headers: ~p", + [Code, Msg, Headers], Cfg), + {ok, Code, Headers, Body} + end; +process_response({error, Reason} = Error, Cfg) -> + begin log("Return error: ~p", [Reason], Cfg), Error end. + +parse_body("application/json", <<>>) -> {ok, #{}}; +parse_body("application/json", Body) -> + {ok, + jsx:decode(Body, + [{labels, attempt_atom}, return_maps])}; +parse_body("text/plain;charset=UTF-8", Bin) -> + {ok, unicode:characters_to_binary(Bin, unicode, utf8)}; +parse_body(_, Bin) -> {ok, Bin}. + +log(Fmt, Params, Cfg) -> + case proplists:get_value(logfun, Cfg) of + undefined -> ok; + F when is_function(F) -> F(Fmt, Params) + end. + +make_body(Params, _Path) when is_map(Params) -> + {"application/json", jsx:encode(Params)}; +make_body(Bin, _Path) when is_binary(Bin) -> + {"application/octet-stream", Bin}; +make_body([], Path) -> + {"application/x-www-form-urlencoded", + http_uri:encode(Path)}. + +content_type("application/json" ++ _) -> + "application/json"; +content_type(Other) -> Other. + +get_by_similar_key(Name, KVs, Array) -> + case Array of + true -> + case get_by_similar_key(Name, KVs) of + undefined -> + {ArrayName, _} = string:take(Name, "[]", false, + trailing), + get_by_similar_key(ArrayName, KVs); + Other -> Other + end; + _ -> get_by_similar_key(Name, KVs) + end. + +get_by_similar_key(Name, KVs) when is_list(Name) -> + case maps:get(Name, KVs, undefined) of + undefined -> + case maps:get(iolist_to_binary(Name), KVs, undefined) of + undefined -> + try AtomName = list_to_existing_atom(Name), + {AtomName, maps:get(AtomName, KVs)} + catch + _:_ -> undefined + end; + BinValue -> {iolist_to_binary(Name), BinValue} + end; + Value -> {Name, Value} + end. + +get_matching(Key, KVs) when is_list(KVs) -> + case {Key, KVs} of + {undefined, []} -> undefined; + {undefined, [{_, V} | _]} -> V; + _ -> + proplists:get_value(string:to_lower(Key), KVs, no_match) + end. + +to_str(Str) -> + begin + if is_list(Str) -> + binary_to_list(iolist_to_binary(Str)); + is_binary(Str) -> binary_to_list(Str); + true -> lists:concat([Str]) + end + end. \ No newline at end of file diff --git a/test/integration_test.ts b/test/integration_test.ts new file mode 100644 index 0000000000000000000000000000000000000000..cd6072f3539badddbf946c973980e4ad38c190f5 --- /dev/null +++ b/test/integration_test.ts @@ -0,0 +1,3 @@ +{node, mq, 'mq@127.0.0.1'}. + +{suites, ".", [server_SUITE]}. \ No newline at end of file diff --git a/test/nynja.erl b/test/nynja.erl new file mode 100644 index 0000000000000000000000000000000000000000..44ded49134efc75ec7cf71e4c3b5bd389e24baf2 --- /dev/null +++ b/test/nynja.erl @@ -0,0 +1,652 @@ +%%% Created : 25 Feb 2020 by Thomas Arts +-module(nynja). + +-include_lib("emqttc/include/emqttc_packet.hrl"). +-include("apps/roster/include/roster.hrl"). + +-compile([export_all, nowarn_export_all]). + +-define(HOST, os:getenv("NYNJA_HOST", "127.0.0.1")). +%% make this into opaque type +-record(user, {conn, + client_pid :: pid() | undefined, + client :: binary(), + phone = undefined :: undefined | binary(), + roster_id = <<>> :: binary(), + roster_ix = 1 :: integer(), + roster_uuid = <<>> :: binary(), + phone_uuid = <<>> :: binary()}). + +-define(FAKE_CODE, <<"903182">>). +-define(FAKE_PHONES, [<<"380500000000">>, <<"819036705059">>, <<"79999999999">>, <<"817012345678">>]). + +%% High level operations + +connect_sys() -> + User0 = ws_connect(), + + ClientId0 = pick_client_id(<<"sys">>), + ClientId = <<"sys_micro_", ClientId0/binary>>, + + User = User0#user{client = ClientId}, + + #mqtt_packet{ variable = #mqtt_packet_connack{} } = + ws_send(User, mqtt_connect(ClientId, <<"micro">>, <<>>)), + + User. + +register_profile(Info) -> + Sys = connect_sys(), + User = register_profile(Sys, Info), + ws_close(Sys), + User. + +register_profile(Sys, Info) -> + register_profile(Sys, Info, true). + +register_profile(Sys, Phone, Connect) when is_binary(Phone) -> + register_profile(Sys, #{ phone => Phone, fname => Phone, + lname => Phone, nick => Phone }, Connect); +register_profile(Sys, Info, Connect) -> + Phone = maps:get(phone, Info), + + PhoneLink = phone_uuid(Phone), + RosterUUID = roster_uuid(Phone), + + Roster = #'Roster'{ id = RosterUUID, + names = maps:get(fname, Info, []), + surnames = maps:get(lname, Info, []), + nick = maps:get(nick, Info), + phone = PhoneLink, + update = now_msec(), + status = create }, + + Profile = #'Profile'{ phone = PhoneLink, rosters = [Roster], update = now_msec(), status = create }, + + #mqtt_packet{ payload = #'Profile'{rosters = [#'Roster'{}]} } = do_register_profile(Sys, Profile), + case Connect of + true -> connect_user(Phone, PhoneLink, RosterUUID); + false -> ok + end. + +connect_user(Phone) -> + PhoneLink = phone_uuid(Phone), + RosterUUID = roster_uuid(Phone), + + connect_user(Phone, PhoneLink, RosterUUID). + +connect_user(Phone, PhoneLink, RosterUUID) -> + Token = jwt_token(RosterUUID), + User0 = ws_connect([{<<"x-json-web-token">>, Token}]), + ClientId = <<"emqttd_", (pick_client_id(Phone))/binary>>, + + User = User0#user{ client = ClientId, phone = Phone, + roster_uuid = RosterUUID, phone_uuid = PhoneLink }, + + connect_micro(User, ClientId), + + timer:sleep(25), %% avoid race between subscriptions and get_profile + + #mqtt_packet{ payload = #'Profile'{ rosters = [#'Roster'{ id = RosterIx }] } } = get_profile(User, 5000), + + RosterId = make_phone_id(PhoneLink, RosterIx), + + User#user{ roster_ix = RosterIx, roster_id = RosterId }. + +register_phone(Phone) -> + register_phone(Phone, true). + +register_phone(Phone, WithRosterId) -> + RegUser = ws_connect(), + {ClientId, Token} = reg(RegUser, Phone), + + User0 = ws_connect(), + connect(User0, {ClientId, Token}), + + User = User0#user{client = ClientId, phone = Phone}, + + RosterIx = + case WithRosterId of + true -> + #mqtt_packet{ payload = #'Profile'{ rosters = Rs } } = get_profile(User), + #'Roster'{ id = RosterIx0 } = hd(Rs), + RosterIx0; + false -> + 1 + end, + + RosterId = iolist_to_binary([Phone, "_", integer_to_list(RosterIx)]), + + User#user{roster_id = RosterId, roster_ix = RosterIx}. + +become_friends(#user{roster_id = RosterId1} = User1, + #user{roster_id = RosterId2} = User2) -> + #mqtt_packet{ payload = {io, _, #'Roster'{}} } = + search_contact(User2, user_phone(User1)), + + send_friend_req(User2, RosterId1), + catch ws_receive(User1, 1, 1000), + + #mqtt_packet{ payload = #'Contact'{ last_msg = #'Message'{ feed_id = FeedId } } } = + friend_req_accept(User1, RosterId2), + ws_receive(User2), %% last_msg + + FeedId. + +room_feed(Room) -> + #muc{ name = typed_uuid(<<"room">>, Room) }. + +feed_id(#user{roster_id = RId1}, #user{ roster_id = RId2 }) -> + list_to_tuple([p2p | lists:sort([RId1, RId2])]). + +send_message(#user{roster_id = RosterId1} = User1, User2, Msg, FeedId) -> + + %% timer:sleep(500), + %% io:format("Sending message from ~s to ~s\n", [ClientId2, ClientId1]), + send_message_(User2, Msg, RosterId1, FeedId), + [#mqtt_packet{ payload = #'Message'{ id = MsgId } }] = ws_receive(User1), + + %% timer:sleep(500), + %% io:format("Client 1 gets messages\n"), + get_messages(User1, FeedId, MsgId), + + %% timer:sleep(500), + %% io:format("Client 1 marks message 2 as read\n"), + read_message(User1, FeedId, MsgId), + ws_receive(User2), %% message read notification + + MsgId. + +create_room(User = #user{ client = ClientId, roster_id = PhoneId }, + Info = #{ name := Name }) -> + RoomId = typed_uuid(<<"room">>, Name), + Owner = make_member(#{ phone_id => PhoneId, room_id => RoomId, status => admin }), + Room = #'Room'{ id = RoomId, name = Name, + members = maps:get(members, Info, []), + admins = [Owner], + type = group, tos = maps:get(desc, Info, []), + status = create }, + + ws_send(User, mqtt_publish(ClientId, Room)). + +join_room(User = #user{ client = ClientId, roster_id = PhoneId }, RoomName, Status) -> + RoomId = typed_uuid(<<"room">>, RoomName), + Member = make_member(#{ phone_id => PhoneId, room_id => RoomId, status => Status }), + Room = #'Room'{ id = RoomId, members = [Member], status = join }, + ws_send(User, mqtt_publish(ClientId, Room)). + +leave_room(User = #user{ client = ClientId, roster_id = PhoneId }, RoomName, Status) -> + RoomId = typed_uuid(<<"room">>, RoomName), + Member = make_member(#{ phone_id => PhoneId, status => Status }), + Room = #'Room'{ id = RoomId, members = [Member], status = leave }, + ws_send(User, mqtt_publish(ClientId, Room)). + +remove_from_room(User = #user{ client = ClientId }, Phone, RoomName, Status) -> + RoomId = typed_uuid(<<"room">>, RoomName), + case get_member_phone_id(User, Phone, RoomName) of + false -> not_in_room; + PhoneId -> + Member = make_member(#{ phone_id => PhoneId, status => Status }), + Room = #'Room'{ id = RoomId, members = [Member], status = remove }, + ws_send(User, mqtt_publish(ClientId, Room)) + end. + +get_member_phone_id(User, Alias, RoomName) -> + #mqtt_packet{ payload = Room } = get_room(User, RoomName), + Members = Room#'Room'.admins ++ Room#'Room'.members, + case lists:keyfind(Alias, #'Member'.alias, Members) of + #'Member'{ phone_id = PhoneId } -> PhoneId; + false -> false + end. + +get_room(User, Room) -> + RoomId = typed_uuid(<<"room">>, Room), + get_room_info(User, RoomId, get). + +get_room_info(User = #user{ client = ClientId }, RoomId, What) -> + ws_send(User, mqtt_publish(ClientId, #'Room'{ id = RoomId, status = What })). + +msg_room(User, Room, Msg) -> + msg_room(User, Room, Msg, false). + +msg_room(User = #user{ client = ClientId }, Room, Msg, Ack) -> + MsgS = make_room_message(User, Room, Msg, Ack), + N = if Ack -> 2; true -> 1 end, + ws_send(User, mqtt_publish(ClientId, MsgS), N, 5000). + +make_room_message(User, RoomName, Msg) -> + make_room_message(User, RoomName, Msg, false). + +make_room_message(User, RoomName, Msg, Ack) -> + RoomId = typed_uuid(<<"room">>, RoomName), + make_message(User, RoomId, #muc{ name = RoomId }, Msg, Ack). + +make_message(#user{ client = ClientId, roster_id = PhoneId }, To, Feed, Msg, Ack) -> + TS = integer_to_binary(now_usec()), + ID = <>, + Feat = #'Feature'{ id = <>, key = <<"SIZE">>, + value = integer_to_binary(byte_size(Msg)), group = <<"FILE_DATA">> }, + AckDesc = [ #'Desc'{ mime = <<"ack">> } || Ack ], + PayloadDesc = [ #'Desc'{ id = ID, mime = <<"text">>, payload = Msg, data = [Feat] } ], + #'Message'{ feed_id = Feed, msg_id = typed_uuid(PhoneId, TS), from = PhoneId, + to = To, files = AckDesc ++ PayloadDesc }. + +make_member(#{ phone_id := PhoneId } = Args) -> + Settings = + case Args of + #{ room_id := RoomId } -> + [{'Feature', <>, <<"DEFAULT_LANGUAGE">>, <<"English:en">>, <<"LANGUAGE_SETTING">>}, + {'Feature', <>, <<"NOTIFICATIONS">>, <<"true">>, <<"GROUP_NOTIFICATIONS">>}]; + _ -> [] + end, + #'Member'{ phone_id = PhoneId, + update = now_msec(), + settings = Settings, + status = maps:get(status, Args, member) }. + +%% Single operations + +do_register_profile(#user{client = ClientId} = User, Profile) -> + ws_send(User, mqtt_publish(ClientId, Profile)). + +get_profile_chunked(#user{client = ClientId} = User, ChunkSize, Chunks, TO) -> + Packets = [Pkt | _] = ws_send(User, mqtt_publish(ClientId, profile_get_chunked(user_phone(User), ChunkSize)), Chunks, TO), + Roster1 = hd(Pkt#mqtt_packet.payload#'Profile'.rosters), + {Us, Rs, Ss} = + lists:unzip3([ {U, R, S} || #mqtt_packet{ payload = Payload } <- Packets, + #'Profile'{ rosters = [#'Roster'{ userlist = U, + roomlist = R, + favorite = S }] } <- [Payload] ]), + U = lists:append(Us), + R = lists:append(Rs), + S = lists:append(Ss), + Pkt#mqtt_packet{ payload = Pkt#mqtt_packet.payload#'Profile'{ rosters = [Roster1#'Roster'{ userlist = U, roomlist = R, favorite = S }] } }. + +get_chunks(_, 0, _, Acc) -> lists:reverse(Acc); +get_chunks(User, Chunks, TO, Acc) -> + [Res] = ws_receive(User, 1, TO), + get_chunks(User, Chunks - 1, TO, [Res | Acc]). + +get_profile(User) -> get_profile(User, 5000). +get_profile(User, TO) -> + get_profile_(User, TO). + +get_profile_(User, TO) -> + get_profile1(User, TO). + +get_profile1(User = #user{client = ClientId}, Timeout) -> + Phone = user_phone(User), + ws_send(User, mqtt_publish(ClientId, profile_get(Phone)), 1, Timeout). + +set_name(#user{client = ClientId, roster_ix = RosterIx} = User, FName, LName) -> + ws_send(User, mqtt_publish(ClientId, roster_patch(RosterIx, FName, LName))). + +set_nick(#user{client = ClientId, roster_ix = RosterIx} = User, NName) -> + ws_send(User, mqtt_publish(ClientId, roster_nick(RosterIx, NName))). + +set_avatar(#user{client = ClientId, roster_ix = RosterIx} = User, Bin) -> + ws_send(User, mqtt_publish(ClientId, roster_avatar(RosterIx, Bin))). + +search_contact(#user{ client = ClientId } = User, Phone) -> + ws_send(User, mqtt_publish(ClientId, search_by_phone(Phone))). + +send_friend_req(#user{ client = ClientId, roster_id = From } = User, To) -> + ws_send(User, mqtt_publish(ClientId, friend_req(From, To))). + +friend_req_accept(#user{ client = ClientId, roster_id = From } = User, To) -> + case ws_send(User, mqtt_publish(ClientId, friend_req_accept_msg(From, To)), 2, 5000) of + [M = #mqtt_packet{ payload = #'Contact'{} }, _] -> M; + [_, M = #mqtt_packet{ payload = #'Contact'{} }] -> M + end. + +send_message_(User, Msg, To, FeedId) -> + send_message_(User, Msg, To, FeedId, false). + +send_message_(User = #user{ client = ClientId }, Msg, To, FeedId, Ack) -> + ws_send(User, mqtt_publish(ClientId, make_message(User, To, FeedId, Msg, Ack))). + +get_messages(User, FeedId, MsgId) -> get_messages(User, FeedId, MsgId, -30). + +get_messages(User = #user{ client = ClientId, roster_id = RId }, FeedId, MsgId, Count) -> + ws_send(User, mqtt_publish(ClientId, get_msg(RId, FeedId, MsgId, Count))). + +read_message(User = #user{ client = ClientId }, FeedId, MsgId) -> + ws_send(User, mqtt_publish(ClientId, read_msg(User, FeedId, MsgId)), 2, 5000). + +mqtt_connect(ClientId, Username, Password) -> + #mqtt_message{qos = WillQos, + retain = WillRetain, + topic = WillTopic, + payload = WillMsg} = #mqtt_message{topic = <<"version/11">>, + payload = <<>>}, + %% if wilol msg and topic is omitted: + %% 020-02-28 14:13:37.047 [info] <0.1150.0>@emqttd_protocol:trace:329 Client(undefined@10.211.55.2:65077): RECV CONNECT(Q0, R0, D0, ClientId=reg_eglhrhgr37wk74lvznu, ProtoName=MQTT, ProtoVsn=4, CleanSess=true, KeepAlive=90, Username=api, Password=******) + %% 2020-02-28 14:13:37.047 [error] <0.1150.0>@emqttd_protocol:process:203 Client(reg_eglhrhgr37wk74lvznu@10.211.55.2:65077): Username 'api' login failed for invalid_version + + Connect = #mqtt_packet_connect{client_id = ClientId, + proto_ver = 4, + proto_name = <<"MQTT">>, + will_flag = true, + will_retain = WillRetain, + will_qos = WillQos, + clean_sess = true, + keep_alive = ?KEEPALIVE, + will_topic = WillTopic, + will_msg = WillMsg, + username = Username, + password = Password}, + ?CONNECT_PACKET(Connect). + +mqtt_pubrec(PackId) -> + ?PUBACK_PACKET(?PUBREC, PackId). + +mqtt_pubcomp(PackId) -> + ?PUBACK_PACKET(?PUBCOMP, PackId). + +mqtt_publish_reg(ClientId, Msg) -> + mqtt_publish(<<"reg_", ClientId/binary>>, Msg). + +mqtt_publish(ClientId, Msg) -> + Node = integer_to_binary(rand:uniform(4)), + Topic = <<"events/1/", Node/binary, "/api/anon/", ClientId/binary, "/">>, + PacketId = undefined, + %% io:format("Publish: ~99999p\n", [Msg]), + Payload = term_to_binary(Msg), + ?PUBLISH_PACKET(?QOS_0, Topic, PacketId, Payload). + +auth_rec(ClientId, Phone) -> + {'Auth', <<"reg_", ClientId/binary>>, ClientId, [], Phone, + [], reg, [], [], [], [], [], [], [], []}. + +auth_verify(ClientId, Phone, Code) -> + {'Auth', [], ClientId, [], Phone, [], + verify, Code, [],[], + [{'Feature',<<"554324f4-f876-4581-ad39-c232db59a798">>, + <<"DEFAULT_LANGUAGE">>,<<"English:en">>, + <<"LANGUAGE_SETTING">>}], + [],[],[],[]}. + +profile_get_chunked(Phone, ChunkSize) -> + Pagination = #'Feature'{ key = <<"size">>, + value = integer_to_binary(ChunkSize), + group = <<"PAGINATION">> }, + #'Profile'{ phone = Phone, settings = [Pagination], status = get }. + +profile_get(Phone) -> + #'Profile'{ phone = Phone, status = get }. + +roster_get(RId) -> + {'Roster', RId, [], [], [], [], [], [], [], [], [], [], [], get}. + +roster_patch(RosterIx, FName, LName) -> + {'Roster', RosterIx, FName, LName, [], [], [], [], [], [], [], [], [], patch}. + +roster_nick(RosterIx, Nick) -> + {'Roster', RosterIx, [], [], [], Nick, [], [], [], [], [], [], [], nick}. + +roster_avatar(RosterIx, Avatar) -> + {'Roster', RosterIx, [], [], [], [], [], [], [], [], [], Avatar, [], patch}. + +search_by_phone(Phone) -> + {'Search', 3, <<"phone">>, <<"phone">>, '==', [Phone], contact}. + +friend_req(From, To) -> + {'Friend', From, To, [], request}. + +friend_req_accept_msg(From, To) -> + {'Friend', From, To, [], confirm}. + +get_msg(RId, FeedId, MsgId, Count) -> + #'History'{ roster_id = RId, feed = FeedId, size = Count, + entity_id = MsgId, status = get }. + +read_msg(#user{ roster_id = RId }, FeedId, MsgId) -> + #'History'{ roster_id = RId, feed = FeedId, entity_id = MsgId, status = update }. + +user_conn(#user{conn = Conn}) -> + Conn. + +user_pid (#user{ client_pid = ClientPid }) -> ClientPid. +user_client(#user{ client = ClientId }) -> ClientId. +user_roster(#user{ roster_id = RosterId }) -> RosterId. + +user_phone_uuid(#user{ phone_uuid = PhoneUUID }) -> PhoneUUID. +user_roster_uuid(#user{ roster_uuid = RosterUUID }) -> RosterUUID. + +%% prioritize phone_uuid +user_phone(#user{phone = undefined, phone_uuid = <<>>}) -> []; +user_phone(#user{phone = Phone, phone_uuid = <<>>}) -> Phone; +user_phone(#user{phone_uuid = Phone}) -> Phone. + +user_client_pid(#user{client_pid = Pid}) -> Pid. + +pick_client_id(Phone) -> + Fresh = iolist_to_binary(io_lib:format("~8.32.0b", [rand:uniform(1 bsl 40) - 1])), + <>. + +reg(User, Phone) -> + ClientId = pick_client_id(Phone), + #mqtt_packet{ variable = #mqtt_packet_connack{} } = + ws_send(User, mqtt_connect(<<"reg_", ClientId/binary>>, <<"api">>, <<>>)), + + Auth = auth_rec(ClientId, Phone), + ws_send(User, mqtt_publish_reg(ClientId, Auth)), + + AuthVerify = auth_verify(ClientId, Phone, ?FAKE_CODE), + #mqtt_packet{payload = Pl} = ws_send(User, mqtt_publish_reg(ClientId, AuthVerify)), + {io, {ok2, login, {UserId, Token}}, <<>>} = Pl, + + ws_close(User), + + {UserId, Token}. + +connect(Connection, {User, Token}) -> + #mqtt_packet{ variable = #mqtt_packet_connack{} } = + ws_send(Connection, mqtt_connect(User, <<"api">>, Token)). + +connect_micro(User, ClientId) -> + #mqtt_packet{ variable = #mqtt_packet_connack{} } = + ws_send(User, mqtt_connect(ClientId, <<"micro">>, <<>>)). + +ws_connect() -> + ws_connect([]). +ws_connect(Hdrs) -> + Root = self(), + Pid = spawn_link(fun() -> + Conn = ws_connect_(Hdrs), + Root ! {self(), Conn}, + ws_loop(Root, Conn, []) + end), + receive + {Pid, Conn} -> #user{conn = Conn, client_pid = Pid} + end. + +ws_loop(Root, Conn = {ConnPid, MRef, StreamRef}, Acks) -> + receive + close -> ws_close(Conn); + {send, Packet} -> + gun_ws_send(Conn, Packet), + ws_loop(Root, Conn, Acks); + {gun_ws, ConnPid, StreamRef, Frame} -> + Msg = decode_frame(Frame), + case Msg of + #mqtt_packet{ header = #mqtt_packet_header{ qos = QoS }, + variable = #mqtt_packet_publish{ packet_id = PackId } } -> + %% io:format("Got msg ~p\n", [PackId]), + Acks1 = + case QoS == ?QOS_2 of + true -> gun_ws_send(Conn, mqtt_pubrec(PackId)), + [PackId | Acks]; + false -> Acks + end, + [ Root ! {self(), Msg} || not ignore_packet(Msg) ], + ws_loop(Root, Conn, Acks1); + #mqtt_packet{ header = #mqtt_packet_header{ type = ?PUBREL }, + variable = #mqtt_packet_puback{ packet_id = PackId } } -> + %% io:format("Acked msg ~p\n", [PackId]), + case lists:member(PackId, Acks) of + false -> exit({unexpected_ack, PackId, Acks}); + true -> + gun_ws_send(Conn, mqtt_pubcomp(PackId)), + ws_loop(Root, Conn, Acks -- [PackId]) + end; + ?PACKET(?PINGRESP) -> + ws_loop(Root, Conn, Acks); + _ -> %% CONNACK etc. + Root ! {self(), Msg}, + ws_loop(Root, Conn, Acks) + end; + {'DOWN', MRef, process, ConnPid, Reason} -> + error_logger:error_msg("Oops!"), + exit(Reason); + Other -> + io:format("Unexpected message: ~p\n", [Other]), + ws_loop(Root, Conn, Acks) + after 240000 -> + gun_ws_send(Conn, ?PACKET(?PINGREQ)), + ws_loop(Root, Conn, Acks) + end. + +ws_connect_(Hdrs) -> + {ok, _} = application:ensure_all_started(gun), + {ok, ConnPid} = gun:open(?HOST, 8083), + MRef = monitor(process, ConnPid), + {ok, http} = gun:await_up(ConnPid), + gun:ws_upgrade(ConnPid, "/mqtt", Hdrs, #{protocols => [{<<"mqtt">>, gun_ws_h}]}), + receive + {gun_upgrade, ConnPid, StreamRef, [<<"websocket">>], _Headers} -> + {ConnPid, MRef, StreamRef}; + + {gun_response, ConnPid, _, _, Status, Headers} -> + exit({ws_upgrade_failed, Status, Headers}); + + {gun_error, ConnPid, _StreamRef, Reason} -> + exit({ws_upgrade_error, Reason}) + after 1000 -> + exit(timeout) + end. + +ws_close(#user{ client_pid = Pid }) when is_pid(Pid) -> + Pid ! close; +ws_close({ConnPid, _, StreamRef}) when is_pid(ConnPid) -> + gun:cancel(ConnPid, StreamRef), + gun:close(ConnPid); +ws_close(_) -> ok. + +ws_receive(Conn) -> + ws_receive(Conn, 1, 5000). + +ws_receive(Conn, Msgs) -> + ws_receive(Conn, Msgs, 5000). + +ws_receive(Conn, Msgs, TO) -> + ws_receive(Conn, [], Msgs, TO). + +ws_receive(#user{ client_pid = Pid }, Msgs, NMsgs, TO) when is_pid(Pid) -> + ws_receive_(Pid, Msgs, NMsgs, TO). + +ignore_packet(#mqtt_packet{ variable = #mqtt_packet_publish{}, payload = Payload }) -> + case Payload of + #'Contact'{status = internal} -> true; + _ -> false + end. + +ws_receive_(_, Msgs, 0, _TO) -> + lists:reverse(Msgs); +ws_receive_(Pid, Msgs, MsgsLeft, TO) -> + receive + {Pid, Msg = #mqtt_packet{}} -> + [ exit({extra_msg, Msg, Msgs}) || MsgsLeft == 0 ], + ws_receive_(Pid, [Msg | Msgs], dec(MsgsLeft), TO) + after TO -> + case MsgsLeft of + any -> + lists:reverse(Msgs); + _ -> + %% io:format("TIMEOUT: ~p\n", [MsgsLeft]), + timeout + end + end. + +dec(any) -> any; +dec(N) -> N - 1. + +gun_ws_send({ConnPid, _, _}, MQTTPacket) -> + ok = gun:ws_send(ConnPid, {binary, emqttc_serialiser:serialise(MQTTPacket)}). + +ws_send_async(#user{ client_pid = Pid }, Packet) when is_pid(Pid) -> + Pid ! {send, Packet}; +ws_send_async(#user{ conn = Conn }, Packet) -> + gun_ws_send(Conn, Packet). + +ws_send(User, Packet) -> + ws_send(User, Packet, 1, 5000). + +ws_send(User, Packet, Replies, TO) -> + ws_send_async(User, Packet), + case ws_receive(User, Replies, TO) of + [Msg] -> Msg; + Msgs -> Msgs + end. + +decode_frame({binary, Bin}) -> + case emqttc_parser:parse(Bin, none) of + {ok, #mqtt_packet{variable = #mqtt_packet_connack{}} = Mqtt, <<>>} -> + Mqtt#mqtt_packet{ payload = <<>> }; + {ok, #mqtt_packet{payload = Payload} = Mqtt, <<>>} -> + Resp = + case Payload of + <<>> -> Mqtt; + undefined -> Mqtt; + _ -> Mqtt#mqtt_packet{payload = binary_to_term(Payload)} + end, + %% io:format("Decoded Frame: ~p\n", [Resp]), + Resp + end. + +now_usec() -> + {A, B, C} = os:timestamp(), + C + 1000000 * (B + 1000000 * A). + +now_msec() -> now_usec() div 1000. +now_sec() -> now_usec() div 1000000. + +new_uuid() -> + {A, B, C} = os:timestamp(), + Bin = <<(integer_to_binary(A))/binary, + (integer_to_binary(B))/binary, + (integer_to_binary(C))/binary>>, + list_to_binary(uuid:uuid_to_string(uuid:get_v3(Bin))). + +phone_uuid(Phone) -> + typed_uuid(<<"phone">>, Phone). + +roster_uuid(Phone) -> + typed_uuid(<<"roster">>, Phone). + +typed_uuid(Type, Value) -> + list_to_binary(uuid:uuid_to_string(uuid:get_v3(Type, Value))). + +make_phone_id(PhoneLink, RosterIx) -> + iolist_to_binary([PhoneLink, "_", integer_to_list(RosterIx)]). + +jwt_token(RosterUUID) -> + Time = now_sec(), + JWTMap = #{ <<"aud">> => <<"MTIz:NynjaWeb:Nynja">>, + <<"exp">> => Time + 3600, + <<"iat">> => Time, + <<"iss">> => <<"https://auth.nynja.biz/">>, + <<"roles">> => [<<"USER">>], + <<"scope">> => <<"access">>, + <<"sub">> => base64:encode(RosterUUID) }, + + {_, Priv} = jose_jwa:generate_key(<<"ES256">>), + + jose_jws_compact:encode(JWTMap, <<"ES256">>, Priv). + + +msgs_split_topic(TopicPrefix, Msgs) -> + lists:partition(fun(#mqtt_packet{ variable = #mqtt_packet_publish{ topic_name = Topic}}) -> + string:prefix(Topic, TopicPrefix) /= nomatch; + (_) ->false + end, Msgs). diff --git a/test/server_SUITE.erl b/test/server_SUITE.erl new file mode 100644 index 0000000000000000000000000000000000000000..7b85d703632ea56a489d7bbed62ba4b63a8af387 --- /dev/null +++ b/test/server_SUITE.erl @@ -0,0 +1,295 @@ +-module(server_SUITE). +-include_lib("stdlib/include/assert.hrl"). + +%% common_test exports +-export([ all/0 + , groups/0 + , init_per_group/2 + , init_per_suite/1 + , end_per_suite/1 + , end_per_group/2 + , init_per_testcase/2 + , end_per_testcase/2 + ]). + +%% Tests +-export([ register_user/1 + , fake_numbers/1 + , sessions/1 + , whitelist/1 + , metrics/1 + , rooms/1 + ]). + +all() -> + [{group, primitives} + ]. + +groups() -> + [ {primitives, [register_user, sessions, fake_numbers, whitelist, metrics, rooms]} + ]. + +init_per_suite(Cfg) -> + case os:getenv("NYNJA_HOST") of + false -> + ct:pal("NYNJA_HOST undefined. Suite requires running nynja server!"), + {skip, {undefined, "NYNJA_HOST"}}; + Host -> + %% Use a random phoen number for testing. Skip tests if number has already been used + Phone = rand:uniform(1000000), + + %% Rebar config has ct_opts point to sys.config such that we + %% have access to config parameters + %% In case one tests against a differently configured node, + %% provide ct_opts with that configuration! + + {ok, Port} = application:get_env(rest, port), + BaseUrl = iolist_to_binary(["http://", Host, ":", integer_to_list(Port), "/"]), + {ok, 200, Response} = request('IsAlive', [], [{base_url, BaseUrl}]), + ct:log("Pinged server resulted in ~p", [Response]), + %% this does not mean that we "Action Process" is passed + [{base_url, BaseUrl}, {phone, iolist_to_binary(integer_to_list(Phone))} | + Cfg] + end. + +end_per_suite(Cfg) -> + Cfg. + +init_per_group(_Grp, Cfg) -> + Cfg. + +end_per_group(_Grp, Cfg) -> + Cfg. + +init_per_testcase(sessions, Cfg) -> + case application:get_env(roster, get_sessions_api, false) of + false -> + {ok, 404, _} = request('GetSessions', #{}, [basic_auth() | Cfg]), + {skip, get_session_api_disabled_in_sys_config}; + true -> + Cfg + end; +init_per_testcase(whitelist, Cfg) -> + case application:get_env(roster, whitelist_check, false) of + false -> + {ok, 404, _} = request('GetWhiteList', #{}, [basic_auth() | Cfg]), + {skip, whitelist_check_disabled_in_sys_config}; + true -> + Cfg + end; +init_per_testcase(_TC, Cfg) -> + Cfg. + +end_per_testcase(_TC, Cfg) -> + Cfg. + +%%% ------------- tests ----------------------- + +register_user(Cfg) -> + Phone = proplists:get_value(phone, Cfg, <<"000000033">>), + ct:log("Phone = ~p", [Phone]), + try User = nynja:register_profile(#{ phone => Phone, fname => <<"Tes">>, + lname => <<"Tester">>, nick => Phone }), + nynja:ws_close(User), + ok + catch + error:{badmatch, {mqtt_packet,_,_, {io,{error,already_exist},_}}} -> + ct:log("Registering failed: user already exists"), + {skip, user_already_registered} + end. + +sessions(Cfg) -> + Phone = proplists:get_value(phone, Cfg, <<"000000034">>), + register_user(Cfg), + %% Some negative testing + {ok, 401, _} = request('GetSessions', #{phone => Phone}, Cfg), + {ok, 400, _} = request('GetSessions', #{phone => Phone}, [basic_auth() | Cfg]), + + LookupPhone = nynja:phone_uuid(Phone), + + nynja:connect_user(Phone), + {ok, 200, Sessions} = request('GetSessions', #{phone => LookupPhone}, [basic_auth() | Cfg]), + ?assertEqual(1, length(Sessions)), + + %% Connect a second device + nynja:connect_user(Phone), + {ok, 200, Sessions2} = request('GetSessions', #{phone => LookupPhone}, [basic_auth() | Cfg]), + ?assertEqual(2, length(Sessions2)), + + {ok, 401, _} = request('GetSessions', #{phone => LookupPhone}, Cfg), + ok. + +fake_numbers(Cfg) -> + {ok, 200, _HTML} = request('GetFakeNumbers', #{}, [basic_auth() | Cfg]), + %% We cannot test the javascript html page: that's a TODO for GUI tester + + %% Make sure internal API uses authentication + {ok, 401, _} = request('fake_numbers', #{}, Cfg), + + %% Put a new fake number in the list + MyFakeNr1 = 123321123, + MyFakeNr2 = 122331122, + {ok, 200, #{'Status' := <<"Success">>}} = request('put_fake_numbers', #{phone => [MyFakeNr1, MyFakeNr2]}, [basic_auth() | Cfg]), + + {ok, 200, #{'Data' := FakeNumbers, + 'Status' := <<"Success">>}} = request('fake_numbers', #{}, [basic_auth() | Cfg]), + StartNrNumbers = length(FakeNumbers), + ?assert(StartNrNumbers > 0), + Nrs = [ Nr || #{phone := Nr} <- FakeNumbers ], + ?assert(lists:member(MyFakeNr1, Nrs)), + Phone = hd(Nrs), + User = nynja:register_phone(list_to_binary(integer_to_list(Phone))), + ct:log("Registerd fake phone ~p\n", [User]), + {ok, 200, _} = request('delete_fake_numbers', #{phone => lists:concat([MyFakeNr1, ",", MyFakeNr2])}, [basic_auth() | Cfg]), + + {ok, 200, #{'Data' := NewFakeNumbers, + 'Status' := <<"Success">>}} = request('fake_numbers', #{}, [basic_auth() | Cfg]), + Nrs2 = [ Nr || #{phone := Nr} <- NewFakeNumbers ], + ?assert(not lists:member(MyFakeNr2, Nrs2)), + ok. + +whitelist(Cfg) -> + {ok, 200, _HTML} = request('GetWhiteList', #{}, [basic_auth() | Cfg]), + %% We cannot test the javascript html page: that's a TODO for GUI tester + + %% Make sure internal API uses authentication + {ok, 401, _} = request('whitelist', #{}, Cfg), + + %% Put a new fake number in the list + MyFakeNr1 = 123321000, + {ok, 200, #{'Status' := <<"Success">>}} = request('put_whitelist', #{phone => [MyFakeNr1]}, [basic_auth() | Cfg]), + + {ok, 200, #{'Data' := FakeNumbers, + 'Status' := <<"Success">>}} = request('whitelist', #{}, [basic_auth() | Cfg]), + StartNrNumbers = length(FakeNumbers), + ?assert(StartNrNumbers > 0), + Nrs = [ Nr || #{phone := Nr} <- FakeNumbers ], + ?assert(lists:member(MyFakeNr1, Nrs)), + Reg = register_user([{phone, list_to_binary(integer_to_list(MyFakeNr1))} | proplists:delete(phone, Cfg)]), + ct:log("Registerd whitelisted phone ~p\n", [Reg]), + {ok, 200, _} = request('delete_whitelist', #{phone => lists:concat([MyFakeNr1])}, [basic_auth() | Cfg]), + + {ok, 200, #{'Data' := NewFakeNumbers, + 'Status' := <<"Success">>}} = request('whitelist', #{}, [basic_auth() | Cfg]), + Nrs2 = [ Nr || #{phone := Nr} <- NewFakeNumbers ], + ?assert(not lists:member(MyFakeNr1, Nrs2)), + ok. + +metrics(Cfg) -> + {Time, {ok, 200, _Text}} = timer:tc(fun() -> request('Metrics', #{}, Cfg) end), + ct:log("Metrics timing: ~p", [Time]), + ok. + +rooms(Cfg) -> + Phone = proplists:get_value(phone, Cfg, <<"000000035">>), + MemberPhone = <<"002003004">>, + + register_user([{phone, Phone}]), %% may fail if already registered + register_user([{phone, MemberPhone}]), + + AdminUser = nynja:connect_user(Phone), + User = nynja:connect_user(MemberPhone), + AdminId = nynja:user_roster(AdminUser), + MemberId = nynja:user_roster(User), + + RoomName = <<"TestRoom-", Phone/binary>>, + nynja:create_room(AdminUser, #{ name => RoomName}), + RoomId = nynja:typed_uuid(<<"room">>, RoomName), + LookupPhone = nynja:phone_uuid(Phone), + + %% Failed to authenticate before get + ct:log("Missing header parameter"), + {ok, 400, _} = request('GetCRIRoom', #{}, [basic_auth() | Cfg]), + + ct:log("Missing query parameter"), + {ok, 400, _} = request('GetCRIRoom', #{phoneid => LookupPhone}, [basic_auth() | Cfg]), + + ct:log("Wrong header parameter"), + {ok, 400, _} = request('GetCRIRoom', #{phoneid => Phone, room_id => RoomId}, [basic_auth() | Cfg]), + + ct:log("No authentication"), + {ok, 401, _} = request('GetCRIRoom', #{phoneid => AdminId, room_id => RoomId}, Cfg), + + {ok, 200, #{data := #{type := Type}}} = + request('GetCRIRoom', #{phoneid => AdminId, room_id => RoomId}, [basic_auth() | Cfg]), + ct:log("Able to get the type of an existing room ~p", [Type]), + + ct:log("Phone ids should be a real id, not phone number"), + {ok, 200, #{data := [#{error := <<"User Not Found">>, + is_member := <<"false">>, + phone_id := Phone}]}} = + request('GetMembersCRIRoom', + #{phoneid => AdminId, room_id => RoomId, phone_ids => Phone}, [basic_auth() | Cfg]), + + ct:log("User is found, but phone ids should contain phone plus roster ix"), + {ok, 200, #{data := [#{is_member := <<"false">>, + phone_id := LookupPhone}]}} = + request('GetMembersCRIRoom', + #{phoneid => AdminId, room_id => RoomId, phone_ids => LookupPhone}, [basic_auth() | Cfg]), + + %% The AdminId is the one we are looking for + {ok, 200, #{data := [#{is_member := <<"true">>, + phone_id := AdminId}]}} = + request('GetMembersCRIRoom', + #{phoneid => AdminId, room_id => RoomId, phone_ids => AdminId}, [basic_auth() | Cfg]), + + + nynja:join_room(User, RoomName, member), + {ok, 200, #{data := [#{is_member := <<"true">>}]}} = + request('GetMembersCRIRoom', + #{phoneid => AdminId, room_id => RoomId, phone_ids => MemberId}, [basic_auth() | Cfg]), + + {ok, 200, #{data := [#{is_member := <<"true">>}]}} = + request('GetMembersCRIRoom', + #{phoneid => MemberId, room_id => RoomId, phone_ids => AdminId}, [basic_auth() | Cfg]), + + {ok, 200, #{data := Members}} = + request('GetMembersCRIRoom', + #{phoneid => LookupPhone, room_id => RoomId, phone_ids => iolist_to_binary([MemberId, ",", AdminId, ",", Phone])}, + [basic_auth() | Cfg]), + ?assertEqual([<<"false">>,<<"true">>,<<"true">>], lists:sort([ B || #{is_member := B} <- Members ])), + + {ok, 403, _} = + request('DeleteMembersCRIRoom', + #{phoneid => MemberId, room_id => RoomId, phone_ids => iolist_to_binary([MemberId, ",", AdminId])}, + [basic_auth() | Cfg]), + + %% Bug in the software + {ok, 200, #{data := _}} = + request('DeleteMembersCRIRoom', + #{phoneid => AdminId, room_id => RoomId, phone_ids => MemberId}, + [basic_auth() | Cfg]), + + {ok, 200, #{data := [#{is_member := <<"false">>}]}} = + request('GetMembersCRIRoom', + #{phoneid => AdminId, room_id => RoomId, phone_ids => MemberId}, [basic_auth() | Cfg]), + + + ok. + + + + + +%%% ------------- helpers --------------------- + +request(Id, Params, Config) -> + Request = api_nynja:request(Id, Params, Config), + ct:pal("Outgoing Request ~p", [Request]), + + {ok, Status, Headers, Body} = + api_nynja:http_request(Request, [], [], + [{logfun, fun(F, As) -> + ct:log(F, As) + end}]), + Response = api_nynja:validate_response(Id, Status, Headers, Body), + [ ct:log("Validated ~p\n", [Response]) || true], + Response. + +basic_auth() -> + %% Get the User and Password from sys.config + {ok, BasicAuth} = application:get_env(rest, basic_auth), + User = proplists:get_value(username, BasicAuth), + Password = proplists:get_value(password, BasicAuth), + {security, + {authorization_basic, [#{username => User, password => Password}]}}. diff --git a/vm.args.src b/vm.args.src new file mode 100644 index 0000000000000000000000000000000000000000..5120fac556991df9c3be91db14e4435d8a64655f --- /dev/null +++ b/vm.args.src @@ -0,0 +1,13 @@ ++W w +-kernel net_ticktime 60 +-env ERL_CRASH_DUMP log/crash.dump +-mnesia dc_dump_limit 10 ++Q 524288 ++A 32 ++K true +-smp auto +-setcookie emq_dist_cookie +-name ${NODE_NAME} ++zdbbl 32768 + +