From 975fad06d06d87ac190770a9f0170bdf8edf0a4c Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Tue, 18 Aug 2020 19:37:16 +0300 Subject: [PATCH 01/32] WIP --- example/mix.exs | 3 +- lib/mix/tasks/thrift.generate.ex | 1 - lib/thrift/binary/framed/server/http.ex | 3 + lib/thrift/generator.ex | 2 +- .../generator/binary/framed/server/http.ex | 81 +++++++++++++++++++ lib/thrift/generator/service.ex | 5 ++ mix.exs | 3 +- mix.lock | 8 ++ 8 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 lib/thrift/binary/framed/server/http.ex create mode 100644 lib/thrift/generator/binary/framed/server/http.ex diff --git a/example/mix.exs b/example/mix.exs index afd72472..457e2256 100644 --- a/example/mix.exs +++ b/example/mix.exs @@ -31,7 +31,8 @@ defmodule Calculator.MixProject do [ # Note: you will want to replace the next line to get elixir-thrift from either # Hex or GitHub in your own project. - {:thrift, path: ".."} + {:thrift, path: ".."}, + {:httpoison, "~> 1.7.0"} ] end end diff --git a/lib/mix/tasks/thrift.generate.ex b/lib/mix/tasks/thrift.generate.ex index b3d20305..d13efc02 100644 --- a/lib/mix/tasks/thrift.generate.ex +++ b/lib/mix/tasks/thrift.generate.ex @@ -71,7 +71,6 @@ defmodule Mix.Tasks.Thrift.Generate do Keyword.new() |> Keyword.put(:include_paths, include_paths) |> Keyword.put(:namespace, namespace) - unless Enum.empty?(files) do File.mkdir_p!(output_path) Enum.each(files, &generate!(&1, output_path, parser_opts, opts)) diff --git a/lib/thrift/binary/framed/server/http.ex b/lib/thrift/binary/framed/server/http.ex new file mode 100644 index 00000000..1f072a34 --- /dev/null +++ b/lib/thrift/binary/framed/server/http.ex @@ -0,0 +1,3 @@ +defmodule Thrift.Binary.Framed.Server.HTTP do + +end diff --git a/lib/thrift/generator.ex b/lib/thrift/generator.ex index 94e6fd31..f4f39d35 100644 --- a/lib/thrift/generator.ex +++ b/lib/thrift/generator.ex @@ -199,7 +199,7 @@ defmodule Thrift.Generator do defp generate_services(schema) do for {_, service} <- schema.services do - Generator.Service.generate(schema, service) + Generator.Service.generate(schema, service) end end diff --git a/lib/thrift/generator/binary/framed/server/http.ex b/lib/thrift/generator/binary/framed/server/http.ex new file mode 100644 index 00000000..07f2247d --- /dev/null +++ b/lib/thrift/generator/binary/framed/server/http.ex @@ -0,0 +1,81 @@ +defmodule Thrift.Generator.Binary.Framed.Server.HTTP do + def generate(dest_module, service, file_group) do + + thrift_root = generate_thrift_root(service) + + quote do + defmodule Binary.Framed.Server.HTTP do + + unquote(thrift_root) + + def get_child_spec do + {Plug.Cowboy, scheme: :http, plug: __MODULE__.Router, options: [port: 4001]} + end + end + end + end + + def generate_thrift_root(service) do + quote do + defmodule Router do + use Plug.Router + + alias Thrift.Protocol + + plug :match + plug :dispatch + + post "/thrift" do + # send_resp(conn, 200, "keke") + with( + {:ok, payload, conn} <- Plug.Conn.read_body(conn), + {:ok, parsed} <- Protocol.Binary.deserialize(:message_begin, payload) |> IO.inspect() + ) do + handle_thrift_message(conn, parsed) + end + end + + match _ do + send_resp(conn, 404, "not found") + end + + def handle_thrift_message(conn, {:call, sequence_id, name, args_binary}) do + IO.inspect(name, label: "FORWARD") + conn = %{conn | body_params: args_binary, path_info: [name]} + Plug.forward(conn, [name], __MODULE__.Handlers, []) + end + + unquote(generate_thrift_handlers(service)) + + end + end + end + + def generate_thrift_handlers(service) do + quote do + defmodule Handlers do + use Plug.Router + + plug :match + plug :dispatch + + unquote_splicing(Enum.map(service.functions, &generate_thrift_handle/1)) + + match _ do + IO.inspect(conn, label: "Not matched") + send_resp(conn, 404, "Ohh") + end + end + end + end + + def generate_thrift_handle({fname, _function_ast}) do + func_name = "/" <> Atom.to_string(fname) + quote do + post unquote(func_name) do + send_resp(conn, 200, unquote(func_name) <> "GOTCHA") + end + end + + end +end diff --git a/lib/thrift/generator/service.ex b/lib/thrift/generator/service.ex index 3d5acbca..5eff76f3 100644 --- a/lib/thrift/generator/service.ex +++ b/lib/thrift/generator/service.ex @@ -12,6 +12,8 @@ defmodule Thrift.Generator.Service do @dialyzer [{:nowarn_function, generate: 2}, {:nowarn_function, generate_response_struct: 2}] def generate(schema, service) do + # IO.inspect(schema, label: "SCHEMA") + # IO.inspect(service, label: "SERVICE") file_group = schema.file_group dest_module = FileGroup.dest_module(file_group, service) @@ -25,6 +27,7 @@ defmodule Thrift.Generator.Service do framed_client = Generator.Binary.Framed.Client.generate(service) framed_server = Generator.Binary.Framed.Server.generate(dest_module, service, file_group) + http_server = Generator.Binary.Framed.Server.HTTP.generate(dest_module, service, file_group) service_module = quote do @@ -36,6 +39,8 @@ defmodule Thrift.Generator.Service do unquote(framed_client) unquote(framed_server) + + unquote(http_server) end end diff --git a/mix.exs b/mix.exs index 5edb3e24..c9a4cd5d 100644 --- a/mix.exs +++ b/mix.exs @@ -85,7 +85,8 @@ defmodule Thrift.Mixfile do # Runtime {:connection, "~> 1.0"}, - {:ranch, "~> 1.6"} + {:ranch, "~> 1.6"}, + {:plug_cowboy, "~> 2.0"} ] end diff --git a/mix.lock b/mix.lock index adb4eb46..a85a5968 100644 --- a/mix.lock +++ b/mix.lock @@ -2,6 +2,8 @@ "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, "certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, + "cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"}, + "cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"}, "credo": {:hex, :credo, "1.4.0", "92339d4cbadd1e88b5ee43d427b639b68a11071b6f73854e33638e30a0ea11f5", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1fd3b70dce216574ce3c18bdf510b57e7c4c85c2ec9cad4bff854abaf7e58658"}, "dialyxir": {:hex, :dialyxir, "1.0.0", "6a1fa629f7881a9f5aaf3a78f094b2a51a0357c843871b8bc98824e7342d00a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "aeb06588145fac14ca08d8061a142d52753dbc2cf7f0d00fc1013f53f8654654"}, "earmark": {:hex, :earmark, "1.4.4", "4821b8d05cda507189d51f2caeef370cf1e18ca5d7dfb7d31e9cafe6688106a4", [:mix], [], "hexpm", "1f93aba7340574847c0f609da787f0d79efcab51b044bb6e242cae5aca9d264d"}, @@ -10,15 +12,21 @@ "ex_doc": {:hex, :ex_doc, "0.22.2", "03a2a58bdd2ba0d83d004507c4ee113b9c521956938298eba16e55cc4aba4a6c", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "cf60e1b3e2efe317095b6bb79651f83a2c1b3edcb4d319c421d7fcda8b3aff26"}, "excoveralls": {:hex, :excoveralls, "0.13.1", "b9f1697f7c9e0cfe15d1a1d737fb169c398803ffcbc57e672aa007e9fd42864c", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "b4bb550e045def1b4d531a37fb766cbbe1307f7628bf8f0414168b3f52021cce"}, "hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"}, + "httpoison": {:hex, :httpoison, "1.7.0", "abba7d086233c2d8574726227b6c2c4f6e53c4deae7fe5f6de531162ce9929a0", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "975cc87c845a103d3d1ea1ccfd68a2700c211a434d8428b10c323dc95dc5b980"}, "idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"}, "jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"}, "makeup": {:hex, :makeup, "1.0.3", "e339e2f766d12e7260e6672dd4047405963c5ec99661abdc432e6ec67d29ef95", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "2e9b4996d11832947731f7608fed7ad2f9443011b3b479ae288011265cdd3dad"}, "makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, + "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, + "plug": {:hex, :plug, "1.10.4", "41eba7d1a2d671faaf531fa867645bd5a3dce0957d8e2a3f398ccff7d2ef017f", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad1e233fe73d2eec56616568d260777b67f53148a999dc2d048f4eb9778fe4a0"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.3.0", "149a50e05cb73c12aad6506a371cd75750c0b19a32f81866e1a323dda9e0e99d", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bc595a1870cef13f9c1e03df56d96804db7f702175e4ccacdb8fc75c02a7b97e"}, + "plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, + "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"}, } From f27083b7c4897d5b7810052ef6cd1b58b898c614 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Wed, 19 Aug 2020 20:09:14 +0300 Subject: [PATCH 02/32] WIP --- .../generator/binary/framed/server/http.ex | 71 ++++++++++++++----- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/lib/thrift/generator/binary/framed/server/http.ex b/lib/thrift/generator/binary/framed/server/http.ex index 07f2247d..9b5f92d2 100644 --- a/lib/thrift/generator/binary/framed/server/http.ex +++ b/lib/thrift/generator/binary/framed/server/http.ex @@ -1,21 +1,24 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do - def generate(dest_module, service, file_group) do + alias Thrift.Generator.{ + Service, + } + def generate(service_module, service, file_group) do - thrift_root = generate_thrift_root(service) + thrift_root = generate_thrift_root(service_module, service) quote do defmodule Binary.Framed.Server.HTTP do unquote(thrift_root) - def get_child_spec do - {Plug.Cowboy, scheme: :http, plug: __MODULE__.Router, options: [port: 4001]} + def get_child_spec(module_handler) do + {Plug.Cowboy, scheme: :http, plug: {__MODULE__.Router, [module_handler]}, options: [port: 4001]} end end end end - def generate_thrift_root(service) do + def generate_thrift_root(service_module, service) do quote do defmodule Router do use Plug.Router @@ -23,7 +26,15 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do alias Thrift.Protocol plug :match - plug :dispatch + plug :dispatch, builder_opts() + + # def init([module_handler]) do + # module_handler + # end + + def init(opts) do + IO.inspect(opts, label: "INIT OPTS") + end post "/thrift" do # send_resp(conn, 200, "keke") @@ -31,7 +42,8 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do {:ok, payload, conn} <- Plug.Conn.read_body(conn), {:ok, parsed} <- Protocol.Binary.deserialize(:message_begin, payload) |> IO.inspect() ) do - handle_thrift_message(conn, parsed) + IO.inspect(opts, label: "THRIFT OPTS") + handle_thrift_message(conn, parsed, opts) end end @@ -39,19 +51,20 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do send_resp(conn, 404, "not found") end - def handle_thrift_message(conn, {:call, sequence_id, name, args_binary}) do + def handle_thrift_message(conn, {:call, sequence_id, name, args_binary}, opts) do IO.inspect(name, label: "FORWARD") + IO.inspect(opts, label: "FORWARD opts") conn = %{conn | body_params: args_binary, path_info: [name]} - Plug.forward(conn, [name], __MODULE__.Handlers, []) + Plug.forward(conn, [name], __MODULE__.Handlers, opts) end - unquote(generate_thrift_handlers(service)) + unquote(generate_thrift_handlers(service_module, service)) end end end - def generate_thrift_handlers(service) do + def generate_thrift_handlers(service_module, service) do quote do defmodule Handlers do use Plug.Router @@ -59,7 +72,11 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do plug :match plug :dispatch - unquote_splicing(Enum.map(service.functions, &generate_thrift_handle/1)) + def init([opts]) do + opts + end + + unquote_splicing(Enum.map(service.functions, &generate_thrift_handle(service_module, &1))) match _ do IO.inspect(conn, label: "Not matched") @@ -69,11 +86,33 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end end - def generate_thrift_handle({fname, _function_ast}) do - func_name = "/" <> Atom.to_string(fname) + def generate_thrift_handle(service_module, {fname, function_ast}) do + func_name = Atom.to_string(fname) + func_path = "/" <> func_name + + handle = + func_name + |> Macro.underscore() + |> String.to_atom() + handler_args = Enum.map(function_ast.params, &Macro.var(&1.name, nil)) + args_module = Module.concat(service_module, Service.module_name(function_ast, :args)) + + struct_matches = + Enum.map(function_ast.params, fn param -> + {param.name, Macro.var(param.name, nil)} + end) quote do - post unquote(func_name) do - send_resp(conn, 200, unquote(func_name) <> "GOTCHA") + post unquote(func_path) do + with( + {:ok, body, conn} <- Plug.Conn.read_body(conn), + { + %unquote(args_module){unquote_splicing(struct_matches)}, # %Service.AddArgs{left: left, right: right} + rest + } <- unquote(args_module).BinaryProtocol.deserialize(body) + ) do + + end + send_resp(conn, 200, unquote(fname) <> "GOTCHA" <> inspect(opts)) end end From 9b1bc932406e51c92c9400fe7235bf1a27d25e6c Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Thu, 20 Aug 2020 17:47:30 +0300 Subject: [PATCH 03/32] WIP http test passed --- .../generator/binary/framed/server/http.ex | 61 +++++++++++++------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/lib/thrift/generator/binary/framed/server/http.ex b/lib/thrift/generator/binary/framed/server/http.ex index 9b5f92d2..c8130b3a 100644 --- a/lib/thrift/generator/binary/framed/server/http.ex +++ b/lib/thrift/generator/binary/framed/server/http.ex @@ -1,18 +1,18 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do alias Thrift.Generator.{ - Service, + Service } - def generate(service_module, service, file_group) do + def generate(service_module, service, file_group) do thrift_root = generate_thrift_root(service_module, service) quote do defmodule Binary.Framed.Server.HTTP do - unquote(thrift_root) def get_child_spec(module_handler) do - {Plug.Cowboy, scheme: :http, plug: {__MODULE__.Router, [module_handler]}, options: [port: 4001]} + {Plug.Cowboy, + scheme: :http, plug: {__MODULE__.Router, [module_handler]}, options: [port: 4001]} end end end @@ -25,8 +25,8 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do alias Thrift.Protocol - plug :match - plug :dispatch, builder_opts() + plug(:match) + plug(:dispatch, builder_opts()) # def init([module_handler]) do # module_handler @@ -52,6 +52,7 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end def handle_thrift_message(conn, {:call, sequence_id, name, args_binary}, opts) do + IO.inspect(args_binary, label: "args binary") IO.inspect(name, label: "FORWARD") IO.inspect(opts, label: "FORWARD opts") conn = %{conn | body_params: args_binary, path_info: [name]} @@ -59,7 +60,6 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end unquote(generate_thrift_handlers(service_module, service)) - end end end @@ -69,8 +69,8 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do defmodule Handlers do use Plug.Router - plug :match - plug :dispatch + plug(:match) + plug(:dispatch, builder_opts()) def init([opts]) do opts @@ -94,27 +94,48 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do func_name |> Macro.underscore() |> String.to_atom() + handler_args = Enum.map(function_ast.params, &Macro.var(&1.name, nil)) args_module = Module.concat(service_module, Service.module_name(function_ast, :args)) + response_module = Module.concat(service_module, Service.module_name(function_ast, :response)) + # |> IO.inspect(label: "body"), struct_matches = Enum.map(function_ast.params, fn param -> {param.name, Macro.var(param.name, nil)} end) + quote do post unquote(func_path) do - with( - {:ok, body, conn} <- Plug.Conn.read_body(conn), - { - %unquote(args_module){unquote_splicing(struct_matches)}, # %Service.AddArgs{left: left, right: right} - rest - } <- unquote(args_module).BinaryProtocol.deserialize(body) - ) do - - end - send_resp(conn, 200, unquote(fname) <> "GOTCHA" <> inspect(opts)) + [handler_module] = opts + IO.inspect(handler_module, label: "End of opts") + body = conn.body_params + + result = + with( + # {:ok, body, conn} <- Plug.Conn.read_body(conn), + IO.inspect(body, label: "body"), + kk = Protocol.Binary.deserialize(:message_begin, body) |> IO.inspect(label: "msgbgn"), + { + # %Service.AddArgs{left: left, right: right} + %unquote(args_module){unquote_splicing(struct_matches)}, + rest + } <- + unquote(args_module).BinaryProtocol.deserialize(body) |> IO.inspect(label: "deser") + ) do + apply( + handler_module, + unquote(handle), + IO.inspect([unquote_splicing(handler_args)], label: "Args") + ) + end + + IO.inspect(result) + response = %unquote(response_module){success: result} + IO.inspect(response) + encoded_result = unquote(response_module).BinaryProtocol.serialize(response) + send_resp(conn, 200, encoded_result) end end - end end From 5c067562bc5bac6dd7b49c7d1156ada058600ce8 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Fri, 21 Aug 2020 08:55:07 +0300 Subject: [PATCH 04/32] WIP --- lib/thrift/generator/binary/framed/server/http.ex | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/thrift/generator/binary/framed/server/http.ex b/lib/thrift/generator/binary/framed/server/http.ex index c8130b3a..c71dcebf 100644 --- a/lib/thrift/generator/binary/framed/server/http.ex +++ b/lib/thrift/generator/binary/framed/server/http.ex @@ -47,6 +47,12 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end end + post "/thrift/:method" do + {:ok, payload, conn} = Plug.Conn.read_body(conn) + conn = %{conn | body_params: payload} + Plug.forward(conn, [method], __MODULE__.Handlers, opts) + end + match _ do send_resp(conn, 404, "not found") end @@ -113,9 +119,6 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do result = with( - # {:ok, body, conn} <- Plug.Conn.read_body(conn), - IO.inspect(body, label: "body"), - kk = Protocol.Binary.deserialize(:message_begin, body) |> IO.inspect(label: "msgbgn"), { # %Service.AddArgs{left: left, right: right} %unquote(args_module){unquote_splicing(struct_matches)}, @@ -130,6 +133,10 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do ) end + case result do + {:ok, result} -> nil + end + IO.inspect(result) response = %unquote(response_module){success: result} IO.inspect(response) From 9670db57fa48a1eeb186a5cd2f826c54aac918a9 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Fri, 21 Aug 2020 20:20:18 +0300 Subject: [PATCH 05/32] error handling WIP --- .../generator/binary/framed/server/http.ex | 83 ++++++++++++++----- 1 file changed, 64 insertions(+), 19 deletions(-) diff --git a/lib/thrift/generator/binary/framed/server/http.ex b/lib/thrift/generator/binary/framed/server/http.ex index c71dcebf..9403a84d 100644 --- a/lib/thrift/generator/binary/framed/server/http.ex +++ b/lib/thrift/generator/binary/framed/server/http.ex @@ -2,9 +2,10 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do alias Thrift.Generator.{ Service } + alias Thrift.Parser.FileGroup def generate(service_module, service, file_group) do - thrift_root = generate_thrift_root(service_module, service) + thrift_root = generate_thrift_root(service_module, service, file_group) quote do defmodule Binary.Framed.Server.HTTP do @@ -18,7 +19,7 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end end - def generate_thrift_root(service_module, service) do + def generate_thrift_root(service_module, service, file_group) do quote do defmodule Router do use Plug.Router @@ -65,12 +66,12 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do Plug.forward(conn, [name], __MODULE__.Handlers, opts) end - unquote(generate_thrift_handlers(service_module, service)) + unquote(generate_thrift_handlers(service_module, service, file_group)) end end end - def generate_thrift_handlers(service_module, service) do + def generate_thrift_handlers(service_module, service, file_group) do quote do defmodule Handlers do use Plug.Router @@ -82,7 +83,7 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do opts end - unquote_splicing(Enum.map(service.functions, &generate_thrift_handle(service_module, &1))) + unquote_splicing(Enum.map(service.functions, &generate_thrift_handle(service_module, file_group, &1))) match _ do IO.inspect(conn, label: "Not matched") @@ -92,7 +93,7 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end end - def generate_thrift_handle(service_module, {fname, function_ast}) do + def generate_thrift_handle(service_module, file_group, {fname, function_ast}) do func_name = Atom.to_string(fname) func_path = "/" <> func_name @@ -105,7 +106,38 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do args_module = Module.concat(service_module, Service.module_name(function_ast, :args)) response_module = Module.concat(service_module, Service.module_name(function_ast, :response)) - # |> IO.inspect(label: "body"), + # exception_module_root = + # service_module # Calculator.Generated.Service + # |> Module.split() + # |> Enum.take_while(&(&1 != 'Service')) + + + # DANGER + # expanded into (clasue -> block) which broke with-else statement + # need to fix on Elixir side + # + # generate_exception_clause = + # fn exception_ast -> + # exception_type_ast = FileGroup.resolve(file_group, exception_ast) |> IO.inspect(label: "RESOLVED") + # exception_module = FileGroup.dest_module(file_group, exception_type_ast.type) |> IO.inspect(label: "module") + # # exception_var = Macro.var(exception_ast.name, nil) + # # field_setter = quote do: {unquote(exception_ast.name), unquote(exception_var)} + # field_setter = quote do: {unquote(exception_ast.name), exc} + # [quote do + # {:exception, %unquote(exception_module){} = exc} -> + # response = %unquote(response_module){unquote(field_setter)} + # unquote(response_module).BinaryProtocol.serialize(response) + # end] + # end + # + # exceptions_clauses = Enum.flat_map( + # function_ast.exceptions, + # generate_exception_clause + # ) + # + # END OF DANGER + # # # # # # + struct_matches = Enum.map(function_ast.params, fn param -> {param.name, Macro.var(param.name, nil)} @@ -117,32 +149,45 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do IO.inspect(handler_module, label: "End of opts") body = conn.body_params - result = + encoded_response = with( { # %Service.AddArgs{left: left, right: right} %unquote(args_module){unquote_splicing(struct_matches)}, rest } <- - unquote(args_module).BinaryProtocol.deserialize(body) |> IO.inspect(label: "deser") - ) do - apply( + unquote(args_module).BinaryProtocol.deserialize(body) |> IO.inspect(label: "deser"), + {:ok, result} <- apply( handler_module, unquote(handle), IO.inspect([unquote_splicing(handler_args)], label: "Args") - ) + ), + response = %unquote(response_module){success: result} + ) do + unquote(response_module).BinaryProtocol.serialize(response) + else + :error -> :error + {:exception, exc} -> handle_exc(exc, unquote(response_module)) + # unquote_splicing(exceptions_clauses) end - case result do - {:ok, result} -> nil + case encoded_response do + :error -> send_resp(conn, 500, "LOL") + encoded_response -> send_resp(conn, 200, encoded_response) end - IO.inspect(result) - response = %unquote(response_module){success: result} - IO.inspect(response) - encoded_result = unquote(response_module).BinaryProtocol.serialize(response) - send_resp(conn, 200, encoded_result) end end end + + def generate_exception_handlers(service_ast, file_group) do + + quote do + defp handle_exc(exception, response_module) do + response = %unquote(response_module){unquote(field_setter)} + unquote(response_module).BinaryProtocol.serialize(response) + end + end + + end end From 62628c6515cdc8c2e385b6a2cba3dd061a0ddc39 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Mon, 24 Aug 2020 14:48:50 +0300 Subject: [PATCH 06/32] exceptions --- .../generator/binary/framed/server/http.ex | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/thrift/generator/binary/framed/server/http.ex b/lib/thrift/generator/binary/framed/server/http.ex index 9403a84d..cee28108 100644 --- a/lib/thrift/generator/binary/framed/server/http.ex +++ b/lib/thrift/generator/binary/framed/server/http.ex @@ -71,7 +71,7 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end end - def generate_thrift_handlers(service_module, service, file_group) do + def generate_thrift_handlers(service_module, service_ast, file_group) do quote do defmodule Handlers do use Plug.Router @@ -83,12 +83,15 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do opts end - unquote_splicing(Enum.map(service.functions, &generate_thrift_handle(service_module, file_group, &1))) + unquote_splicing(Enum.map(service_ast.functions, &generate_thrift_handle(service_module, file_group, &1))) match _ do IO.inspect(conn, label: "Not matched") send_resp(conn, 404, "Ohh") end + + unquote_splicing(generate_exception_handlers(service_ast, service_module, file_group)) + end end end @@ -180,10 +183,22 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end end - def generate_exception_handlers(service_ast, file_group) do + def generate_exception_handlers(service_ast, service_module, file_group) do + for {_fname, function_ast} <- service_ast.functions, exception_ast <- function_ast.exceptions do + response_module = Module.concat(service_module, Service.module_name(function_ast, :response)) + generate_exception_handler(response_module, exception_ast, file_group) + end + end + + def generate_exception_handler(response_module, exception_ast, file_group) do + exception_type_ast = FileGroup.resolve(file_group, exception_ast) |> IO.inspect(label: "RESOLVED") + exception_module = FileGroup.dest_module(file_group, exception_type_ast.type) |> IO.inspect(label: "module") + # exception_var = Macro.var(exception_ast.name, nil) + # field_setter = quote do: {unquote(exception_ast.name), unquote(exception_var)} + field_setter = quote do: {unquote(exception_ast.name), exc} quote do - defp handle_exc(exception, response_module) do + defp handle_exc(%unquote(exception_module){} = exc, unquote(response_module)) do response = %unquote(response_module){unquote(field_setter)} unquote(response_module).BinaryProtocol.serialize(response) end From 96d17118611b0b286219668d4028585d6517d253 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Mon, 24 Aug 2020 15:44:29 +0300 Subject: [PATCH 07/32] More verbose behaviour spec --- lib/thrift/generator/behaviour.ex | 26 +++++++++++++++++++ .../generator/binary/framed/server/http.ex | 2 -- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/thrift/generator/behaviour.ex b/lib/thrift/generator/behaviour.ex index 119eb853..da93aef6 100644 --- a/lib/thrift/generator/behaviour.ex +++ b/lib/thrift/generator/behaviour.ex @@ -42,6 +42,14 @@ defmodule Thrift.Generator.Behaviour do callback_name = Utils.underscore(function.name) return_type = typespec(function.return_type, file_group) + return_type = ok_type(return_type) + + exceptions = Enum.map( + function.exceptions, + &(&1.type |> typespec(file_group) |> exception_type()) + ) + + return_type = Enum.reduce([return_type | exceptions], &unite/2) params = function.params @@ -133,4 +141,22 @@ defmodule Thrift.Generator.Behaviour do any end end + + defp ok_type(typespec_) do + quote do + {:ok, unquote(typespec_)} + end + end + + defp exception_type(typespec_) do + quote do + {:exception, unquote(typespec_)} + end + end + + defp unite(type, acc) do + quote do + unquote(acc) | unquote(type) + end + end end diff --git a/lib/thrift/generator/binary/framed/server/http.ex b/lib/thrift/generator/binary/framed/server/http.ex index cee28108..0787eba1 100644 --- a/lib/thrift/generator/binary/framed/server/http.ex +++ b/lib/thrift/generator/binary/framed/server/http.ex @@ -193,8 +193,6 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do def generate_exception_handler(response_module, exception_ast, file_group) do exception_type_ast = FileGroup.resolve(file_group, exception_ast) |> IO.inspect(label: "RESOLVED") exception_module = FileGroup.dest_module(file_group, exception_type_ast.type) |> IO.inspect(label: "module") - # exception_var = Macro.var(exception_ast.name, nil) - # field_setter = quote do: {unquote(exception_ast.name), unquote(exception_var)} field_setter = quote do: {unquote(exception_ast.name), exc} quote do From e72023df9692ecf89100ff29b585b6cab2142154 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Mon, 24 Aug 2020 19:12:17 +0300 Subject: [PATCH 08/32] cleanup --- .../generator/binary/framed/server/http.ex | 33 +++++-------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/lib/thrift/generator/binary/framed/server/http.ex b/lib/thrift/generator/binary/framed/server/http.ex index 0787eba1..10f62cd8 100644 --- a/lib/thrift/generator/binary/framed/server/http.ex +++ b/lib/thrift/generator/binary/framed/server/http.ex @@ -29,21 +29,15 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do plug(:match) plug(:dispatch, builder_opts()) - # def init([module_handler]) do - # module_handler - # end - def init(opts) do - IO.inspect(opts, label: "INIT OPTS") + opts end post "/thrift" do - # send_resp(conn, 200, "keke") with( {:ok, payload, conn} <- Plug.Conn.read_body(conn), - {:ok, parsed} <- Protocol.Binary.deserialize(:message_begin, payload) |> IO.inspect() + {:ok, parsed} <- Protocol.Binary.deserialize(:message_begin, payload) ) do - IO.inspect(opts, label: "THRIFT OPTS") handle_thrift_message(conn, parsed, opts) end end @@ -59,9 +53,6 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end def handle_thrift_message(conn, {:call, sequence_id, name, args_binary}, opts) do - IO.inspect(args_binary, label: "args binary") - IO.inspect(name, label: "FORWARD") - IO.inspect(opts, label: "FORWARD opts") conn = %{conn | body_params: args_binary, path_info: [name]} Plug.forward(conn, [name], __MODULE__.Handlers, opts) end @@ -86,7 +77,6 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do unquote_splicing(Enum.map(service_ast.functions, &generate_thrift_handle(service_module, file_group, &1))) match _ do - IO.inspect(conn, label: "Not matched") send_resp(conn, 404, "Ohh") end @@ -96,7 +86,7 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end end - def generate_thrift_handle(service_module, file_group, {fname, function_ast}) do + def generate_thrift_handle(service_module, _file_group, {fname, function_ast}) do func_name = Atom.to_string(fname) func_path = "/" <> func_name @@ -109,12 +99,6 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do args_module = Module.concat(service_module, Service.module_name(function_ast, :args)) response_module = Module.concat(service_module, Service.module_name(function_ast, :response)) - # exception_module_root = - # service_module # Calculator.Generated.Service - # |> Module.split() - # |> Enum.take_while(&(&1 != 'Service')) - - # DANGER # expanded into (clasue -> block) which broke with-else statement # need to fix on Elixir side @@ -149,7 +133,6 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do quote do post unquote(func_path) do [handler_module] = opts - IO.inspect(handler_module, label: "End of opts") body = conn.body_params encoded_response = @@ -157,13 +140,13 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do { # %Service.AddArgs{left: left, right: right} %unquote(args_module){unquote_splicing(struct_matches)}, - rest + _rest } <- - unquote(args_module).BinaryProtocol.deserialize(body) |> IO.inspect(label: "deser"), + unquote(args_module).BinaryProtocol.deserialize(body), {:ok, result} <- apply( handler_module, unquote(handle), - IO.inspect([unquote_splicing(handler_args)], label: "Args") + [unquote_splicing(handler_args)] ), response = %unquote(response_module){success: result} ) do @@ -191,8 +174,8 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end def generate_exception_handler(response_module, exception_ast, file_group) do - exception_type_ast = FileGroup.resolve(file_group, exception_ast) |> IO.inspect(label: "RESOLVED") - exception_module = FileGroup.dest_module(file_group, exception_type_ast.type) |> IO.inspect(label: "module") + exception_type_ast = FileGroup.resolve(file_group, exception_ast) + exception_module = FileGroup.dest_module(file_group, exception_type_ast.type) field_setter = quote do: {unquote(exception_ast.name), exc} quote do From 65cfb8c668fae6ebd2ca3100e73b9048aaf6c763 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Tue, 25 Aug 2020 14:36:40 +0300 Subject: [PATCH 09/32] Correct typespec for void call --- lib/thrift/generator/behaviour.ex | 3 ++- lib/thrift/generator/binary/framed/server/http.ex | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/thrift/generator/behaviour.ex b/lib/thrift/generator/behaviour.ex index da93aef6..bfb641ba 100644 --- a/lib/thrift/generator/behaviour.ex +++ b/lib/thrift/generator/behaviour.ex @@ -67,7 +67,8 @@ defmodule Thrift.Generator.Behaviour do end end - defp typespec(:void, _), do: quote(do: no_return()) + # defp typespec(:void, _), do: quote(do: no_return()) + defp typespec(:void, _), do: quote(do: nil) defp typespec(:bool, _), do: quote(do: boolean()) defp typespec(:string, _), do: quote(do: String.t()) defp typespec(:binary, _), do: quote(do: binary) diff --git a/lib/thrift/generator/binary/framed/server/http.ex b/lib/thrift/generator/binary/framed/server/http.ex index 10f62cd8..e0439ec2 100644 --- a/lib/thrift/generator/binary/framed/server/http.ex +++ b/lib/thrift/generator/binary/framed/server/http.ex @@ -167,10 +167,17 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do end def generate_exception_handlers(service_ast, service_module, file_group) do + + unknown_exception_handler = quote do + defp handle_exc(_, _) do + :error + end + end + for {_fname, function_ast} <- service_ast.functions, exception_ast <- function_ast.exceptions do response_module = Module.concat(service_module, Service.module_name(function_ast, :response)) generate_exception_handler(response_module, exception_ast, file_group) - end + end ++ [unknown_exception_handler] end def generate_exception_handler(response_module, exception_ast, file_group) do From e6370e645d4b9f40e518d7c5c39b00760944dbaf Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Wed, 26 Aug 2020 19:48:34 +0300 Subject: [PATCH 10/32] Reply header --- lib/thrift/generator/binary/framed/server/http.ex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/thrift/generator/binary/framed/server/http.ex b/lib/thrift/generator/binary/framed/server/http.ex index e0439ec2..7e98b4fc 100644 --- a/lib/thrift/generator/binary/framed/server/http.ex +++ b/lib/thrift/generator/binary/framed/server/http.ex @@ -3,6 +3,7 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do Service } alias Thrift.Parser.FileGroup + alias Thrift.Protocol.Binary def generate(service_module, service, file_group) do thrift_root = generate_thrift_root(service_module, service, file_group) @@ -99,6 +100,8 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do args_module = Module.concat(service_module, Service.module_name(function_ast, :args)) response_module = Module.concat(service_module, Service.module_name(function_ast, :response)) + response_header = Binary.serialize(:message_begin, {:reply, 0, func_name}) + # DANGER # expanded into (clasue -> block) which broke with-else statement # need to fix on Elixir side @@ -159,7 +162,9 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do case encoded_response do :error -> send_resp(conn, 500, "LOL") - encoded_response -> send_resp(conn, 200, encoded_response) + encoded_response -> + encoded_response = IO.iodata_to_binary([unquote(response_header) | encoded_response]) + send_resp(conn, 200, encoded_response) end end From 9021f422a264f6f01da78abe56437452f923b3ec Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Wed, 2 Sep 2020 19:22:21 +0300 Subject: [PATCH 11/32] Correct typespec for struct --- lib/thrift/generator/behaviour.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/thrift/generator/behaviour.ex b/lib/thrift/generator/behaviour.ex index bfb641ba..69845426 100644 --- a/lib/thrift/generator/behaviour.ex +++ b/lib/thrift/generator/behaviour.ex @@ -102,7 +102,8 @@ defmodule Thrift.Generator.Behaviour do dest_module = FileGroup.dest_module(file_group, name) quote do - %unquote(dest_module){} + # %unquote(dest_module){} + unquote(dest_module).t() end end @@ -110,7 +111,8 @@ defmodule Thrift.Generator.Behaviour do dest_module = FileGroup.dest_module(file_group, name) quote do - %unquote(dest_module){} + # %unquote(dest_module){} + unquote(dest_module).t() end end From 30b2442207d7c17d58f95a7dc5f490f4eadedde5 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Thu, 3 Sep 2020 15:49:22 +0300 Subject: [PATCH 12/32] wip http client --- .../generator/binary/framed/http/client.ex | 67 +++++++++++++++++++ .../framed/{server/http.ex => http/server.ex} | 4 +- lib/thrift/generator/service.ex | 5 +- 3 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 lib/thrift/generator/binary/framed/http/client.ex rename lib/thrift/generator/binary/framed/{server/http.ex => http/server.ex} (98%) diff --git a/lib/thrift/generator/binary/framed/http/client.ex b/lib/thrift/generator/binary/framed/http/client.ex new file mode 100644 index 00000000..194783d6 --- /dev/null +++ b/lib/thrift/generator/binary/framed/http/client.ex @@ -0,0 +1,67 @@ +defmodule Thrift.Generator.Binary.Framed.HTTP.Client do + alias Thrift.Generator.{ + Service + } + alias Thrift.Parser.FileGroup + alias Thrift.Protocol.Binary + + def generate(service_module, service, file_group) do + # thrift_root = generate_thrift_root(service_module, service, file_group) + + methods = Enum.map( + service.functions, + &(generate_method(service_module, &1, file_group)) + ) + + quote do + defmodule Binary.Framed.HTTP.Client do + alias Thrift.Protocol.Binary + + unquote_splicing(methods) + + end + end + end + + def generate_method(service_module, {func_name, function_ast}, file_group) do + + # function_name = nil + function_args = Enum.map(function_ast.params, &Macro.var(&1.name, nil)) + args_module = Service.module_name(function_ast, :args) + args_binary_module = Module.concat(args_module, :BinaryProtocol) + response_module = Service.module_name(function_ast, :response) + response_binary_module = Module.concat(response_module, :BinaryProtocol) + s_func_name = Atom.to_string(func_name) + + assignments = + function_ast.params + |> Enum.zip(function_args) + |> Enum.map(fn {param, var} -> + quote do + {unquote(param.name), unquote(var)} + end + end) + + quote do + def unquote(func_name)(thrift_url, unquote_splicing(function_args)) do + args = %unquote(args_module){unquote_splicing(assignments)} + serialized_args = unquote(args_binary_module).serialize(args) + header = Binary.serialize(:message_begin, {:call, 0, unquote(s_func_name)}) + payload = [header | serialized_args] |> IO.iodata_to_binary() + + {:ok, response} = HTTPoison.post(thrift_url, payload) + + result = + with( + {:ok, {:reply, 0, unquote(s_func_name), rest}} <- Binary.deserialize(:message_begin, response.body), + {%unquote(response_module){success: s}, ""} <- unquote(response_binary_module).deserialize(rest) + ) do + {:ok, s} + end + {response, result} + end + end + + end + +end diff --git a/lib/thrift/generator/binary/framed/server/http.ex b/lib/thrift/generator/binary/framed/http/server.ex similarity index 98% rename from lib/thrift/generator/binary/framed/server/http.ex rename to lib/thrift/generator/binary/framed/http/server.ex index 7e98b4fc..99e705a0 100644 --- a/lib/thrift/generator/binary/framed/server/http.ex +++ b/lib/thrift/generator/binary/framed/http/server.ex @@ -1,4 +1,4 @@ -defmodule Thrift.Generator.Binary.Framed.Server.HTTP do +defmodule Thrift.Generator.Binary.Framed.HTTP.Server do alias Thrift.Generator.{ Service } @@ -9,7 +9,7 @@ defmodule Thrift.Generator.Binary.Framed.Server.HTTP do thrift_root = generate_thrift_root(service_module, service, file_group) quote do - defmodule Binary.Framed.Server.HTTP do + defmodule Binary.Framed.HTTP.Server do unquote(thrift_root) def get_child_spec(module_handler) do diff --git a/lib/thrift/generator/service.ex b/lib/thrift/generator/service.ex index 5eff76f3..1e4989c1 100644 --- a/lib/thrift/generator/service.ex +++ b/lib/thrift/generator/service.ex @@ -27,7 +27,8 @@ defmodule Thrift.Generator.Service do framed_client = Generator.Binary.Framed.Client.generate(service) framed_server = Generator.Binary.Framed.Server.generate(dest_module, service, file_group) - http_server = Generator.Binary.Framed.Server.HTTP.generate(dest_module, service, file_group) + http_server = Generator.Binary.Framed.HTTP.Server.generate(dest_module, service, file_group) + http_client = Generator.Binary.Framed.HTTP.Client.generate(dest_module, service, file_group) service_module = quote do @@ -40,6 +41,8 @@ defmodule Thrift.Generator.Service do unquote(framed_server) + unquote(http_client) + unquote(http_server) end end From 0ee89e990929262e2c695b2bb49f04a7f7f78f8c Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Wed, 9 Sep 2020 15:14:51 +0300 Subject: [PATCH 13/32] Typespec in generated structs --- lib/thrift/generator/behaviour.ex | 79 +-------------------- lib/thrift/generator/struct_generator.ex | 13 +++- lib/thrift/generator/utils/types.ex | 89 ++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 80 deletions(-) create mode 100644 lib/thrift/generator/utils/types.ex diff --git a/lib/thrift/generator/behaviour.ex b/lib/thrift/generator/behaviour.ex index 69845426..383f90a6 100644 --- a/lib/thrift/generator/behaviour.ex +++ b/lib/thrift/generator/behaviour.ex @@ -3,6 +3,7 @@ # equivalent to their thrift counterparts. defmodule Thrift.Generator.Behaviour do @moduledoc false + import Thrift.Generator.Utils.Types, only: [typespec: 2] alias Thrift.AST.{ Exception, @@ -67,84 +68,6 @@ defmodule Thrift.Generator.Behaviour do end end - # defp typespec(:void, _), do: quote(do: no_return()) - defp typespec(:void, _), do: quote(do: nil) - defp typespec(:bool, _), do: quote(do: boolean()) - defp typespec(:string, _), do: quote(do: String.t()) - defp typespec(:binary, _), do: quote(do: binary) - defp typespec(:i8, _), do: quote(do: Thrift.i8()) - defp typespec(:i16, _), do: quote(do: Thrift.i16()) - defp typespec(:i32, _), do: quote(do: Thrift.i32()) - defp typespec(:i64, _), do: quote(do: Thrift.i64()) - defp typespec(:double, _), do: quote(do: Thrift.double()) - - defp typespec(%TypeRef{} = ref, file_group) do - file_group - |> FileGroup.resolve(ref) - |> typespec(file_group) - end - - defp typespec(%TEnum{}, _) do - quote do - non_neg_integer - end - end - - defp typespec(%Union{name: name}, file_group) do - dest_module = FileGroup.dest_module(file_group, name) - - quote do - %unquote(dest_module){} - end - end - - defp typespec(%Exception{name: name}, file_group) do - dest_module = FileGroup.dest_module(file_group, name) - - quote do - # %unquote(dest_module){} - unquote(dest_module).t() - end - end - - defp typespec(%Struct{name: name}, file_group) do - dest_module = FileGroup.dest_module(file_group, name) - - quote do - # %unquote(dest_module){} - unquote(dest_module).t() - end - end - - defp typespec({:set, _t}, _) do - quote do - %MapSet{} - end - end - - defp typespec({:list, t}, file_group) do - quote do - [unquote(typespec(t, file_group))] - end - end - - defp typespec({:map, {k, v}}, file_group) do - key_type = typespec(k, file_group) - val_type = typespec(v, file_group) - - quote do - %{unquote(key_type) => unquote(val_type)} - end - end - - defp typespec(unknown_typespec, _) do - Logger.error("Unknown type: #{inspect(unknown_typespec)}. Falling back to any()") - - quote do - any - end - end - defp ok_type(typespec_) do quote do {:ok, unquote(typespec_)} diff --git a/lib/thrift/generator/struct_generator.ex b/lib/thrift/generator/struct_generator.ex index 8c95f0b3..06791b43 100644 --- a/lib/thrift/generator/struct_generator.ex +++ b/lib/thrift/generator/struct_generator.ex @@ -44,7 +44,12 @@ defmodule Thrift.Generator.StructGenerator do end end - quote do + struct_spec_block = Enum.map( + struct.fields, + &(generate_struct_spec(&1, schema.file_group)) + ) + + quote do defmodule unquote(name) do @moduledoc false _ = unquote("Auto-generated Thrift #{label} #{struct.name}") @@ -59,7 +64,7 @@ defmodule Thrift.Generator.StructGenerator do ) unquote(define_block) - @type t :: %__MODULE__{} + @type t :: %__MODULE__{unquote_splicing(struct_spec_block)} def new, do: %__MODULE__{} unquote_splicing(List.wrap(extra_defs)) @@ -118,4 +123,8 @@ defmodule Thrift.Generator.StructGenerator do defp to_thrift(%TypeRef{referenced_type: type}, file_group) do to_thrift(FileGroup.resolve(file_group, type), file_group) end + + defp generate_struct_spec(field_ast, file_group) do + {field_ast.name, Utils.Types.typespec(field_ast.type, file_group)} + end end diff --git a/lib/thrift/generator/utils/types.ex b/lib/thrift/generator/utils/types.ex new file mode 100644 index 00000000..f4a8a68c --- /dev/null +++ b/lib/thrift/generator/utils/types.ex @@ -0,0 +1,89 @@ +defmodule Thrift.Generator.Utils.Types do + require Logger + alias Thrift.AST.{ + Exception, + Struct, + TEnum, + TypeRef, + Union + } + alias Thrift.Parser.FileGroup + + def typespec(:void, _), do: quote(do: nil) + def typespec(:bool, _), do: quote(do: boolean()) + def typespec(:string, _), do: quote(do: String.t()) + def typespec(:binary, _), do: quote(do: binary) + def typespec(:i8, _), do: quote(do: Thrift.i8()) + def typespec(:i16, _), do: quote(do: Thrift.i16()) + def typespec(:i32, _), do: quote(do: Thrift.i32()) + def typespec(:i64, _), do: quote(do: Thrift.i64()) + def typespec(:double, _), do: quote(do: Thrift.double()) + + def typespec(%TypeRef{} = ref, file_group) do + file_group + |> FileGroup.resolve(ref) + |> typespec(file_group) + end + + def typespec(%TEnum{}, _) do + quote do + non_neg_integer + end + end + + def typespec(%Union{name: name}, file_group) do + dest_module = FileGroup.dest_module(file_group, name) + + quote do + %unquote(dest_module){} + end + end + + def typespec(%Exception{name: name}, file_group) do + dest_module = FileGroup.dest_module(file_group, name) + + quote do + # %unquote(dest_module){} + unquote(dest_module).t() + end + end + + def typespec(%Struct{name: name}, file_group) do + dest_module = FileGroup.dest_module(file_group, name) + + quote do + # %unquote(dest_module){} + unquote(dest_module).t() + end + end + + def typespec({:set, _t}, _) do + quote do + %MapSet{} + end + end + + def typespec({:list, t}, file_group) do + quote do + [unquote(typespec(t, file_group))] + end + end + + def typespec({:map, {k, v}}, file_group) do + key_type = typespec(k, file_group) + val_type = typespec(v, file_group) + + quote do + %{unquote(key_type) => unquote(val_type)} + end + end + + def typespec(unknown_typespec, _) do + Logger.error("Unknown type: #{inspect(unknown_typespec)}. Falling back to any()") + + quote do + any() + end + end + +end From 3de99490cc8ea0a125b7d8f8c07a519004ce1145 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Fri, 11 Sep 2020 20:23:51 +0300 Subject: [PATCH 14/32] WIP generate test data --- lib/mix/tasks/compile.thrift.ex | 34 +++-- lib/mix/tasks/thrift.generate.ex | 7 +- lib/thrift/generator.ex | 78 +++++++++--- lib/thrift/generator/test_data_generator.ex | 116 ++++++++++++++++++ .../generator/test_data_generator/enum.ex | 27 ++++ .../test_data_generator/exception.ex | 2 + .../generator/test_data_generator/struct.ex | 48 ++++++++ .../generator/test_data_generator/union.ex | 38 ++++++ lib/thrift/parser/file_group.ex | 2 +- 9 files changed, 323 insertions(+), 29 deletions(-) create mode 100644 lib/thrift/generator/test_data_generator.ex create mode 100644 lib/thrift/generator/test_data_generator/enum.ex create mode 100644 lib/thrift/generator/test_data_generator/exception.ex create mode 100644 lib/thrift/generator/test_data_generator/struct.ex create mode 100644 lib/thrift/generator/test_data_generator/union.ex diff --git a/lib/mix/tasks/compile.thrift.ex b/lib/mix/tasks/compile.thrift.ex index 7d37beb5..bcc4924a 100644 --- a/lib/mix/tasks/compile.thrift.ex +++ b/lib/mix/tasks/compile.thrift.ex @@ -46,7 +46,8 @@ defmodule Mix.Tasks.Compile.Thrift do # other settings... thrift: [ files: Path.wildcard("thrift/**/*.thrift"), - output_path: "lib/generated" + output_path: "lib/generated", + output_test_data: "test/test_data/" ] ] end @@ -63,6 +64,12 @@ defmodule Mix.Tasks.Compile.Thrift do config = Keyword.get(Mix.Project.config(), :thrift, []) input_files = Keyword.get(config, :files, []) output_path = Keyword.get(config, :output_path, "lib") + output_test_data = Keyword.get( + config, + :output_test_data, + "test/test_data/" + ) |> IO.inspect(label: "output test data") + parser_opts = config @@ -77,8 +84,8 @@ defmodule Mix.Tasks.Compile.Thrift do {groups, [] = _diagnostics} -> groups - |> extract_targets(output_path, opts[:force]) - |> generate(manifest(), output_path, opts) + |> extract_targets({output_path, output_test_data}, opts[:force]) + |> generate(manifest(), [output_path, output_test_data], opts) {_groups, diagnostics} -> {:error, diagnostics} @@ -117,14 +124,16 @@ defmodule Mix.Tasks.Compile.Thrift do @typep mappings :: [{:stale, FileGroup.t(), [Path.t()]} | {:ok, FileGroup.t(), [Path.t()]}] - @spec extract_targets([FileGroup.t()], Path.t(), boolean) :: mappings - defp extract_targets(groups, output_path, force) when is_list(groups) do + @spec extract_targets([FileGroup.t()], {Path.t(), Path.t()}, boolean) :: mappings + defp extract_targets(groups, {output_path, _}, force) when is_list(groups) do for %FileGroup{initial_file: file} = group <- groups do + # IO.puts("ololo") targets = group + # |> IO.inspect(label: "group for extract") |> Thrift.Generator.targets() |> Enum.map(&Path.join(output_path, &1)) - + # IO.puts("keke") if force || Mix.Utils.stale?([file], targets) do {:stale, group, targets} else @@ -135,12 +144,12 @@ defmodule Mix.Tasks.Compile.Thrift do @spec generate(mappings, Path.t(), Path.t(), OptionParser.parsed()) :: {:ok | :noop | :error, [Diagnostic.t()]} - defp generate(mappings, manifest, output_path, opts) do + defp generate(mappings, manifest, [output_path, output_test_data] = output, opts) do timestamp = :calendar.universal_time() verbose = opts[:verbose] # Load the list of previously-generated files. - previous = read_manifest(manifest) + previous = read_manifest(manifest |> IO.inspect(label: "manifest")) |> IO.inspect(label: "previous") # Determine which of our current targets are in need of (re)generation. stale = for {:stale, group, targets} <- mappings, do: {group, targets} @@ -157,15 +166,22 @@ defmodule Mix.Tasks.Compile.Thrift do else # Ensure we have an output directory and remove old target files. File.mkdir_p!(output_path) + File.mkdir_p!(output_test_data) + Enum.each(removed, &File.rm/1) unless Enum.empty?(stale) do Mix.Utils.compiling_n(length(stale), :thrift) + # IO.inspect(stale, lable: "stale") + Enum.each(stale, fn {group, _targets} -> - Thrift.Generator.generate!(group, output_path) + # IO.inspect(output, label: "output") + Thrift.Generator.generate!(group, output) + IO.puts("after gen") verbose && Mix.shell().info("Compiled #{group.initial_file}") end) + end # Update and rewrite the manifest. diff --git a/lib/mix/tasks/thrift.generate.ex b/lib/mix/tasks/thrift.generate.ex index d13efc02..91198b89 100644 --- a/lib/mix/tasks/thrift.generate.ex +++ b/lib/mix/tasks/thrift.generate.ex @@ -42,7 +42,8 @@ defmodule Mix.Tasks.Thrift.Generate do # other settings... thrift: [ include_paths: ["./extra_thrift"], - output_path: "lib/generated" + output_path: "lib/generated", + output_test_data: "test/test_data/" ] ] end @@ -55,12 +56,14 @@ defmodule Mix.Tasks.Thrift.Generate do {opts, files} = OptionParser.parse!( args, - switches: [include: :keep, namespace: :string, out: :string, verbose: :boolean], + switches: [include: :keep, namespace: :string, out: :string, out_test: :string, verbose: :boolean], aliases: [I: :include, o: :out, v: :verbose] ) config = Keyword.get(Mix.Project.config(), :thrift, []) output_path = opts[:out] || Keyword.get(config, :output_path, "lib") + output_test_data = opts[:out_test] || Keyword.get(config, :output_test_data, "lib") |> IO.inspect(label: "test_data") + namespace = opts[:namespace] || Keyword.get(config, :namespace) include_paths = diff --git a/lib/thrift/generator.ex b/lib/thrift/generator.ex index f4f39d35..9ce88d44 100644 --- a/lib/thrift/generator.ex +++ b/lib/thrift/generator.ex @@ -10,7 +10,8 @@ defmodule Thrift.Generator do Generator, Generator.ConstantGenerator, Generator.EnumGenerator, - Generator.StructGenerator + Generator.StructGenerator, + Generator.TestDataGenerator } alias Thrift.Parser.FileGroup @@ -24,7 +25,9 @@ defmodule Thrift.Generator do schema |> Map.put(:file_group, file_group) |> generate_schema + |> hd() |> Enum.map(fn {name, _} -> target_path(name) end) + # |> IO.inspect(label: "target paths") end) end @@ -38,12 +41,13 @@ defmodule Thrift.Generator do |> Kernel.<>(".ex") end - def generate!(%FileGroup{} = file_group, output_dir) do + def generate!(%FileGroup{} = file_group, [_output_dir, _output_test_data] = output) do + IO.inspect(file_group.schemas, label: "fg") Enum.flat_map(file_group.schemas, fn {_, schema} -> schema |> Map.put(:file_group, file_group) |> generate_schema - |> write_schema_to_file(output_dir) + |> write_schema_to_file(output) end) end @@ -62,25 +66,36 @@ defmodule Thrift.Generator do current_module_file_group = FileGroup.set_current_module(schema.file_group, schema.module) schema = %Schema{schema | file_group: current_module_file_group} - List.flatten([ - generate_enum_modules(schema), - generate_const_modules(schema), - generate_struct_modules(schema), - generate_union_modules(schema), - generate_exception_modules(schema), - generate_services(schema), - generate_behaviours(schema) - ]) - end - - defp write_schema_to_file(generated_modules, output_dir) do - generated_modules + modules = + List.flatten([ + generate_enum_modules(schema), + generate_const_modules(schema), + generate_struct_modules(schema), + generate_union_modules(schema), + generate_exception_modules(schema), + generate_services(schema), + generate_behaviours(schema) + ]) + test_modules = generate_test_data_modules(schema) + [modules, test_modules] + end + + defp write_schema_to_file(module_groups, outputs) do + module_groups + |> Enum.zip(outputs) + |> IO.inspect(label: "zip res") + |> Enum.map(&perform_write/1) + |> IO.inspect(label: "after") + end + + defp perform_write({modules, output}) do + modules |> resolve_name_collisions |> Enum.map(fn {name, quoted} -> filename = target_path(name) source = Macro.to_string(quoted) - path = Path.join(output_dir, filename) + path = Path.join(output, filename) path |> Path.dirname() @@ -141,6 +156,35 @@ defmodule Thrift.Generator do {:defmodule, meta, [name, [do: {:__block__, [], ast1 ++ ast2}]]} end + defp generate_test_data_modules(schema) do + endless_label = fn label -> Stream.repeatedly(fn -> label end) end + zip_with_label = fn label, stream -> Stream.zip(endless_label.(label), stream) end + + structs_stream = zip_with_label.(:struct, schema.structs) + exceptions_stream = zip_with_label.(:exception, schema.exceptions) + unions_stream = zip_with_label.(:union, schema.unions) + enums_stream = zip_with_label.(:enum, schema.enums) + + all_streams = Stream.concat([ + structs_stream, + exceptions_stream, + unions_stream, + enums_stream + ]) + + for {label, {_, struct}} <- all_streams do + full_name = FileGroup.dest_module(schema.file_group, struct) + full_test_data_name = TestDataGenerator.test_data_module_from_data_module(full_name) + {full_test_data_name, TestDataGenerator.generate(label, schema, full_name, struct)} + end + + # for {_, struct} <- schema.structs do + # full_name = FileGroup.dest_module(schema.file_group, struct) + # full_test_data_name = TestDataGenerator.test_data_module_from_data_module(full_name) + # {full_test_data_name, TestDataGenerator.generate(:struct, schema, full_name, struct)} + # end + end + defp generate_enum_modules(schema) do for {_, enum} <- schema.enums do full_name = FileGroup.dest_module(schema.file_group, enum) diff --git a/lib/thrift/generator/test_data_generator.ex b/lib/thrift/generator/test_data_generator.ex new file mode 100644 index 00000000..b275ce13 --- /dev/null +++ b/lib/thrift/generator/test_data_generator.ex @@ -0,0 +1,116 @@ +defmodule Thrift.Generator.TestDataGenerator do + alias __MODULE__, as: TestDataGenerator + alias Thrift.AST.{ + Exception, + Struct, + TEnum, + TypeRef, + Union + } + alias Thrift.Parser.FileGroup + + def generate(label, schema, full_name, struct) do + case label do + :union -> TestDataGenerator.Union.generate(schema, full_name, struct) + :enum -> TestDataGenerator.Enum.generate(schema, full_name, struct) + _ -> TestDataGenerator.Struct.generate(schema, full_name, struct) + end + end + + + def get_generator(:bool, _) do + quote do + bool() + end + end + + def get_generator(:string, _) do + quote do + utf8(100) + end + end + + def get_generator(:binary, _) do + quote do + binary(100) + end + end + + def get_generator(:i8, _) do + quote do + integer(-128, 127) + end + end + + def get_generator(:i16, _) do + quote do + integer(-32_768, 32_767) + end + end + + def get_generator(:i32, _) do + quote do + integer(-2_147_483_648, 2_147_483_647) + end + end + + def get_generator(:i64, _) do + quote do + integer( + -9_223_372_036_854_775_808, + 9_223_372_036_854_775_807 + ) + end + end + + def get_generator(:double, _) do + quote do + float() + end + end + + def get_generator(%TypeRef{} = ref, file_group) do + file_group + |> FileGroup.resolve(ref) + |> get_generator(file_group) + end + + def get_generator(%Union{name: name}, file_group) do + dest_module = + FileGroup.dest_module(file_group, name) + |> test_data_module_from_data_module + + quote do + unquote(dest_module).get_generator() + end + end + + def get_generator(%Exception{name: name}, file_group) do + dest_module = + FileGroup.dest_module(file_group, name) + |> test_data_module_from_data_module + + quote do + unquote(dest_module).get_generator() + end + end + + def get_generator(%Struct{name: name}, file_group) do + dest_module = + FileGroup.dest_module(file_group, name) + |> test_data_module_from_data_module + + quote do + unquote(dest_module).get_generator() + end + end + + def test_data_module_from_data_module(data_module) do + + data_module + |> Module.split() + |> List.insert_at(0, "TestData") + |> Module.concat() + end + +end diff --git a/lib/thrift/generator/test_data_generator/enum.ex b/lib/thrift/generator/test_data_generator/enum.ex new file mode 100644 index 00000000..ba546994 --- /dev/null +++ b/lib/thrift/generator/test_data_generator/enum.ex @@ -0,0 +1,27 @@ +defmodule Thrift.Generator.TestDataGenerator.Enum do + alias Thrift.Generator.TestDataGenerator + + def generate(_schema, name, enum_ast) do + # file_group = schema.file_group + test_data_module_name = TestDataGenerator.test_data_module_from_data_module(name) + # enums = Enum.map(enum_ast.values, fn {vname, _} -> to_name(vname) end) + enums = Enum.map(enum_ast.values, fn {_, val} -> val end) + quote do + defmodule unquote(test_data_module_name) do + use PropCheck + + def get_generator() do + oneof(unquote(enums)) + end + + end + end + end + + def to_name(key) do + key + |> to_string() + |> String.downcase() + |> String.to_atom() + end +end diff --git a/lib/thrift/generator/test_data_generator/exception.ex b/lib/thrift/generator/test_data_generator/exception.ex new file mode 100644 index 00000000..5e2d343b --- /dev/null +++ b/lib/thrift/generator/test_data_generator/exception.ex @@ -0,0 +1,2 @@ +defmodule Thrift.Generator.TestDataGenerator.Exception do +end diff --git a/lib/thrift/generator/test_data_generator/struct.ex b/lib/thrift/generator/test_data_generator/struct.ex new file mode 100644 index 00000000..51cdf26b --- /dev/null +++ b/lib/thrift/generator/test_data_generator/struct.ex @@ -0,0 +1,48 @@ +defmodule Thrift.Generator.TestDataGenerator.Struct do + alias Thrift.Generator.TestDataGenerator + def generate(schema, name, struct_ast) do + file_group = schema.file_group + test_data_module_name = TestDataGenerator.test_data_module_from_data_module(name) + + struct_fields = Enum.map(struct_ast.fields, &gen_struct_field/1) + draw_fields = Enum.map(struct_ast.fields, &gen_draw(&1, file_group)) + + quote do + defmodule unquote(test_data_module_name) do + use PropCheck + + def get_generator() do + let [unquote_splicing(draw_fields)] do + %unquote(name){unquote_splicing(struct_fields)} + end + end + + end + end + end + + + def gen_struct_field(field_ast) do + quote do + unquote({field_ast.name, Macro.var(field_ast.name, nil)}) + end + end + + def gen_draw(field_ast, file_group) do + + generator = TestDataGenerator.get_generator(field_ast.type, file_group) + generator = + if field_ast.required do + generator + else + quote do + oneof([unquote(generator), nil]) + end + end + + quote do + unquote(Macro.var(field_ast.name, nil)) <- unquote(generator) + end + end + +end diff --git a/lib/thrift/generator/test_data_generator/union.ex b/lib/thrift/generator/test_data_generator/union.ex new file mode 100644 index 00000000..f7414820 --- /dev/null +++ b/lib/thrift/generator/test_data_generator/union.ex @@ -0,0 +1,38 @@ +defmodule Thrift.Generator.TestDataGenerator.Union do + alias Thrift.Generator.TestDataGenerator + def generate(schema, name, struct_ast) do + file_group = schema.file_group + test_data_module_name = TestDataGenerator.test_data_module_from_data_module(name) + + subgens = Enum.map(struct_ast.fields, &gen_sub_gens(&1, file_group, name)) + fields = Enum.map(struct_ast.fields, &Macro.var(&1.name, nil)) + + quote do + defmodule unquote(test_data_module_name) do + use PropCheck + + def get_generator() do + + unquote_splicing(subgens) + + oneof([unquote_splicing(fields)]) + end + + end + end + end + + + def gen_sub_gens(field_ast, file_group, module_name) do + field_var = Macro.var(field_ast.name, nil) + gen = TestDataGenerator.get_generator(field_ast.type, file_group) + fill_field = [{field_ast.name, field_var}] + quote do + unquote(field_var) = + let unquote(field_var) <- unquote(gen) do + %unquote(module_name){unquote_splicing(fill_field)} + end + end + end + +end diff --git a/lib/thrift/parser/file_group.ex b/lib/thrift/parser/file_group.ex index 26499fbe..d9eb617e 100644 --- a/lib/thrift/parser/file_group.ex +++ b/lib/thrift/parser/file_group.ex @@ -185,7 +185,7 @@ defmodule Thrift.Parser.FileGroup do # (ignoring case), use that instead to avoid generating two modules with # the same spellings but different cases. schema = file_group.schemas[base] - + IO.puts("file_group flatten") symbols = [ Enum.map(schema.enums, fn {_, s} -> s.name end), From 48b5d0b9c6ab218c8b38f9e24280fec16fa21911 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Mon, 14 Sep 2020 16:53:07 +0300 Subject: [PATCH 15/32] collections --- lib/thrift/generator/test_data_generator.ex | 32 +++++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/thrift/generator/test_data_generator.ex b/lib/thrift/generator/test_data_generator.ex index b275ce13..add2918d 100644 --- a/lib/thrift/generator/test_data_generator.ex +++ b/lib/thrift/generator/test_data_generator.ex @@ -1,5 +1,6 @@ defmodule Thrift.Generator.TestDataGenerator do alias __MODULE__, as: TestDataGenerator + alias Thrift.AST.{ Exception, Struct, @@ -7,6 +8,7 @@ defmodule Thrift.Generator.TestDataGenerator do TypeRef, Union } + alias Thrift.Parser.FileGroup def generate(label, schema, full_name, struct) do @@ -17,7 +19,6 @@ defmodule Thrift.Generator.TestDataGenerator do end end - def get_generator(:bool, _) do quote do bool() @@ -69,6 +70,33 @@ defmodule Thrift.Generator.TestDataGenerator do end end + def get_generator({:list, t}, file_group) do + subgen = get_generator(t, file_group) + + quote do + list(unquote(subgen)) + end + end + + def get_generator({:set, t}, file_group) do + subgen = get_generator({:list, t}, file_group) + + quote do + let set <- unquote(subgen) do + MapSet.new(set) + end + end + end + + def get_generator({:map, {k, v}}, file_group) do + key_subgen = get_generator(k, file_group) + val_subgen = get_generator(v, file_group) + + quote do + map(unquote(key_subgen), unquote(val_subgen)) + end + end + def get_generator(%TypeRef{} = ref, file_group) do file_group |> FileGroup.resolve(ref) @@ -106,11 +134,9 @@ defmodule Thrift.Generator.TestDataGenerator do end def test_data_module_from_data_module(data_module) do - data_module |> Module.split() |> List.insert_at(0, "TestData") |> Module.concat() end - end From 08fed2614b8cb6591e411168c1f4edd8d07d29d7 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Tue, 15 Sep 2020 12:59:33 +0300 Subject: [PATCH 16/32] Generators for enum and collections --- lib/thrift/generator/test_data_generator.ex | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/thrift/generator/test_data_generator.ex b/lib/thrift/generator/test_data_generator.ex index b275ce13..d787d8bc 100644 --- a/lib/thrift/generator/test_data_generator.ex +++ b/lib/thrift/generator/test_data_generator.ex @@ -69,6 +69,41 @@ defmodule Thrift.Generator.TestDataGenerator do end end + def get_generator({:list, t}, file_group) do + subgen = get_generator(t, file_group) + quote do + list(unquote(subgen)) + end + end + + def get_generator({:set, t}, file_group) do + subgen = get_generator({:list, t}, file_group) + quote do + let x <- unquote(subgen) do + MapSet.new(x) + end + end + end + + def get_generator({:map, {k, v}}, file_group) do + key_subgen = get_generator(k, file_group) + val_subgen = get_generator(v, file_group) + + quote do + map(unquote(key_subgen), unquote(val_subgen)) + end + end + + def get_generator(%TEnum{name: name}, file_group) do + dest_module = + FileGroup.dest_module(file_group, name) + |> test_data_module_from_data_module + + quote do + unquote(dest_module).get_generator() + end + end + def get_generator(%TypeRef{} = ref, file_group) do file_group |> FileGroup.resolve(ref) From 20c13b83bebe4f2558980eb778f03cdea19d3a47 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Tue, 15 Sep 2020 13:12:31 +0300 Subject: [PATCH 17/32] fix no field struct problem --- .../generator/test_data_generator/struct.ex | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/thrift/generator/test_data_generator/struct.ex b/lib/thrift/generator/test_data_generator/struct.ex index 51cdf26b..f6e26026 100644 --- a/lib/thrift/generator/test_data_generator/struct.ex +++ b/lib/thrift/generator/test_data_generator/struct.ex @@ -7,15 +7,25 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do struct_fields = Enum.map(struct_ast.fields, &gen_struct_field/1) draw_fields = Enum.map(struct_ast.fields, &gen_draw(&1, file_group)) + gen = + case draw_fields do + [] -> + quote do + %unquote(name){unquote_splicing(struct_fields)} + end + draw_fields -> + quote do + let [unquote_splicing(draw_fields)] do + %unquote(name){unquote_splicing(struct_fields)} + end + end + end + quote do defmodule unquote(test_data_module_name) do use PropCheck - def get_generator() do - let [unquote_splicing(draw_fields)] do - %unquote(name){unquote_splicing(struct_fields)} - end - end + unquote(gen) end end From 37176085b98eba30576582e6cc612ce4c8aa420e Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Tue, 15 Sep 2020 13:18:48 +0300 Subject: [PATCH 18/32] fix --- lib/thrift/generator/test_data_generator/struct.ex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/thrift/generator/test_data_generator/struct.ex b/lib/thrift/generator/test_data_generator/struct.ex index f6e26026..f86bee87 100644 --- a/lib/thrift/generator/test_data_generator/struct.ex +++ b/lib/thrift/generator/test_data_generator/struct.ex @@ -24,8 +24,9 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do quote do defmodule unquote(test_data_module_name) do use PropCheck - - unquote(gen) + def get_generator() do + unquote(gen) + end end end From d3696edd3245e8cccae6fad7129b2e2504b6c3f0 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Thu, 17 Sep 2020 19:08:22 +0300 Subject: [PATCH 19/32] better text --- lib/mix/tasks/compile.thrift.ex | 2 +- lib/mix/tasks/thrift.generate.ex | 2 +- lib/thrift/generator.ex | 24 ++++++++++++------- lib/thrift/generator/test_data_generator.ex | 17 +++++++++---- .../generator/test_data_generator/struct.ex | 4 +++- 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/lib/mix/tasks/compile.thrift.ex b/lib/mix/tasks/compile.thrift.ex index bcc4924a..c2416845 100644 --- a/lib/mix/tasks/compile.thrift.ex +++ b/lib/mix/tasks/compile.thrift.ex @@ -68,7 +68,7 @@ defmodule Mix.Tasks.Compile.Thrift do config, :output_test_data, "test/test_data/" - ) |> IO.inspect(label: "output test data") + )# |> IO.inspect(label: "output test data") parser_opts = diff --git a/lib/mix/tasks/thrift.generate.ex b/lib/mix/tasks/thrift.generate.ex index 91198b89..60e2e76a 100644 --- a/lib/mix/tasks/thrift.generate.ex +++ b/lib/mix/tasks/thrift.generate.ex @@ -62,7 +62,7 @@ defmodule Mix.Tasks.Thrift.Generate do config = Keyword.get(Mix.Project.config(), :thrift, []) output_path = opts[:out] || Keyword.get(config, :output_path, "lib") - output_test_data = opts[:out_test] || Keyword.get(config, :output_test_data, "lib") |> IO.inspect(label: "test_data") + output_test_data = opts[:out_test] || Keyword.get(config, :output_test_data, "lib") #|> IO.inspect(label: "test_data") namespace = opts[:namespace] || Keyword.get(config, :namespace) diff --git a/lib/thrift/generator.ex b/lib/thrift/generator.ex index 9ce88d44..8e995361 100644 --- a/lib/thrift/generator.ex +++ b/lib/thrift/generator.ex @@ -27,6 +27,7 @@ defmodule Thrift.Generator do |> generate_schema |> hd() |> Enum.map(fn {name, _} -> target_path(name) end) + # |> IO.inspect(label: "target paths") end) end @@ -43,6 +44,7 @@ defmodule Thrift.Generator do def generate!(%FileGroup{} = file_group, [_output_dir, _output_test_data] = output) do IO.inspect(file_group.schemas, label: "fg") + Enum.flat_map(file_group.schemas, fn {_, schema} -> schema |> Map.put(:file_group, file_group) @@ -65,6 +67,7 @@ defmodule Thrift.Generator do def generate_schema(schema) do current_module_file_group = FileGroup.set_current_module(schema.file_group, schema.module) schema = %Schema{schema | file_group: current_module_file_group} + IO.inspect(schema, label: "schema") modules = List.flatten([ @@ -76,6 +79,7 @@ defmodule Thrift.Generator do generate_services(schema), generate_behaviours(schema) ]) + test_modules = generate_test_data_modules(schema) [modules, test_modules] end @@ -83,9 +87,10 @@ defmodule Thrift.Generator do defp write_schema_to_file(module_groups, outputs) do module_groups |> Enum.zip(outputs) - |> IO.inspect(label: "zip res") + # |> IO.inspect(label: "zip res") |> Enum.map(&perform_write/1) - |> IO.inspect(label: "after") + + # |> IO.inspect(label: "after") end defp perform_write({modules, output}) do @@ -165,12 +170,13 @@ defmodule Thrift.Generator do unions_stream = zip_with_label.(:union, schema.unions) enums_stream = zip_with_label.(:enum, schema.enums) - all_streams = Stream.concat([ - structs_stream, - exceptions_stream, - unions_stream, - enums_stream - ]) + all_streams = + Stream.concat([ + structs_stream, + exceptions_stream, + unions_stream, + enums_stream + ]) for {label, {_, struct}} <- all_streams do full_name = FileGroup.dest_module(schema.file_group, struct) @@ -243,7 +249,7 @@ defmodule Thrift.Generator do defp generate_services(schema) do for {_, service} <- schema.services do - Generator.Service.generate(schema, service) + Generator.Service.generate(schema, service) end end diff --git a/lib/thrift/generator/test_data_generator.ex b/lib/thrift/generator/test_data_generator.ex index d787d8bc..82cb2e9b 100644 --- a/lib/thrift/generator/test_data_generator.ex +++ b/lib/thrift/generator/test_data_generator.ex @@ -1,5 +1,6 @@ defmodule Thrift.Generator.TestDataGenerator do alias __MODULE__, as: TestDataGenerator + alias Thrift.AST.{ Exception, Struct, @@ -7,6 +8,7 @@ defmodule Thrift.Generator.TestDataGenerator do TypeRef, Union } + alias Thrift.Parser.FileGroup def generate(label, schema, full_name, struct) do @@ -17,7 +19,6 @@ defmodule Thrift.Generator.TestDataGenerator do end end - def get_generator(:bool, _) do quote do bool() @@ -25,9 +26,17 @@ defmodule Thrift.Generator.TestDataGenerator do end def get_generator(:string, _) do + ascii = ?a..?z |> Enum.to_list() + quote do - utf8(100) + let chars <- list(oneof(unquote(ascii))) do + List.to_string(chars) + end end + + # quote do + # utf8(100) + # end end def get_generator(:binary, _) do @@ -71,6 +80,7 @@ defmodule Thrift.Generator.TestDataGenerator do def get_generator({:list, t}, file_group) do subgen = get_generator(t, file_group) + quote do list(unquote(subgen)) end @@ -78,6 +88,7 @@ defmodule Thrift.Generator.TestDataGenerator do def get_generator({:set, t}, file_group) do subgen = get_generator({:list, t}, file_group) + quote do let x <- unquote(subgen) do MapSet.new(x) @@ -141,11 +152,9 @@ defmodule Thrift.Generator.TestDataGenerator do end def test_data_module_from_data_module(data_module) do - data_module |> Module.split() |> List.insert_at(0, "TestData") |> Module.concat() end - end diff --git a/lib/thrift/generator/test_data_generator/struct.ex b/lib/thrift/generator/test_data_generator/struct.ex index f86bee87..0628a5f5 100644 --- a/lib/thrift/generator/test_data_generator/struct.ex +++ b/lib/thrift/generator/test_data_generator/struct.ex @@ -15,15 +15,17 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do end draw_fields -> quote do + let [unquote_splicing(draw_fields)] do %unquote(name){unquote_splicing(struct_fields)} end end - end + end quote do defmodule unquote(test_data_module_name) do use PropCheck + def get_generator() do unquote(gen) end From d00eef34e2f4c94e39c6e62989424dee72d17a2a Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Thu, 24 Sep 2020 19:10:06 +0300 Subject: [PATCH 20/32] apply nested defaults --- .../generator/binary/framed/http/client.ex | 2 +- .../generator/binary/framed/http/server.ex | 52 ++++++++++++------- lib/thrift/generator/test_data_generator.ex | 28 ++++++++-- .../generator/test_data_generator/struct.ex | 51 ++++++++++++++++-- lib/thrift/generator/utils/types.ex | 3 +- 5 files changed, 106 insertions(+), 30 deletions(-) diff --git a/lib/thrift/generator/binary/framed/http/client.ex b/lib/thrift/generator/binary/framed/http/client.ex index 194783d6..819b9779 100644 --- a/lib/thrift/generator/binary/framed/http/client.ex +++ b/lib/thrift/generator/binary/framed/http/client.ex @@ -49,7 +49,7 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Client do header = Binary.serialize(:message_begin, {:call, 0, unquote(s_func_name)}) payload = [header | serialized_args] |> IO.iodata_to_binary() - {:ok, response} = HTTPoison.post(thrift_url, payload) + {:ok, response} = HTTPoison.post(thrift_url, payload, [], hackney: [pool: false]) result = with( diff --git a/lib/thrift/generator/binary/framed/http/server.ex b/lib/thrift/generator/binary/framed/http/server.ex index 99e705a0..f816c204 100644 --- a/lib/thrift/generator/binary/framed/http/server.ex +++ b/lib/thrift/generator/binary/framed/http/server.ex @@ -2,6 +2,7 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Server do alias Thrift.Generator.{ Service } + alias Thrift.Parser.FileGroup alias Thrift.Protocol.Binary @@ -39,7 +40,10 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Server do {:ok, payload, conn} <- Plug.Conn.read_body(conn), {:ok, parsed} <- Protocol.Binary.deserialize(:message_begin, payload) ) do + IO.inspect(parsed) handle_thrift_message(conn, parsed, opts) + else + {:error, _} -> send_resp(conn, 500, "bad thrift") end end @@ -75,14 +79,15 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Server do opts end - unquote_splicing(Enum.map(service_ast.functions, &generate_thrift_handle(service_module, file_group, &1))) + unquote_splicing( + Enum.map(service_ast.functions, &generate_thrift_handle(service_module, file_group, &1)) + ) match _ do send_resp(conn, 404, "Ohh") end unquote_splicing(generate_exception_handlers(service_ast, service_module, file_group)) - end end end @@ -146,41 +151,49 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Server do _rest } <- unquote(args_module).BinaryProtocol.deserialize(body), - {:ok, result} <- apply( - handler_module, - unquote(handle), - [unquote_splicing(handler_args)] - ), + {:ok, result} <- + apply( + handler_module, + unquote(handle), + [unquote_splicing(handler_args)] + ), response = %unquote(response_module){success: result} ) do unquote(response_module).BinaryProtocol.serialize(response) else - :error -> :error - {:exception, exc} -> handle_exc(exc, unquote(response_module)) - # unquote_splicing(exceptions_clauses) + :error -> + :error + + {:exception, exc} -> + handle_exc(exc, unquote(response_module)) + # unquote_splicing(exceptions_clauses) end case encoded_response do - :error -> send_resp(conn, 500, "LOL") + :error -> + send_resp(conn, 500, "LOL") + encoded_response -> encoded_response = IO.iodata_to_binary([unquote(response_header) | encoded_response]) send_resp(conn, 200, encoded_response) end - end end end def generate_exception_handlers(service_ast, service_module, file_group) do - - unknown_exception_handler = quote do - defp handle_exc(_, _) do - :error + unknown_exception_handler = + quote do + defp handle_exc(_, _) do + :error + end end - end - for {_fname, function_ast} <- service_ast.functions, exception_ast <- function_ast.exceptions do - response_module = Module.concat(service_module, Service.module_name(function_ast, :response)) + for {_fname, function_ast} <- service_ast.functions, + exception_ast <- function_ast.exceptions do + response_module = + Module.concat(service_module, Service.module_name(function_ast, :response)) + generate_exception_handler(response_module, exception_ast, file_group) end ++ [unknown_exception_handler] end @@ -196,6 +209,5 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Server do unquote(response_module).BinaryProtocol.serialize(response) end end - end end diff --git a/lib/thrift/generator/test_data_generator.ex b/lib/thrift/generator/test_data_generator.ex index 82cb2e9b..4132b71d 100644 --- a/lib/thrift/generator/test_data_generator.ex +++ b/lib/thrift/generator/test_data_generator.ex @@ -33,10 +33,6 @@ defmodule Thrift.Generator.TestDataGenerator do List.to_string(chars) end end - - # quote do - # utf8(100) - # end end def get_generator(:binary, _) do @@ -157,4 +153,28 @@ defmodule Thrift.Generator.TestDataGenerator do |> List.insert_at(0, "TestData") |> Module.concat() end + + def apply_defaults(struct_) when is_struct(struct_) do + module_name = + struct_.__struct__ + |> test_data_module_from_data_module() + + apply(module_name, :apply_defaults, [struct_]) + end + + def apply_defaults(some_list) when is_list(some_list) do + Enum.map(some_list, &apply_defaults/1) + end + + def apply_defaults(some_map) when is_map(some_map) do + Map.new(some_map, fn {k, v} -> {k, apply_defaults(v)} end) + end + + def apply_defaults(%MapSet{} = some_set) do + MapSet.new(some_set, &apply_defaults/1) + end + + def apply_defaults(anything_else) do + anything_else + end end diff --git a/lib/thrift/generator/test_data_generator/struct.ex b/lib/thrift/generator/test_data_generator/struct.ex index 0628a5f5..34ca64a0 100644 --- a/lib/thrift/generator/test_data_generator/struct.ex +++ b/lib/thrift/generator/test_data_generator/struct.ex @@ -1,5 +1,6 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do alias Thrift.Generator.TestDataGenerator + def generate(schema, name, struct_ast) do file_group = schema.file_group test_data_module_name = TestDataGenerator.test_data_module_from_data_module(name) @@ -7,20 +8,39 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do struct_fields = Enum.map(struct_ast.fields, &gen_struct_field/1) draw_fields = Enum.map(struct_ast.fields, &gen_draw(&1, file_group)) + {fast_access, apply_defaults} = + struct_ast.fields + |> Enum.flat_map(&gen_fast_access_replace/1) + |> Enum.unzip() + gen = case draw_fields do [] -> quote do %unquote(name){unquote_splicing(struct_fields)} end + draw_fields -> quote do - let [unquote_splicing(draw_fields)] do %unquote(name){unquote_splicing(struct_fields)} end end - end + end + + gen_replace = + case fast_access do + [] -> + quote do + unquote(Macro.var(:struct_, nil)) + end + + _otherwise -> + quote do + %unquote(name){unquote_splicing(fast_access)} = struct_ + %{struct_ | unquote_splicing(apply_defaults)} + end + end quote do defmodule unquote(test_data_module_name) do @@ -30,11 +50,13 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do unquote(gen) end + def apply_defaults(struct_) do + unquote(gen_replace) + end end end end - def gen_struct_field(field_ast) do quote do unquote({field_ast.name, Macro.var(field_ast.name, nil)}) @@ -42,8 +64,8 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do end def gen_draw(field_ast, file_group) do - generator = TestDataGenerator.get_generator(field_ast.type, file_group) + generator = if field_ast.required do generator @@ -58,4 +80,25 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do end end + def gen_fast_access_replace(field_ast) do + field_var = Macro.var(field_ast.name, nil) + default = field_ast.default + + access = + quote do + unquote({field_ast.name, field_var}) + end + + with_default = + quote do + Thrift.Generator.TestDataGenerator.apply_defaults(unquote(field_var)) || unquote(default) + end + + replace = + quote do + unquote({field_ast.name, with_default}) + end + + [{access, replace}] + end end diff --git a/lib/thrift/generator/utils/types.ex b/lib/thrift/generator/utils/types.ex index f4a8a68c..4810608d 100644 --- a/lib/thrift/generator/utils/types.ex +++ b/lib/thrift/generator/utils/types.ex @@ -1,5 +1,6 @@ defmodule Thrift.Generator.Utils.Types do require Logger + alias Thrift.AST.{ Exception, Struct, @@ -7,6 +8,7 @@ defmodule Thrift.Generator.Utils.Types do TypeRef, Union } + alias Thrift.Parser.FileGroup def typespec(:void, _), do: quote(do: nil) @@ -85,5 +87,4 @@ defmodule Thrift.Generator.Utils.Types do any() end end - end From 2fc7ac05cdd8733352a2c2fdc557405f0225b075 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Thu, 24 Sep 2020 19:33:40 +0300 Subject: [PATCH 21/32] correct apply defaults --- .../generator/test_data_generator/struct.ex | 13 +++++++-- .../generator/test_data_generator/union.ex | 28 +++++++++++++++++-- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/lib/thrift/generator/test_data_generator/struct.ex b/lib/thrift/generator/test_data_generator/struct.ex index 34ca64a0..2e1b827d 100644 --- a/lib/thrift/generator/test_data_generator/struct.ex +++ b/lib/thrift/generator/test_data_generator/struct.ex @@ -90,8 +90,17 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do end with_default = - quote do - Thrift.Generator.TestDataGenerator.apply_defaults(unquote(field_var)) || unquote(default) + case default do + nil -> + quote do + Thrift.Generator.TestDataGenerator.apply_defaults(unquote(field_var)) + end + + default -> + quote do + Thrift.Generator.TestDataGenerator.apply_defaults(unquote(field_var)) || + unquote(default) + end end replace = diff --git a/lib/thrift/generator/test_data_generator/union.ex b/lib/thrift/generator/test_data_generator/union.ex index f7414820..5d92e8a1 100644 --- a/lib/thrift/generator/test_data_generator/union.ex +++ b/lib/thrift/generator/test_data_generator/union.ex @@ -1,5 +1,7 @@ defmodule Thrift.Generator.TestDataGenerator.Union do alias Thrift.Generator.TestDataGenerator + alias Thrift.Generator.TestDataGenerator.Struct, as: StructGenerator + def generate(schema, name, struct_ast) do file_group = schema.file_group test_data_module_name = TestDataGenerator.test_data_module_from_data_module(name) @@ -7,26 +9,47 @@ defmodule Thrift.Generator.TestDataGenerator.Union do subgens = Enum.map(struct_ast.fields, &gen_sub_gens(&1, file_group, name)) fields = Enum.map(struct_ast.fields, &Macro.var(&1.name, nil)) + {fast_access, apply_defaults} = + struct_ast.fields + |> Enum.flat_map(&StructGenerator.gen_fast_access_replace/1) + |> Enum.unzip() + + gen_replace = + case fast_access do + [] -> + quote do + unquote(Macro.var(:struct_, nil)) + end + + _otherwise -> + quote do + %unquote(name){unquote_splicing(fast_access)} = struct_ + %{struct_ | unquote_splicing(apply_defaults)} + end + end + quote do defmodule unquote(test_data_module_name) do use PropCheck def get_generator() do - unquote_splicing(subgens) oneof([unquote_splicing(fields)]) end + def apply_defaults(struct_) do + unquote(gen_replace) + end end end end - def gen_sub_gens(field_ast, file_group, module_name) do field_var = Macro.var(field_ast.name, nil) gen = TestDataGenerator.get_generator(field_ast.type, file_group) fill_field = [{field_ast.name, field_var}] + quote do unquote(field_var) = let unquote(field_var) <- unquote(gen) do @@ -34,5 +57,4 @@ defmodule Thrift.Generator.TestDataGenerator.Union do end end end - end From 33792f55144f7ab4d2162441a23c23e953f70894 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Tue, 29 Sep 2020 20:41:13 +0300 Subject: [PATCH 22/32] typedef wip --- lib/thrift/generator.ex | 42 +++++++++---- lib/thrift/generator/test_data_generator.ex | 30 ++++++--- .../generator/test_data_generator/enum.ex | 4 +- .../generator/test_data_generator/struct.ex | 32 ++++++++-- .../generator/test_data_generator/typedef.ex | 63 +++++++++++++++++++ .../generator/test_data_generator/union.ex | 6 +- 6 files changed, 147 insertions(+), 30 deletions(-) create mode 100644 lib/thrift/generator/test_data_generator/typedef.ex diff --git a/lib/thrift/generator.ex b/lib/thrift/generator.ex index 8e995361..d56e3375 100644 --- a/lib/thrift/generator.ex +++ b/lib/thrift/generator.ex @@ -87,10 +87,7 @@ defmodule Thrift.Generator do defp write_schema_to_file(module_groups, outputs) do module_groups |> Enum.zip(outputs) - # |> IO.inspect(label: "zip res") |> Enum.map(&perform_write/1) - - # |> IO.inspect(label: "after") end defp perform_write({modules, output}) do @@ -165,6 +162,7 @@ defmodule Thrift.Generator do endless_label = fn label -> Stream.repeatedly(fn -> label end) end zip_with_label = fn label, stream -> Stream.zip(endless_label.(label), stream) end + typedefs_stream = zip_with_label.(:typedef, schema.typedefs) structs_stream = zip_with_label.(:struct, schema.structs) exceptions_stream = zip_with_label.(:exception, schema.exceptions) unions_stream = zip_with_label.(:union, schema.unions) @@ -172,23 +170,45 @@ defmodule Thrift.Generator do all_streams = Stream.concat([ + typedefs_stream, structs_stream, exceptions_stream, unions_stream, enums_stream ]) - for {label, {_, struct}} <- all_streams do - full_name = FileGroup.dest_module(schema.file_group, struct) + for {label, {key, struct}} <- all_streams do + full_name = + case label do + :typedef -> + thrift_module = Atom.to_string(schema.module) + typedef = key |> Atom.to_string() |> String.capitalize() + name = "#{thrift_module}.#{typedef}" |> String.to_atom() + + # typedef_name = + # Atom.to_string(schema.module) <> + # "." <> (key |> Atom.to_string() |> String.capitalize()) + + # IO.inspect(schema, limit: 10, label: "schema") + + # IO.inspect(ss) + FileGroup.dest_module(schema.file_group, name) + + # |> IO.inspect(label: "full_name") + + _otherwise -> + FileGroup.dest_module(schema.file_group, struct) + end + + # if label == :typedef do + # IO.inspect(ss) + # end + # full_name = FileGroup.dest_module(schema.file_group, struct) + # |> IO.inspect() full_test_data_name = TestDataGenerator.test_data_module_from_data_module(full_name) + {full_test_data_name, TestDataGenerator.generate(label, schema, full_name, struct)} end - - # for {_, struct} <- schema.structs do - # full_name = FileGroup.dest_module(schema.file_group, struct) - # full_test_data_name = TestDataGenerator.test_data_module_from_data_module(full_name) - # {full_test_data_name, TestDataGenerator.generate(:struct, schema, full_name, struct)} - # end end defp generate_enum_modules(schema) do diff --git a/lib/thrift/generator/test_data_generator.ex b/lib/thrift/generator/test_data_generator.ex index 4132b71d..eb3591b2 100644 --- a/lib/thrift/generator/test_data_generator.ex +++ b/lib/thrift/generator/test_data_generator.ex @@ -13,6 +13,7 @@ defmodule Thrift.Generator.TestDataGenerator do def generate(label, schema, full_name, struct) do case label do + :typedef -> TestDataGenerator.Typedef.generate(schema, full_name, struct) :union -> TestDataGenerator.Union.generate(schema, full_name, struct) :enum -> TestDataGenerator.Enum.generate(schema, full_name, struct) _ -> TestDataGenerator.Struct.generate(schema, full_name, struct) @@ -107,7 +108,7 @@ defmodule Thrift.Generator.TestDataGenerator do |> test_data_module_from_data_module quote do - unquote(dest_module).get_generator() + unquote(dest_module).get_generator(context) end end @@ -123,7 +124,7 @@ defmodule Thrift.Generator.TestDataGenerator do |> test_data_module_from_data_module quote do - unquote(dest_module).get_generator() + unquote(dest_module).get_generator(context) end end @@ -133,7 +134,7 @@ defmodule Thrift.Generator.TestDataGenerator do |> test_data_module_from_data_module quote do - unquote(dest_module).get_generator() + unquote(dest_module).get_generator(context) end end @@ -143,15 +144,16 @@ defmodule Thrift.Generator.TestDataGenerator do |> test_data_module_from_data_module quote do - unquote(dest_module).get_generator() + unquote(dest_module).get_generator(context) end end def test_data_module_from_data_module(data_module) do - data_module - |> Module.split() - |> List.insert_at(0, "TestData") - |> Module.concat() + # data_module + # |> Module.split() + # |> List.insert_at(0, "TestData") + # |> Module.concat() + Module.concat(TestData, data_module) end def apply_defaults(struct_) when is_struct(struct_) do @@ -177,4 +179,16 @@ defmodule Thrift.Generator.TestDataGenerator do def apply_defaults(anything_else) do anything_else end + + def find_first_realization([], _fns, default) do + default + end + + def find_first_realization([module | rest], {fname, arity} = func, default) do + if function_exported?(module, fname, arity) do + Function.capture(module, fname, arity) + else + find_first_realization(rest, func, default) + end + end end diff --git a/lib/thrift/generator/test_data_generator/enum.ex b/lib/thrift/generator/test_data_generator/enum.ex index ba546994..348c60d7 100644 --- a/lib/thrift/generator/test_data_generator/enum.ex +++ b/lib/thrift/generator/test_data_generator/enum.ex @@ -6,14 +6,14 @@ defmodule Thrift.Generator.TestDataGenerator.Enum do test_data_module_name = TestDataGenerator.test_data_module_from_data_module(name) # enums = Enum.map(enum_ast.values, fn {vname, _} -> to_name(vname) end) enums = Enum.map(enum_ast.values, fn {_, val} -> val end) + quote do defmodule unquote(test_data_module_name) do use PropCheck - def get_generator() do + def get_generator(context \\ nil) do oneof(unquote(enums)) end - end end end diff --git a/lib/thrift/generator/test_data_generator/struct.ex b/lib/thrift/generator/test_data_generator/struct.ex index 2e1b827d..9cd59333 100644 --- a/lib/thrift/generator/test_data_generator/struct.ex +++ b/lib/thrift/generator/test_data_generator/struct.ex @@ -10,7 +10,7 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do {fast_access, apply_defaults} = struct_ast.fields - |> Enum.flat_map(&gen_fast_access_replace/1) + |> Enum.map(&gen_fast_access_replace/1) |> Enum.unzip() gen = @@ -46,11 +46,11 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do defmodule unquote(test_data_module_name) do use PropCheck - def get_generator() do + def get_generator(context \\ nil) do unquote(gen) end - def apply_defaults(struct_) do + def apply_defaults(struct_, context \\ nil) do unquote(gen_replace) end end @@ -93,12 +93,12 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do case default do nil -> quote do - Thrift.Generator.TestDataGenerator.apply_defaults(unquote(field_var)) + Thrift.Generator.TestDataGenerator.apply_defaults(unquote(field_var), context) end default -> quote do - Thrift.Generator.TestDataGenerator.apply_defaults(unquote(field_var)) || + Thrift.Generator.TestDataGenerator.apply_defaults(unquote(field_var), context) || unquote(default) end end @@ -108,6 +108,26 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do unquote({field_ast.name, with_default}) end - [{access, replace}] + {access, replace} + end + + def gen_apply_defaults(struct_ast, name) do + {fast_access, apply_defaults} = + struct_ast.fields + |> Enum.map(&gen_fast_access_replace/1) + |> Enum.unzip() + + case fast_access do + [] -> + quote do + unquote(Macro.var(:struct_, nil)) + end + + _otherwise -> + quote do + %unquote(name){unquote_splicing(fast_access)} = struct_ + %{struct_ | unquote_splicing(apply_defaults)} + end + end end end diff --git a/lib/thrift/generator/test_data_generator/typedef.ex b/lib/thrift/generator/test_data_generator/typedef.ex new file mode 100644 index 00000000..a39311a1 --- /dev/null +++ b/lib/thrift/generator/test_data_generator/typedef.ex @@ -0,0 +1,63 @@ +defmodule Thrift.Generator.TestDataGenerator.Typedef do + alias Thrift.Generator.TestDataGenerator + + def generate(schema, name, type) do + file_group = schema.file_group + test_data_module_name = TestDataGenerator.test_data_module_from_data_module(name) + + quote do + defmodule unquote(test_data_module_name) do + use PropCheck + + def get_generator(context) when is_list(context) do + ctx = Enum.map(context, &handler_module_from_context/1) + + f = + TestDataGenerator.find_first_realization( + ctx, + {:get_generator, 1}, + &get_default_generator/1 + ) + + f.(context) + end + + def get_generator(nil) do + get_generator([]) + end + + def get_generator(context) do + get_generator([context]) + end + + def apply_defaults(example, nil) do + apply_defaults(example, []) + end + + def apply_defaults(example, context) when is_list(context) do + ctx = Enum.map(context, &handler_module_from_context/1) + + f = + TestDataGenerator.find_first_realization( + context, + {:apply_defaults, 2}, + &default_apply_defaults/2 + ) + + f.(example, context) + end + + def get_default_generator(context) do + unquote(TestDataGenerator.get_generator(type, file_group)) + end + + def default_apply_defaults(example, context) do + end + + def handler_module_from_context(context) do + Module.concat(context, unquote(name)) + end + end + end + end +end diff --git a/lib/thrift/generator/test_data_generator/union.ex b/lib/thrift/generator/test_data_generator/union.ex index 5d92e8a1..18d41a5e 100644 --- a/lib/thrift/generator/test_data_generator/union.ex +++ b/lib/thrift/generator/test_data_generator/union.ex @@ -11,7 +11,7 @@ defmodule Thrift.Generator.TestDataGenerator.Union do {fast_access, apply_defaults} = struct_ast.fields - |> Enum.flat_map(&StructGenerator.gen_fast_access_replace/1) + |> Enum.map(&StructGenerator.gen_fast_access_replace/1) |> Enum.unzip() gen_replace = @@ -32,13 +32,13 @@ defmodule Thrift.Generator.TestDataGenerator.Union do defmodule unquote(test_data_module_name) do use PropCheck - def get_generator() do + def get_generator(context \\ nil) do unquote_splicing(subgens) oneof([unquote_splicing(fields)]) end - def apply_defaults(struct_) do + def apply_defaults(struct_, context \\ nil) do unquote(gen_replace) end end From aa6bbe9ed96bff8aa2dc7ec52ccbcf54d3770970 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Wed, 30 Sep 2020 19:23:20 +0300 Subject: [PATCH 23/32] typedef testdata hooks --- lib/thrift/ast.ex | 41 ++++++++++++--- lib/thrift/generator/test_data_generator.ex | 52 +++++++++++++------ .../generator/test_data_generator/typedef.ex | 8 +-- lib/thrift/parser/file_group.ex | 6 +++ src/thrift_parser.yrl | 3 +- 5 files changed, 83 insertions(+), 27 deletions(-) diff --git a/lib/thrift/ast.ex b/lib/thrift/ast.ex index 0cc046f2..5c7165a4 100644 --- a/lib/thrift/ast.ex +++ b/lib/thrift/ast.ex @@ -98,6 +98,23 @@ defmodule Thrift.AST do end end + defmodule Typedef do + + @type t :: %Typedef{ + line: Thrift.Parser.line(), + annotations: Thrift.Parser.annotations(), + name: atom(), + type: Types.t() + } + @enforce_keys [:name, :type] + defstruct line: nil, annotations: %{}, name: nil, type: nil + + def new(name, type) do + %Typedef{name: List.to_atom(name), type: type} + end + + end + defmodule TEnum do @moduledoc """ An enumerated type with named values. @@ -528,18 +545,30 @@ defmodule Thrift.AST do } end - defp merge(schema, {:typedef, actual_type, type_alias}) do + defp merge(schema, %Typedef{} = typedef) do %Schema{ schema | typedefs: - put_new_strict( - schema.typedefs, - List.to_atom(type_alias), - add_namespace_to_type(schema.module, actual_type) - ) + put_new_strict( + schema.typedefs, + typedef.name, + add_namespace_to_name(schema.module, typedef) + ) } end + # defp merge(schema, {:typedef, actual_type, type_alias}) do + # %Schema{ + # schema + # | typedefs: + # put_new_strict( + # schema.typedefs, + # List.to_atom(type_alias), + # add_namespace_to_type(schema.module, actual_type) + # ) + # } + # end + defp add_namespace_to_name(nil, model) do model end diff --git a/lib/thrift/generator/test_data_generator.ex b/lib/thrift/generator/test_data_generator.ex index eb3591b2..860a529f 100644 --- a/lib/thrift/generator/test_data_generator.ex +++ b/lib/thrift/generator/test_data_generator.ex @@ -6,6 +6,7 @@ defmodule Thrift.Generator.TestDataGenerator do Struct, TEnum, TypeRef, + Typedef, Union } @@ -112,10 +113,19 @@ defmodule Thrift.Generator.TestDataGenerator do end end - def get_generator(%TypeRef{} = ref, file_group) do - file_group - |> FileGroup.resolve(ref) - |> get_generator(file_group) + def get_generator( + %TypeRef{referenced_type: type_name}, + %FileGroup{resolutions: resolutions} = file_group + ) do + case resolutions[type_name] do + %Typedef{} = td -> + get_generator(td, file_group) + + other_type -> + file_group + |> FileGroup.resolve(other_type) + |> get_generator(file_group) + end end def get_generator(%Union{name: name}, file_group) do @@ -148,35 +158,41 @@ defmodule Thrift.Generator.TestDataGenerator do end end + def get_generator(%Typedef{name: name}, file_group) do + dest_module = + FileGroup.dest_module(file_group, name) + |> test_data_module_from_data_module + + quote do + unquote(dest_module).get_generator(context) + end + end + def test_data_module_from_data_module(data_module) do - # data_module - # |> Module.split() - # |> List.insert_at(0, "TestData") - # |> Module.concat() Module.concat(TestData, data_module) end - def apply_defaults(struct_) when is_struct(struct_) do + def apply_defaults(struct_, context) when is_struct(struct_) do module_name = struct_.__struct__ |> test_data_module_from_data_module() - apply(module_name, :apply_defaults, [struct_]) + apply(module_name, :apply_defaults, [struct_, context]) end - def apply_defaults(some_list) when is_list(some_list) do - Enum.map(some_list, &apply_defaults/1) + def apply_defaults(some_list, context) when is_list(some_list) do + Enum.map(some_list, &apply_defaults(&1, context)) end - def apply_defaults(some_map) when is_map(some_map) do - Map.new(some_map, fn {k, v} -> {k, apply_defaults(v)} end) + def apply_defaults(some_map, context) when is_map(some_map) do + Map.new(some_map, fn {k, v} -> {k, apply_defaults(v, context)} end) end - def apply_defaults(%MapSet{} = some_set) do - MapSet.new(some_set, &apply_defaults/1) + def apply_defaults(%MapSet{} = some_set, context) do + MapSet.new(some_set, &apply_defaults(&1, context)) end - def apply_defaults(anything_else) do + def apply_defaults(anything_else, _context) do anything_else end @@ -185,6 +201,8 @@ defmodule Thrift.Generator.TestDataGenerator do end def find_first_realization([module | rest], {fname, arity} = func, default) do + Code.ensure_loaded(module) + if function_exported?(module, fname, arity) do Function.capture(module, fname, arity) else diff --git a/lib/thrift/generator/test_data_generator/typedef.ex b/lib/thrift/generator/test_data_generator/typedef.ex index a39311a1..9f738469 100644 --- a/lib/thrift/generator/test_data_generator/typedef.ex +++ b/lib/thrift/generator/test_data_generator/typedef.ex @@ -1,13 +1,14 @@ defmodule Thrift.Generator.TestDataGenerator.Typedef do alias Thrift.Generator.TestDataGenerator - def generate(schema, name, type) do + def generate(schema, name, typedef_ast) do file_group = schema.file_group test_data_module_name = TestDataGenerator.test_data_module_from_data_module(name) quote do defmodule unquote(test_data_module_name) do use PropCheck + alias Thrift.Generator.TestDataGenerator def get_generator(context) when is_list(context) do ctx = Enum.map(context, &handler_module_from_context/1) @@ -39,7 +40,7 @@ defmodule Thrift.Generator.TestDataGenerator.Typedef do f = TestDataGenerator.find_first_realization( - context, + ctx, {:apply_defaults, 2}, &default_apply_defaults/2 ) @@ -48,10 +49,11 @@ defmodule Thrift.Generator.TestDataGenerator.Typedef do end def get_default_generator(context) do - unquote(TestDataGenerator.get_generator(type, file_group)) + unquote(TestDataGenerator.get_generator(typedef_ast.type, file_group)) end def default_apply_defaults(example, context) do + TestDataGenerator.apply_defaults(example, context) end def handler_module_from_context(context) do diff --git a/lib/thrift/parser/file_group.ex b/lib/thrift/parser/file_group.ex index d9eb617e..a577fbc2 100644 --- a/lib/thrift/parser/file_group.ex +++ b/lib/thrift/parser/file_group.ex @@ -17,6 +17,7 @@ defmodule Thrift.Parser.FileGroup do Struct, TEnum, TypeRef, + Typedef, Union, ValueRef } @@ -125,6 +126,10 @@ defmodule Thrift.Parser.FileGroup do resolve(group, resolutions[type_name]) end + def resolve(%FileGroup{} = group, %Typedef{type: type}) do + resolve(group, type) + end + def resolve(%FileGroup{resolutions: resolutions} = group, %ValueRef{ referenced_value: value_name }) do @@ -186,6 +191,7 @@ defmodule Thrift.Parser.FileGroup do # the same spellings but different cases. schema = file_group.schemas[base] IO.puts("file_group flatten") + symbols = [ Enum.map(schema.enums, fn {_, s} -> s.name end), diff --git a/src/thrift_parser.yrl b/src/thrift_parser.yrl index 12812b75..c2019b75 100644 --- a/src/thrift_parser.yrl +++ b/src/thrift_parser.yrl @@ -105,7 +105,8 @@ ConstList -> ConstValue Separator ConstList: ['$1'|'$3']. % Typedef Typedef -> typedef FieldType Annotations ident Annotations Separator: - {typedef, '$2', unwrap('$4')}. + build_node('Typedef', line('$1'), '$5', [unwrap('$4'), '$2']). + % {typedef, '$2', unwrap('$4')}. % Enum From 6d5b5b7c98cbe49b204633984c60fb667ff3ad89 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Fri, 2 Oct 2020 14:21:40 +0300 Subject: [PATCH 24/32] default empty context in apply defaults --- lib/thrift/generator/test_data_generator.ex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/thrift/generator/test_data_generator.ex b/lib/thrift/generator/test_data_generator.ex index 860a529f..d468d6e5 100644 --- a/lib/thrift/generator/test_data_generator.ex +++ b/lib/thrift/generator/test_data_generator.ex @@ -172,6 +172,9 @@ defmodule Thrift.Generator.TestDataGenerator do Module.concat(TestData, data_module) end + def apply_defaults(struct_) do + apply_defaults(struct_, nil) + end def apply_defaults(struct_, context) when is_struct(struct_) do module_name = struct_.__struct__ From 9ce83c5a0b182d0d7f620f041894391b51ebe87a Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Mon, 9 Nov 2020 21:57:43 +0300 Subject: [PATCH 25/32] annotations wip --- lib/thrift/generator/test_data_generator.ex | 113 +++++++++++++----- .../generator/test_data_generator/struct.ex | 2 +- lib/thrift/generator/utils.ex | 1 + 3 files changed, 84 insertions(+), 32 deletions(-) diff --git a/lib/thrift/generator/test_data_generator.ex b/lib/thrift/generator/test_data_generator.ex index 54f4c4cb..fe6bc783 100644 --- a/lib/thrift/generator/test_data_generator.ex +++ b/lib/thrift/generator/test_data_generator.ex @@ -21,13 +21,14 @@ defmodule Thrift.Generator.TestDataGenerator do end end - def get_generator(:bool, _) do + def get_generator(type, file_group, annotations \\ %{}) + def get_generator(:bool, _, _) do quote do bool() end end - def get_generator(:string, _) do + def get_generator(:string, _, _) do ascii = ?a..?z |> Enum.to_list() quote do @@ -37,55 +38,63 @@ defmodule Thrift.Generator.TestDataGenerator do end end - def get_generator(:binary, _) do + def get_generator(:binary, _, _) do quote do binary(100) end end - def get_generator(:i8, _) do + def get_generator(:i8, _, annotations) do + min = refinement_from_annotations(annotations, :min, &parse_integer/1, -128) + max = refinement_from_annotations(annotations, :max, &parse_integer/1, 127) + quote do - integer(-128, 127) + integer(unquote(min), unquote(max)) end end - def get_generator(:i16, _) do + def get_generator(:i16, _, annotations) do + min = refinement_from_annotations(annotations, :min, &parse_integer/1, -32_768) + max = refinement_from_annotations(annotations, :max, &parse_integer/1, 32_767) quote do - integer(-32_768, 32_767) + integer(unquote(min), unquote(max)) end end - def get_generator(:i32, _) do + def get_generator(:i32, _, annotations) do + min = refinement_from_annotations(annotations, :min, &parse_integer/1, -2_147_483_648) + max = refinement_from_annotations(annotations, :max, &parse_integer/1, 2_147_483_647) quote do - integer(-2_147_483_648, 2_147_483_647) + integer(unquote(min), unquote(max)) end end - def get_generator(:i64, _) do + def get_generator(:i64, _, annotations) do + min = refinement_from_annotations(annotations, :min, &parse_integer/1, -9_223_372_036_854_775_808) + max = refinement_from_annotations(annotations, :max, &parse_integer/1, 9_223_372_036_854_775_807) quote do - integer( - -9_223_372_036_854_775_808, - 9_223_372_036_854_775_807 - ) + integer(unquote(min), unquote(max)) end end - def get_generator(:double, _) do + def get_generator(:double, _, annotations) do + min = refinement_from_annotations(annotations, :min, &parse_float/1, :inf) + max = refinement_from_annotations(annotations, :max, &parse_float/1, :inf) quote do - float() + float(unquote(min), unquote(max)) end end - def get_generator({:list, t}, file_group) do - subgen = get_generator(t, file_group) + def get_generator({:list, t}, file_group, annotations) do + subgen = get_generator(t, file_group, annotations) quote do list(unquote(subgen)) end end - def get_generator({:set, t}, file_group) do - subgen = get_generator({:list, t}, file_group) + def get_generator({:set, t}, file_group, annotations) do + subgen = get_generator({:list, t}, file_group, annotations) quote do let set <- unquote(subgen) do @@ -94,16 +103,34 @@ defmodule Thrift.Generator.TestDataGenerator do end end - def get_generator({:map, {k, v}}, file_group) do - key_subgen = get_generator(k, file_group) - val_subgen = get_generator(v, file_group) + def get_generator({:map, {k, v}}, file_group, annotations) do + + separate = + fn {k, v} -> + [type, key] = + k + |> Atom.to_string() + |> String.split("_", parts: 2) + {type, {String.to_atom(key), v}} + end + + separated_annotations = + annotations + |> Enum.map(separate) + |> Enum.group_by(&elem(&1, 0), &elem(&1, 2)) + + key_annotations = Map.new(separated_annotations[:k]) + val_annotations = Map.new(separated_annotations[:v]) + + key_subgen = get_generator(k, file_group, key_annotations) + val_subgen = get_generator(v, file_group, val_annotations) quote do map(unquote(key_subgen), unquote(val_subgen)) end end - def get_generator(%TEnum{name: name}, file_group) do + def get_generator(%TEnum{name: name}, file_group, _annotations) do dest_module = FileGroup.dest_module(file_group, name) |> test_data_module_from_data_module @@ -115,20 +142,21 @@ defmodule Thrift.Generator.TestDataGenerator do def get_generator( %TypeRef{referenced_type: type_name}, - %FileGroup{resolutions: resolutions} = file_group + %FileGroup{resolutions: resolutions} = file_group, + annotations ) do case resolutions[type_name] do %Typedef{} = td -> - get_generator(td, file_group) + get_generator(td, file_group, annotations) other_type -> file_group |> FileGroup.resolve(other_type) - |> get_generator(file_group) + |> get_generator(file_group, annotations) end end - def get_generator(%Union{name: name}, file_group) do + def get_generator(%Union{name: name}, file_group, annotations) do dest_module = FileGroup.dest_module(file_group, name) |> test_data_module_from_data_module @@ -138,7 +166,7 @@ defmodule Thrift.Generator.TestDataGenerator do end end - def get_generator(%Exception{name: name}, file_group) do + def get_generator(%Exception{name: name}, file_group, annotations) do dest_module = FileGroup.dest_module(file_group, name) |> test_data_module_from_data_module @@ -148,7 +176,7 @@ defmodule Thrift.Generator.TestDataGenerator do end end - def get_generator(%Struct{name: name}, file_group) do + def get_generator(%Struct{name: name}, file_group, annotations) do dest_module = FileGroup.dest_module(file_group, name) |> test_data_module_from_data_module @@ -158,7 +186,7 @@ defmodule Thrift.Generator.TestDataGenerator do end end - def get_generator(%Typedef{name: name}, file_group) do + def get_generator(%Typedef{name: name}, file_group, annotations) do dest_module = FileGroup.dest_module(file_group, name) |> test_data_module_from_data_module @@ -212,4 +240,27 @@ defmodule Thrift.Generator.TestDataGenerator do find_first_realization(rest, func, default) end end + + defp refinement_from_annotations(annotations, field, parse_fun, default) do + annotations + |> Map.get(field) + |> case do + nil -> default + "^" <> v -> + var_name = v |> String.to_atom() |> Macro.var(nil) + quote do + ^unquote(var_name) + end + v -> parse_fun.(v) + end + end + + defp parse_integer(str) do + str |> Integer.parse() |> elem(0) + end + + defp parse_float(str) do + str |> Float.parse() |> elem(0) + end + end diff --git a/lib/thrift/generator/test_data_generator/struct.ex b/lib/thrift/generator/test_data_generator/struct.ex index 9cd59333..f287ebd2 100644 --- a/lib/thrift/generator/test_data_generator/struct.ex +++ b/lib/thrift/generator/test_data_generator/struct.ex @@ -64,7 +64,7 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do end def gen_draw(field_ast, file_group) do - generator = TestDataGenerator.get_generator(field_ast.type, file_group) + generator = TestDataGenerator.get_generator(field_ast.type, file_group, field_ast.annotations) generator = if field_ast.required do diff --git a/lib/thrift/generator/utils.ex b/lib/thrift/generator/utils.ex index 01daddb5..29bb2f6b 100644 --- a/lib/thrift/generator/utils.ex +++ b/lib/thrift/generator/utils.ex @@ -343,4 +343,5 @@ defmodule Thrift.Generator.Utils do defp resolution_failed(%ValueRef{} = ref) do raise "Fatal error: Could not find value: #{ref.referenced_value}" end + end From b34987726a7dcead118b547b88b50ae1fb571fa2 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Wed, 11 Nov 2020 20:46:47 +0300 Subject: [PATCH 26/32] annotations --- lib/thrift/generator/test_data_generator.ex | 49 +++++++++++++++---- .../generator/test_data_generator/enum.ex | 2 +- .../generator/test_data_generator/struct.ex | 3 +- .../generator/test_data_generator/typedef.ex | 22 +++++---- .../generator/test_data_generator/union.ex | 4 +- 5 files changed, 56 insertions(+), 24 deletions(-) diff --git a/lib/thrift/generator/test_data_generator.ex b/lib/thrift/generator/test_data_generator.ex index fe6bc783..f1a31399 100644 --- a/lib/thrift/generator/test_data_generator.ex +++ b/lib/thrift/generator/test_data_generator.ex @@ -119,8 +119,8 @@ defmodule Thrift.Generator.TestDataGenerator do |> Enum.map(separate) |> Enum.group_by(&elem(&1, 0), &elem(&1, 2)) - key_annotations = Map.new(separated_annotations[:k]) - val_annotations = Map.new(separated_annotations[:v]) + key_annotations = Map.new(separated_annotations[:k] || %{}) + val_annotations = Map.new(separated_annotations[:v] || %{}) key_subgen = get_generator(k, file_group, key_annotations) val_subgen = get_generator(v, file_group, val_annotations) @@ -161,8 +161,9 @@ defmodule Thrift.Generator.TestDataGenerator do FileGroup.dest_module(file_group, name) |> test_data_module_from_data_module + props = gen_props(annotations) quote do - unquote(dest_module).get_generator(context) + unquote(dest_module).get_generator(context, unquote(props)) end end @@ -181,8 +182,10 @@ defmodule Thrift.Generator.TestDataGenerator do FileGroup.dest_module(file_group, name) |> test_data_module_from_data_module + + props = gen_props(annotations) quote do - unquote(dest_module).get_generator(context) + unquote(dest_module).get_generator(context, unquote(props)) end end @@ -191,8 +194,9 @@ defmodule Thrift.Generator.TestDataGenerator do FileGroup.dest_module(file_group, name) |> test_data_module_from_data_module + props = gen_props(annotations) quote do - unquote(dest_module).get_generator(context) + unquote(dest_module).get_generator(context, unquote(props)) end end @@ -246,11 +250,7 @@ defmodule Thrift.Generator.TestDataGenerator do |> Map.get(field) |> case do nil -> default - "^" <> v -> - var_name = v |> String.to_atom() |> Macro.var(nil) - quote do - ^unquote(var_name) - end + "^" <> v -> annotation_to_pin(v) v -> parse_fun.(v) end end @@ -263,4 +263,33 @@ defmodule Thrift.Generator.TestDataGenerator do str |> Float.parse() |> elem(0) end + defp annotation_to_pin(anno) do + var_name = + anno + |> String.to_atom() + |> Macro.var(nil) + quote do + ^unquote(var_name) + end + end + + defp gen_props(annotations) do + to_prop = fn {k, v} -> + v = + case v do + "^" <> v -> annotation_to_pin(v) + v -> v + end + {k, v} + end + + props_from_annotations = + annotations + |> Enum.map(to_prop) + + quote do + [unquote_splicing(props_from_annotations)] + end + end + end diff --git a/lib/thrift/generator/test_data_generator/enum.ex b/lib/thrift/generator/test_data_generator/enum.ex index 348c60d7..5bcd6bf1 100644 --- a/lib/thrift/generator/test_data_generator/enum.ex +++ b/lib/thrift/generator/test_data_generator/enum.ex @@ -11,7 +11,7 @@ defmodule Thrift.Generator.TestDataGenerator.Enum do defmodule unquote(test_data_module_name) do use PropCheck - def get_generator(context \\ nil) do + def get_generator(context \\ nil, props \\ []) do oneof(unquote(enums)) end end diff --git a/lib/thrift/generator/test_data_generator/struct.ex b/lib/thrift/generator/test_data_generator/struct.ex index f287ebd2..2d6eb012 100644 --- a/lib/thrift/generator/test_data_generator/struct.ex +++ b/lib/thrift/generator/test_data_generator/struct.ex @@ -46,7 +46,7 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do defmodule unquote(test_data_module_name) do use PropCheck - def get_generator(context \\ nil) do + def get_generator(context \\ nil, props \\ []) do unquote(gen) end @@ -64,6 +64,7 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do end def gen_draw(field_ast, file_group) do + # IO.inspect(field_ast.annotations, label: "anno") generator = TestDataGenerator.get_generator(field_ast.type, file_group, field_ast.annotations) generator = diff --git a/lib/thrift/generator/test_data_generator/typedef.ex b/lib/thrift/generator/test_data_generator/typedef.ex index 9f738469..4cad8289 100644 --- a/lib/thrift/generator/test_data_generator/typedef.ex +++ b/lib/thrift/generator/test_data_generator/typedef.ex @@ -10,25 +10,27 @@ defmodule Thrift.Generator.TestDataGenerator.Typedef do use PropCheck alias Thrift.Generator.TestDataGenerator - def get_generator(context) when is_list(context) do + def get_generator(context, props \\ []) + + def get_generator(context, props) when is_list(context) do ctx = Enum.map(context, &handler_module_from_context/1) f = TestDataGenerator.find_first_realization( ctx, - {:get_generator, 1}, - &get_default_generator/1 + {:get_generator, 2}, + &get_default_generator/2 ) - f.(context) + f.(context, props) end - def get_generator(nil) do - get_generator([]) + def get_generator(nil, props) do + get_generator([], props) end - def get_generator(context) do - get_generator([context]) + def get_generator(context, props) do + get_generator([context], props) end def apply_defaults(example, nil) do @@ -48,8 +50,8 @@ defmodule Thrift.Generator.TestDataGenerator.Typedef do f.(example, context) end - def get_default_generator(context) do - unquote(TestDataGenerator.get_generator(typedef_ast.type, file_group)) + def get_default_generator(context, props) do + unquote(TestDataGenerator.get_generator(typedef_ast.type, file_group, typedef_ast.annotations)) end def default_apply_defaults(example, context) do diff --git a/lib/thrift/generator/test_data_generator/union.ex b/lib/thrift/generator/test_data_generator/union.ex index 18d41a5e..0dde015b 100644 --- a/lib/thrift/generator/test_data_generator/union.ex +++ b/lib/thrift/generator/test_data_generator/union.ex @@ -32,7 +32,7 @@ defmodule Thrift.Generator.TestDataGenerator.Union do defmodule unquote(test_data_module_name) do use PropCheck - def get_generator(context \\ nil) do + def get_generator(context \\ nil, props \\ []) do unquote_splicing(subgens) oneof([unquote_splicing(fields)]) @@ -47,7 +47,7 @@ defmodule Thrift.Generator.TestDataGenerator.Union do def gen_sub_gens(field_ast, file_group, module_name) do field_var = Macro.var(field_ast.name, nil) - gen = TestDataGenerator.get_generator(field_ast.type, file_group) + gen = TestDataGenerator.get_generator(field_ast.type, file_group, field_ast.annotations) fill_field = [{field_ast.name, field_var}] quote do From 6bf05be125d18c5f3e409b8536340165ec2faea2 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Thu, 19 Nov 2020 13:52:33 +0300 Subject: [PATCH 27/32] struct with overrides --- .../generator/test_data_generator/struct.ex | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/thrift/generator/test_data_generator/struct.ex b/lib/thrift/generator/test_data_generator/struct.ex index 2d6eb012..f1c4c8fb 100644 --- a/lib/thrift/generator/test_data_generator/struct.ex +++ b/lib/thrift/generator/test_data_generator/struct.ex @@ -45,14 +45,32 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do quote do defmodule unquote(test_data_module_name) do use PropCheck + alias Thrift.Generator.TestDataGenerator - def get_generator(context \\ nil, props \\ []) do + def get_generator(context \\ [], props \\ []) do + ctx = Enum.map(context, &handler_module_from_context/1) + + f = + TestDataGenerator.find_first_realization( + ctx, + {:get_generator, 2}, + &get_default_generator/2 + ) + + f.(context, props) + end + + def get_default_generator(context \\ nil, props \\ []) do unquote(gen) end def apply_defaults(struct_, context \\ nil) do unquote(gen_replace) end + + defp handler_module_from_context(context) do + Module.concat(context, unquote(name)) + end end end end @@ -131,4 +149,5 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do end end end + end From 5532a0d6938ad42ed7f2f2e05ac64f046cf7bd2e Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Wed, 16 Dec 2020 20:28:06 +0300 Subject: [PATCH 28/32] refinement for option fields in struct only affect test_data --- lib/mix/tasks/compile.thrift.ex | 22 +++----- lib/mix/tasks/thrift.generate.ex | 11 +++- lib/thrift/generator.ex | 19 ------- .../generator/binary/framed/http/server.ex | 1 - lib/thrift/generator/service.ex | 2 - lib/thrift/generator/test_data_generator.ex | 51 +++++++++++-------- .../generator/test_data_generator/struct.ex | 16 ++++-- lib/thrift/parser/file_group.ex | 6 ++- 8 files changed, 65 insertions(+), 63 deletions(-) diff --git a/lib/mix/tasks/compile.thrift.ex b/lib/mix/tasks/compile.thrift.ex index c2416845..016223ee 100644 --- a/lib/mix/tasks/compile.thrift.ex +++ b/lib/mix/tasks/compile.thrift.ex @@ -64,12 +64,13 @@ defmodule Mix.Tasks.Compile.Thrift do config = Keyword.get(Mix.Project.config(), :thrift, []) input_files = Keyword.get(config, :files, []) output_path = Keyword.get(config, :output_path, "lib") - output_test_data = Keyword.get( - config, - :output_test_data, - "test/test_data/" - )# |> IO.inspect(label: "output test data") + output_test_data = + Keyword.get( + config, + :output_test_data, + "test/test_data/" + ) parser_opts = config @@ -127,13 +128,11 @@ defmodule Mix.Tasks.Compile.Thrift do @spec extract_targets([FileGroup.t()], {Path.t(), Path.t()}, boolean) :: mappings defp extract_targets(groups, {output_path, _}, force) when is_list(groups) do for %FileGroup{initial_file: file} = group <- groups do - # IO.puts("ololo") targets = group - # |> IO.inspect(label: "group for extract") |> Thrift.Generator.targets() |> Enum.map(&Path.join(output_path, &1)) - # IO.puts("keke") + if force || Mix.Utils.stale?([file], targets) do {:stale, group, targets} else @@ -149,7 +148,7 @@ defmodule Mix.Tasks.Compile.Thrift do verbose = opts[:verbose] # Load the list of previously-generated files. - previous = read_manifest(manifest |> IO.inspect(label: "manifest")) |> IO.inspect(label: "previous") + previous = read_manifest(manifest) # Determine which of our current targets are in need of (re)generation. stale = for {:stale, group, targets} <- mappings, do: {group, targets} @@ -173,15 +172,10 @@ defmodule Mix.Tasks.Compile.Thrift do unless Enum.empty?(stale) do Mix.Utils.compiling_n(length(stale), :thrift) - # IO.inspect(stale, lable: "stale") - Enum.each(stale, fn {group, _targets} -> - # IO.inspect(output, label: "output") Thrift.Generator.generate!(group, output) - IO.puts("after gen") verbose && Mix.shell().info("Compiled #{group.initial_file}") end) - end # Update and rewrite the manifest. diff --git a/lib/mix/tasks/thrift.generate.ex b/lib/mix/tasks/thrift.generate.ex index 60e2e76a..c4d1c78d 100644 --- a/lib/mix/tasks/thrift.generate.ex +++ b/lib/mix/tasks/thrift.generate.ex @@ -56,13 +56,19 @@ defmodule Mix.Tasks.Thrift.Generate do {opts, files} = OptionParser.parse!( args, - switches: [include: :keep, namespace: :string, out: :string, out_test: :string, verbose: :boolean], + switches: [ + include: :keep, + namespace: :string, + out: :string, + out_test: :string, + verbose: :boolean + ], aliases: [I: :include, o: :out, v: :verbose] ) config = Keyword.get(Mix.Project.config(), :thrift, []) output_path = opts[:out] || Keyword.get(config, :output_path, "lib") - output_test_data = opts[:out_test] || Keyword.get(config, :output_test_data, "lib") #|> IO.inspect(label: "test_data") + output_test_data = opts[:out_test] || Keyword.get(config, :output_test_data, "lib") namespace = opts[:namespace] || Keyword.get(config, :namespace) @@ -74,6 +80,7 @@ defmodule Mix.Tasks.Thrift.Generate do Keyword.new() |> Keyword.put(:include_paths, include_paths) |> Keyword.put(:namespace, namespace) + unless Enum.empty?(files) do File.mkdir_p!(output_path) Enum.each(files, &generate!(&1, output_path, parser_opts, opts)) diff --git a/lib/thrift/generator.ex b/lib/thrift/generator.ex index d56e3375..10f65dc6 100644 --- a/lib/thrift/generator.ex +++ b/lib/thrift/generator.ex @@ -27,8 +27,6 @@ defmodule Thrift.Generator do |> generate_schema |> hd() |> Enum.map(fn {name, _} -> target_path(name) end) - - # |> IO.inspect(label: "target paths") end) end @@ -43,8 +41,6 @@ defmodule Thrift.Generator do end def generate!(%FileGroup{} = file_group, [_output_dir, _output_test_data] = output) do - IO.inspect(file_group.schemas, label: "fg") - Enum.flat_map(file_group.schemas, fn {_, schema} -> schema |> Map.put(:file_group, file_group) @@ -67,7 +63,6 @@ defmodule Thrift.Generator do def generate_schema(schema) do current_module_file_group = FileGroup.set_current_module(schema.file_group, schema.module) schema = %Schema{schema | file_group: current_module_file_group} - IO.inspect(schema, label: "schema") modules = List.flatten([ @@ -185,26 +180,12 @@ defmodule Thrift.Generator do typedef = key |> Atom.to_string() |> String.capitalize() name = "#{thrift_module}.#{typedef}" |> String.to_atom() - # typedef_name = - # Atom.to_string(schema.module) <> - # "." <> (key |> Atom.to_string() |> String.capitalize()) - - # IO.inspect(schema, limit: 10, label: "schema") - - # IO.inspect(ss) FileGroup.dest_module(schema.file_group, name) - # |> IO.inspect(label: "full_name") - _otherwise -> FileGroup.dest_module(schema.file_group, struct) end - # if label == :typedef do - # IO.inspect(ss) - # end - # full_name = FileGroup.dest_module(schema.file_group, struct) - # |> IO.inspect() full_test_data_name = TestDataGenerator.test_data_module_from_data_module(full_name) {full_test_data_name, TestDataGenerator.generate(label, schema, full_name, struct)} diff --git a/lib/thrift/generator/binary/framed/http/server.ex b/lib/thrift/generator/binary/framed/http/server.ex index f816c204..c160d595 100644 --- a/lib/thrift/generator/binary/framed/http/server.ex +++ b/lib/thrift/generator/binary/framed/http/server.ex @@ -40,7 +40,6 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Server do {:ok, payload, conn} <- Plug.Conn.read_body(conn), {:ok, parsed} <- Protocol.Binary.deserialize(:message_begin, payload) ) do - IO.inspect(parsed) handle_thrift_message(conn, parsed, opts) else {:error, _} -> send_resp(conn, 500, "bad thrift") diff --git a/lib/thrift/generator/service.ex b/lib/thrift/generator/service.ex index 1e4989c1..83feb294 100644 --- a/lib/thrift/generator/service.ex +++ b/lib/thrift/generator/service.ex @@ -12,8 +12,6 @@ defmodule Thrift.Generator.Service do @dialyzer [{:nowarn_function, generate: 2}, {:nowarn_function, generate_response_struct: 2}] def generate(schema, service) do - # IO.inspect(schema, label: "SCHEMA") - # IO.inspect(service, label: "SERVICE") file_group = schema.file_group dest_module = FileGroup.dest_module(file_group, service) diff --git a/lib/thrift/generator/test_data_generator.ex b/lib/thrift/generator/test_data_generator.ex index f1a31399..81e9248c 100644 --- a/lib/thrift/generator/test_data_generator.ex +++ b/lib/thrift/generator/test_data_generator.ex @@ -22,6 +22,7 @@ defmodule Thrift.Generator.TestDataGenerator do end def get_generator(type, file_group, annotations \\ %{}) + def get_generator(:bool, _, _) do quote do bool() @@ -56,6 +57,7 @@ defmodule Thrift.Generator.TestDataGenerator do def get_generator(:i16, _, annotations) do min = refinement_from_annotations(annotations, :min, &parse_integer/1, -32_768) max = refinement_from_annotations(annotations, :max, &parse_integer/1, 32_767) + quote do integer(unquote(min), unquote(max)) end @@ -64,14 +66,19 @@ defmodule Thrift.Generator.TestDataGenerator do def get_generator(:i32, _, annotations) do min = refinement_from_annotations(annotations, :min, &parse_integer/1, -2_147_483_648) max = refinement_from_annotations(annotations, :max, &parse_integer/1, 2_147_483_647) + quote do integer(unquote(min), unquote(max)) end end def get_generator(:i64, _, annotations) do - min = refinement_from_annotations(annotations, :min, &parse_integer/1, -9_223_372_036_854_775_808) - max = refinement_from_annotations(annotations, :max, &parse_integer/1, 9_223_372_036_854_775_807) + min = + refinement_from_annotations(annotations, :min, &parse_integer/1, -9_223_372_036_854_775_808) + + max = + refinement_from_annotations(annotations, :max, &parse_integer/1, 9_223_372_036_854_775_807) + quote do integer(unquote(min), unquote(max)) end @@ -80,6 +87,7 @@ defmodule Thrift.Generator.TestDataGenerator do def get_generator(:double, _, annotations) do min = refinement_from_annotations(annotations, :min, &parse_float/1, :inf) max = refinement_from_annotations(annotations, :max, &parse_float/1, :inf) + quote do float(unquote(min), unquote(max)) end @@ -104,15 +112,14 @@ defmodule Thrift.Generator.TestDataGenerator do end def get_generator({:map, {k, v}}, file_group, annotations) do + separate = fn {k, v} -> + [type, key] = + k + |> Atom.to_string() + |> String.split("_", parts: 2) - separate = - fn {k, v} -> - [type, key] = - k - |> Atom.to_string() - |> String.split("_", parts: 2) - {type, {String.to_atom(key), v}} - end + {type, {String.to_atom(key), v}} + end separated_annotations = annotations @@ -162,6 +169,7 @@ defmodule Thrift.Generator.TestDataGenerator do |> test_data_module_from_data_module props = gen_props(annotations) + quote do unquote(dest_module).get_generator(context, unquote(props)) end @@ -182,19 +190,20 @@ defmodule Thrift.Generator.TestDataGenerator do FileGroup.dest_module(file_group, name) |> test_data_module_from_data_module - props = gen_props(annotations) + quote do unquote(dest_module).get_generator(context, unquote(props)) end end - def get_generator(%Typedef{name: name}, file_group, annotations) do + def get_generator(%Typedef{} = typedef, file_group, annotations) do dest_module = - FileGroup.dest_module(file_group, name) + FileGroup.dest_module(file_group, typedef) |> test_data_module_from_data_module props = gen_props(annotations) + quote do unquote(dest_module).get_generator(context, unquote(props)) end @@ -207,6 +216,7 @@ defmodule Thrift.Generator.TestDataGenerator do def apply_defaults(struct_) do apply_defaults(struct_, nil) end + def apply_defaults(struct_, context) when is_struct(struct_) do module_name = struct_.__struct__ @@ -247,12 +257,12 @@ defmodule Thrift.Generator.TestDataGenerator do defp refinement_from_annotations(annotations, field, parse_fun, default) do annotations - |> Map.get(field) - |> case do - nil -> default - "^" <> v -> annotation_to_pin(v) - v -> parse_fun.(v) - end + |> Map.get(field) + |> case do + nil -> default + "^" <> v -> annotation_to_pin(v) + v -> parse_fun.(v) + end end defp parse_integer(str) do @@ -268,6 +278,7 @@ defmodule Thrift.Generator.TestDataGenerator do anno |> String.to_atom() |> Macro.var(nil) + quote do ^unquote(var_name) end @@ -280,6 +291,7 @@ defmodule Thrift.Generator.TestDataGenerator do "^" <> v -> annotation_to_pin(v) v -> v end + {k, v} end @@ -291,5 +303,4 @@ defmodule Thrift.Generator.TestDataGenerator do [unquote_splicing(props_from_annotations)] end end - end diff --git a/lib/thrift/generator/test_data_generator/struct.ex b/lib/thrift/generator/test_data_generator/struct.ex index f1c4c8fb..6609c970 100644 --- a/lib/thrift/generator/test_data_generator/struct.ex +++ b/lib/thrift/generator/test_data_generator/struct.ex @@ -82,11 +82,20 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do end def gen_draw(field_ast, file_group) do - # IO.inspect(field_ast.annotations, label: "anno") - generator = TestDataGenerator.get_generator(field_ast.type, file_group, field_ast.annotations) + annotations = field_ast.annotations + draw_anno = Map.drop(annotations, [:act]) + generator = TestDataGenerator.get_generator(field_ast.type, file_group, draw_anno) + + declared_required = + case field_ast.required do + :default -> true + otherwise -> otherwise + end + + actually_required = Map.get(field_ast.annotations, :act) == "required" generator = - if field_ast.required do + if declared_required or actually_required do generator else quote do @@ -149,5 +158,4 @@ defmodule Thrift.Generator.TestDataGenerator.Struct do end end end - end diff --git a/lib/thrift/parser/file_group.ex b/lib/thrift/parser/file_group.ex index a577fbc2..0a249b9a 100644 --- a/lib/thrift/parser/file_group.ex +++ b/lib/thrift/parser/file_group.ex @@ -180,6 +180,10 @@ defmodule Thrift.Parser.FileGroup do dest_module(file_group, name) end + def dest_module(file_group, %Typedef{name: name}) do + dest_module(file_group, name) + end + def dest_module(file_group, Constant) do # Default to naming the constants module after the namespaced, camelized # basename of its file. For foo.thrift, this would be `foo.Foo`. @@ -223,7 +227,7 @@ defmodule Thrift.Parser.FileGroup do struct_name = name_parts |> Enum.at(1) - |> initialcase() + |> Macro.camelize() case file_group.namespaces[module_name] do nil -> From 035f9ba1f9cc2d41acb876fbe89e2adc8ef64682 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Thu, 4 Feb 2021 17:16:30 +0300 Subject: [PATCH 29/32] WIP: pass headers in handlers --- lib/thrift/generator/binary/framed/http/server.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/thrift/generator/binary/framed/http/server.ex b/lib/thrift/generator/binary/framed/http/server.ex index c160d595..a4f21580 100644 --- a/lib/thrift/generator/binary/framed/http/server.ex +++ b/lib/thrift/generator/binary/framed/http/server.ex @@ -141,6 +141,7 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Server do post unquote(func_path) do [handler_module] = opts body = conn.body_params + headers = conn.req_headers encoded_response = with( @@ -154,7 +155,7 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Server do apply( handler_module, unquote(handle), - [unquote_splicing(handler_args)] + [unquote_splicing(handler_args), headers] ), response = %unquote(response_module){success: result} ) do From 63d573b5f00bb1f9235bec3780956c5cbc053f47 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Mon, 8 Feb 2021 17:37:34 +0300 Subject: [PATCH 30/32] Pass opts in thrift client --- lib/thrift/generator/binary/framed/http/client.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/thrift/generator/binary/framed/http/client.ex b/lib/thrift/generator/binary/framed/http/client.ex index 819b9779..1c4b2fac 100644 --- a/lib/thrift/generator/binary/framed/http/client.ex +++ b/lib/thrift/generator/binary/framed/http/client.ex @@ -43,13 +43,15 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Client do end) quote do - def unquote(func_name)(thrift_url, unquote_splicing(function_args)) do + def unquote(func_name)(thrift_url, unquote_splicing(function_args), opts \\ []) do args = %unquote(args_module){unquote_splicing(assignments)} serialized_args = unquote(args_binary_module).serialize(args) header = Binary.serialize(:message_begin, {:call, 0, unquote(s_func_name)}) payload = [header | serialized_args] |> IO.iodata_to_binary() - {:ok, response} = HTTPoison.post(thrift_url, payload, [], hackney: [pool: false]) + opts = Keyword.merge([hackney: [pool: false]], opts) + + {:ok, response} = HTTPoison.post(thrift_url, payload, [], opts) result = with( From 0d83a1868241d09415ed3676be7b4a13a09f19a6 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Mon, 8 Feb 2021 18:44:56 +0300 Subject: [PATCH 31/32] Headers now in proper place >_> --- lib/thrift/generator/binary/framed/http/client.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/thrift/generator/binary/framed/http/client.ex b/lib/thrift/generator/binary/framed/http/client.ex index 1c4b2fac..c1a351e4 100644 --- a/lib/thrift/generator/binary/framed/http/client.ex +++ b/lib/thrift/generator/binary/framed/http/client.ex @@ -49,9 +49,10 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Client do header = Binary.serialize(:message_begin, {:call, 0, unquote(s_func_name)}) payload = [header | serialized_args] |> IO.iodata_to_binary() + {http_headers, opts} = Keyword.pop(opts, :headers, []) opts = Keyword.merge([hackney: [pool: false]], opts) - {:ok, response} = HTTPoison.post(thrift_url, payload, [], opts) + {:ok, response} = HTTPoison.post(thrift_url, payload, http_headers, opts) result = with( From 27ba61743248789e5d3822f34433c27dd4d24407 Mon Sep 17 00:00:00 2001 From: Alexsandr Tortsev Date: Wed, 17 Mar 2021 17:53:56 +0300 Subject: [PATCH 32/32] Pass thrift errors --- lib/thrift/generator/binary/framed/http/client.ex | 5 +++-- lib/thrift/utils.ex | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 lib/thrift/utils.ex diff --git a/lib/thrift/generator/binary/framed/http/client.ex b/lib/thrift/generator/binary/framed/http/client.ex index c1a351e4..4ce9c38c 100644 --- a/lib/thrift/generator/binary/framed/http/client.ex +++ b/lib/thrift/generator/binary/framed/http/client.ex @@ -57,9 +57,10 @@ defmodule Thrift.Generator.Binary.Framed.HTTP.Client do result = with( {:ok, {:reply, 0, unquote(s_func_name), rest}} <- Binary.deserialize(:message_begin, response.body), - {%unquote(response_module){success: s}, ""} <- unquote(response_binary_module).deserialize(rest) + # {%unquote(response_module){success: s}, ""} <- unquote(response_binary_module).deserialize(rest) + {%unquote(response_module){} = r, ""} <- unquote(response_binary_module).deserialize(rest) ) do - {:ok, s} + Thrift.Utils.Web.normalize_q_result(r) end {response, result} end diff --git a/lib/thrift/utils.ex b/lib/thrift/utils.ex new file mode 100644 index 00000000..a36acdfc --- /dev/null +++ b/lib/thrift/utils.ex @@ -0,0 +1,15 @@ +defmodule Thrift.Utils.Web do + + def normalize_q_result(result) do + result + |> Map.from_struct() + |> Enum.find({:success, nil}, fn {_k, v} -> v != nil end) + |> case do + {:success, r} -> {:ok, r} + {_f, err} -> {:error, err} + end + + + end + +end