From a8a66c65612ee995ac692e6841db3b60fdb75b73 Mon Sep 17 00:00:00 2001 From: SpaceEEC Date: Tue, 2 Apr 2019 19:40:36 +0200 Subject: [PATCH] feat: add credo --- .credo.exs | 87 ++++++++++ .travis.yml | 1 + lib/rest.ex | 16 +- lib/rest/api_error.ex | 14 +- lib/rest/cdn.ex | 2 +- lib/rest/functions.ex | 294 ++++++++++++++++++++++++--------- lib/rest/handler/global.ex | 2 + lib/rest/handler/handler.ex | 28 ++-- lib/rest/handler/state.ex | 6 +- lib/rest/handler/supervisor.ex | 3 + lib/rest/http.ex | 14 +- lib/rest/request.ex | 3 +- lib/rest/util.ex | 6 +- mix.exs | 3 +- mix.lock | 3 + 15 files changed, 369 insertions(+), 113 deletions(-) create mode 100644 .credo.exs diff --git a/.credo.exs b/.credo.exs new file mode 100644 index 0000000..2cd1182 --- /dev/null +++ b/.credo.exs @@ -0,0 +1,87 @@ +%{ + configs: [ + %{ + name: "default", + files: %{ + included: ["lib/rest"], + excluded: ["lib/rest/gen"] + }, + requires: [], + checks: [ + # Consistency + {Credo.Check.Consistency.ExceptionNames}, + {Credo.Check.Consistency.LineEndings}, + {Credo.Check.Consistency.MultiAliasImportRequireUse}, + {Credo.Check.Consistency.ParameterPatternMatching}, + {Credo.Check.Consistency.SpaceAroundOperators}, + {Credo.Check.Consistency.SpaceInParentheses}, + {Credo.Check.Consistency.TabsOrSpaces}, + # Design + {Credo.Check.Design.AliasUsage}, + {Credo.Check.Design.DuplicatedCode}, + {Credo.Check.Design.TagFIXME}, + {Credo.Check.Design.TagTODO, exit_status: 0}, + # Readability + {Credo.Check.Readability.AliasOrder}, + {Credo.Check.Readability.FunctionNames}, + {Credo.Check.Readability.LargeNumbers}, + {Credo.Check.Readability.MaxLineLength}, + {Credo.Check.Readability.ModuleAttributeNames}, + {Credo.Check.Readability.ModuleDoc}, + {Credo.Check.Readability.ModuleNames}, + {Credo.Check.Readability.ParenthesesOnZeroArityDefs, false}, + {Credo.Check.Readability.PredicateFunctionNames}, + {Credo.Check.Readability.PreferImplicitTry, false}, + {Credo.Check.Readability.PreferUnquotedAtoms}, + {Credo.Check.Readability.RedundantBlankLines}, + {Credo.Check.Readability.Semicolons}, + {Credo.Check.Readability.SinglePipe}, + {Credo.Check.Readability.SpaceAfterCommas}, + {Credo.Check.Readability.Specs}, + {Credo.Check.Readability.StringSigils}, + {Credo.Check.Readability.TrailingBlankLine}, + {Credo.Check.Readability.TrailingWhiteSpace}, + {Credo.Check.Readability.UnnecessaryAliasExpansion}, + {Credo.Check.Readability.VariableNames}, + # Refactor + {Credo.Check.Refactor.ABCSize, false}, + {Credo.Check.Refactor.AppendSingleItem}, + {Credo.Check.Refactor.CaseTrivialMatches}, + {Credo.Check.Refactor.CondStatements}, + {Credo.Check.Refactor.CyclomaticComplexity}, + {Credo.Check.Refactor.DoubleBooleanNegation}, + {Credo.Check.Refactor.FunctionArity}, + {Credo.Check.Refactor.LongQuoteBlocks, false}, + {Credo.Check.Refactor.MapInto}, + {Credo.Check.Refactor.MatchInCondition}, + {Credo.Check.Refactor.ModuleDependencies, false}, + {Credo.Check.Refactor.NegatedConditionsInUnless}, + {Credo.Check.Refactor.NegatedConditionsWithElse}, + {Credo.Check.Refactor.Nesting}, + {Credo.Check.Refactor.PerceivedComplexity}, + {Credo.Check.Refactor.PipeChainStart}, + {Credo.Check.Refactor.UnlessWithElse}, + {Credo.Check.Refactor.VariableRebinding, false}, + # Warning + {Credo.Check.Warning.BoolOperationOnSameValues}, + {Credo.Check.Warning.ExpensiveEmptyEnumCheck}, + {Credo.Check.Warning.IExPry}, + {Credo.Check.Warning.IoInspect}, + {Credo.Check.Warning.LazyLogging}, + {Credo.Check.Warning.MapGetUnsafePass}, + {Credo.Check.Warning.OperationOnSameValues}, + {Credo.Check.Warning.OperationWithConstantResult}, + {Credo.Check.Warning.RaiseInsideRescue}, + {Credo.Check.Warning.UnsafeToAtom, false}, + {Credo.Check.Warning.UnusedEnumOperation}, + {Credo.Check.Warning.UnusedFileOperation}, + {Credo.Check.Warning.UnusedKeywordOperation}, + {Credo.Check.Warning.UnusedListOperation}, + {Credo.Check.Warning.UnusedPathOperation}, + {Credo.Check.Warning.UnusedRegexOperation}, + {Credo.Check.Warning.UnusedStringOperation}, + {Credo.Check.Warning.UnusedTupleOperation} + ] + } + ] +} diff --git a/.travis.yml b/.travis.yml index 316345e..7469718 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ matrix: otp_release: '21.0.8' script: - mix format --check-formatted + - mix credo --strict - mix test - language: elixir diff --git a/lib/rest.ex b/lib/rest.ex index 4d28248..978271a 100644 --- a/lib/rest.ex +++ b/lib/rest.ex @@ -1435,11 +1435,17 @@ defmodule Crux.Rest do """ Version.since("0.2.0") + @callback execute_webhook( + webhook :: Webhook.t(), + data :: Crux.Rest.execute_webhook_options() + ) :: :ok + Version.since("0.2.0") + @callback execute_webhook( webhook :: Webhook.t(), wait :: boolean() | nil, data :: Crux.Rest.execute_webhook_options() - ) :: :ok | {:ok, Message.t()} | {:error, term} + ) :: :ok | {:ok, Message.t()} | {:error, term()} Version.since("0.2.0") @callback execute_webhook( @@ -1458,6 +1464,9 @@ defmodule Crux.Rest do """ Version.since("0.2.0") + @callback execute_slack_webhook(webhook :: Webhook.t(), data :: term()) :: :ok + Version.since("0.2.0") + @callback execute_slack_webhook( webhook :: Webhook.t(), wait :: boolean() | nil, @@ -1483,6 +1492,11 @@ defmodule Crux.Rest do """ Version.since("0.2.0") + @callback execute_github_webhook(webhook :: Webhook.t(), event :: String.t(), data :: term()) :: + :ok + + Version.since("0.2.0") + @callback execute_github_webhook( webhook :: Webhook.t(), event :: String.t(), diff --git a/lib/rest/api_error.ex b/lib/rest/api_error.ex index 618ebb7..e027150 100644 --- a/lib/rest/api_error.ex +++ b/lib/rest/api_error.ex @@ -82,12 +82,14 @@ defmodule Crux.Rest.ApiError do defp map_inner(error, key) when is_map(error) do Enum.map_join(error, "\n", fn {k, v} -> - cond do - key && Regex.match?(~r/\d+/, k) -> "#{key}[#{k}]" - key -> "#{key}.#{k}" - true -> k - end - |> transform_value(v) + new_k = + cond do + key && Regex.match?(~r/\d+/, k) -> "#{key}[#{k}]" + key -> "#{key}.#{k}" + true -> k + end + + transform_value(new_k, v) end) end diff --git a/lib/rest/cdn.ex b/lib/rest/cdn.ex index 30a029f..0265c48 100644 --- a/lib/rest/cdn.ex +++ b/lib/rest/cdn.ex @@ -250,7 +250,7 @@ defmodule Crux.Rest.CDN do def default_user_avatar(user) def default_user_avatar(%{discriminator: discrim}) do - user_discriminator = String.to_integer(discrim) |> rem(5) + user_discriminator = discrim |> String.to_integer() |> rem(5) "#{@base_url}/embed/avatars/#{user_discriminator}.png" end diff --git a/lib/rest/functions.ex b/lib/rest/functions.ex index a9d16e6..13230fa 100644 --- a/lib/rest/functions.ex +++ b/lib/rest/functions.ex @@ -11,23 +11,25 @@ defmodule Crux.Rest.Functions do @behaviour Crux.Rest alias Crux.Rest.{Endpoints, Request, Util} - alias Crux.Structs alias Crux.Structs.{ AuditLog, - Guild, Channel, - Message, Emoji, - User, + Guild, Invite, Member, + Message, Role, + User, Webhook } + alias Crux.Structs + ### Message + @impl true def create_message(channel_or_message, data) do channel_id = Util.resolve_channel_id(channel_or_message) @@ -38,11 +40,13 @@ defmodule Crux.Rest.Functions do |> Map.new() |> Util.resolve_multipart() - Request.new(:post, path, data) + :post + |> Request.new(path, data) |> Request.set_headers(disposition) |> Request.set_transform(Message) end + @impl true def get_message(message_or_channel, data_or_channel \\ [], data \\ []) def get_message(%{channel_id: channel_id, id: message_id}, data, _) do @@ -52,8 +56,8 @@ defmodule Crux.Rest.Functions do def get_message(channel, message, data) do data = Keyword.new(data) - common_message( - :get, + :get + |> common_message( channel, message, &Endpoints.channel_messages/2, @@ -63,24 +67,28 @@ defmodule Crux.Rest.Functions do |> Request.set_transform(Message) end + @impl true def get_messages(channel, data) do channel_id = Util.resolve_channel_id(channel) path = Endpoints.channel_messages(channel_id) data = Keyword.new(data) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_params(data) |> Request.set_transform(&Structs.Util.raw_data_to_map(&1, Message)) end + @impl true def edit_message(%{channel_id: channel_id, id: message_id}, data) do edit_message(channel_id, message_id, data) end + @impl true def edit_message(channel, message, data) do - common_message( - :patch, + :patch + |> common_message( channel, message, &Endpoints.channel_messages/2, @@ -89,13 +97,15 @@ defmodule Crux.Rest.Functions do |> Request.set_transform(Message) end + @impl true def delete_message(%{channel_id: channel_id, id: message_id}) do delete_message(channel_id, message_id) end + @impl true def delete_message(channel, message) do - common_message( - :delete, + :delete + |> common_message( channel, message, &Endpoints.channel_messages/2 @@ -106,6 +116,7 @@ defmodule Crux.Rest.Functions do |> Map.update!(:route, &Kernel.<>(&1, "/delete")) end + @impl true def delete_messages(channel, messages) do channel_id = Util.resolve_channel_id(channel) message_ids = Enum.map(messages, &Util.resolve_message_id/1) @@ -116,10 +127,12 @@ defmodule Crux.Rest.Functions do Request.new(:post, path, data) end + @impl true def add_pinned_message(%{channel_id: channel_id, id: message_id}) do add_pinned_message(channel_id, message_id) end + @impl true def add_pinned_message(channel, message) do common_message( :put, @@ -129,10 +142,12 @@ defmodule Crux.Rest.Functions do ) end + @impl true def delete_pinned_message(%{channel_id: channel_id, id: message_id}) do delete_pinned_message(channel_id, message_id) end + @impl true def delete_pinned_message(channel, message) do common_message( :delete, @@ -142,12 +157,14 @@ defmodule Crux.Rest.Functions do ) end + @impl true def get_pinned_messages(channel) do channel_id = Util.resolve_channel_id(channel) path = Endpoints.channel_pins(channel_id) - Request.new(:get, path, channel_id) + :get + |> Request.new(path, channel_id) |> Request.set_transform(&Structs.Util.raw_data_to_map(&1, Message)) end @@ -157,13 +174,15 @@ defmodule Crux.Rest.Functions do ### Reaction + @impl true def create_reaction(%{channel_id: channel_id, id: message_id}, emoji) do create_reaction(channel_id, message_id, emoji) end + @impl true def create_reaction(channel, message, emoji) do - common_message( - :put, + :put + |> common_message( channel, message, &Endpoints.message_reactions(&1, &2, emoji, "@me") @@ -172,6 +191,7 @@ defmodule Crux.Rest.Functions do |> Request.set_rate_limit_reset(250) end + @impl true def get_reactions( channel_or_message, emoji_or_message_id, @@ -186,8 +206,8 @@ defmodule Crux.Rest.Functions do def get_reactions(channel, message, emoji, data) do data = Keyword.new(data) - common_message( - :get, + :get + |> common_message( channel, message, &Endpoints.message_reactions(&1, &2, emoji) @@ -196,6 +216,7 @@ defmodule Crux.Rest.Functions do |> Request.set_transform(&Structs.Util.raw_data_to_map(&1, User)) end + @impl true def delete_reaction( message_or_channel, emoji_or_message_id, @@ -210,11 +231,7 @@ defmodule Crux.Rest.Functions do def delete_reaction(channel, message, emoji, user) do user = Util.resolve_user_id(user) - emoji = - emoji - |> Emoji.to_identifier() - # TODO: Is this necessary? - |> URI.encode_www_form() + emoji = Emoji.to_identifier(emoji) common_message( :delete, @@ -224,16 +241,14 @@ defmodule Crux.Rest.Functions do ) end + @impl true def delete_all_reactions(%{channel_id: channel_id, id: message_id}, emoji) do delete_all_reactions(channel_id, message_id, emoji) end + @impl true def delete_all_reactions(channel, message, emoji) do - emoji = - emoji - |> Emoji.to_identifier() - # TODO: Is this necessary? - |> URI.encode_www_form() + emoji = Emoji.to_identifier(emoji) common_message( :delete, @@ -247,6 +262,7 @@ defmodule Crux.Rest.Functions do ### Channel + @impl true def trigger_typing(channel) do channel_id = Util.resolve_channel_id(channel) @@ -255,15 +271,18 @@ defmodule Crux.Rest.Functions do Request.new(:post, path) end + @impl true def get_channel(channel) do channel_id = Util.resolve_channel_id(channel) path = Endpoints.channel(channel_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(Channel) end + @impl true def modify_channel(channel, data) do channel_id = Util.resolve_channel_id(channel) @@ -274,20 +293,24 @@ defmodule Crux.Rest.Functions do |> Map.new() |> Util.resolve_image_in_map(:icon) - Request.new(:path, path, data) + :path + |> Request.new(path, data) |> Request.set_transform(Channel) end + @impl true def delete_channel(channel, reason \\ nil) do channel_id = Util.resolve_channel_id(channel) path = Endpoints.channel(channel_id) - Request.new(:delete, path) + :delete + |> Request.new(path) |> Request.set_transform(Channel) |> Request.set_reason(reason) end + @impl true def edit_channel_permissions(channel, target, data) when is_map(target) do channel_id = Util.resolve_channel_id(channel) @@ -310,32 +333,38 @@ defmodule Crux.Rest.Functions do Request.new(:put, path, data) end + @impl true def delete_channel_permissions(channel, target, reason \\ nil) do channel_id = Util.resolve_channel_id(channel) {_type, target_id} = Util.resolve_overwrite_target(target) path = Endpoints.channel_permissions(channel_id, target_id) - Request.new(:delete, path) + :delete + |> Request.new(path) |> Request.set_reason(reason) end + @impl true def get_channel_invites(channel) do channel_id = Util.resolve_channel_id(channel) path = Endpoints.channel_invites(channel_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(Invite) end + @impl true def create_channel_invite(channel, data) do channel_id = Util.resolve_channel_id(channel) path = Endpoints.channel_invites(channel_id) data = Map.new(data) - Request.new(:post, path, data) + :post + |> Request.new(path, data) |> Request.set_transform(Invite) end @@ -343,25 +372,30 @@ defmodule Crux.Rest.Functions do ### Emojis + @impl true def list_guild_emojis(guild) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_emojis(guild_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(&Structs.Util.raw_data_to_map(&1, Emoji)) end + @impl true def get_guild_emoji(guild, emoji) do guild_id = Util.resolve_guild_id(guild) emoji_id = Util.resolve_emoji_id(emoji) path = Endpoints.guild_emojis(guild_id, emoji_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(Emoji) end + @impl true def create_guild_emoji(guild, data) do guild_id = Util.resolve_guild_id(guild) @@ -377,10 +411,12 @@ defmodule Crux.Rest.Functions do Map.put(data, :roles, roles) end - Request.new(:post, path, data) + :post + |> Request.new(path, data) |> Request.set_transform(Emoji) end + @impl true def modify_guild_emoji(guild, emoji, data) do guild_id = Util.resolve_guild_id(guild) emoji_id = Util.resolve_emoji_id(emoji) @@ -397,17 +433,20 @@ defmodule Crux.Rest.Functions do data end - Request.new(:patch, path, data) + :patch + |> Request.new(path, data) |> Request.set_transform(Emoji) end + @impl true def delete_guild_emoji(guild, emoji, reason \\ nil) do guild_id = Util.resolve_guild_id(guild) emoji_id = Util.resolve_emoji_id(emoji) path = Endpoints.guild_emojis(guild_id, emoji_id) - Request.new(:delete, path) + :delete + |> Request.new(path) |> Request.set_reason(reason) end @@ -415,6 +454,7 @@ defmodule Crux.Rest.Functions do ### Guild + @impl true def create_guild(data) do path = Endpoints.guild() @@ -423,19 +463,23 @@ defmodule Crux.Rest.Functions do |> Map.new() |> Util.resolve_image_in_map(:icon) - Request.new(:post, path, data) + :post + |> Request.new(path, data) |> Request.set_transform(Guild) end + @impl true def get_guild(guild) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild(guild_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(Guild) end + @impl true def modify_guild(guild, data) do guild_id = Util.resolve_guild_id(guild) @@ -448,10 +492,12 @@ defmodule Crux.Rest.Functions do |> Util.resolve_image_in_map(:splash) |> Util.resolve_image_in_map(:banner) - Request.new(:patch, path, data) + :patch + |> Request.new(path, data) |> Request.set_transform(Guild) end + @impl true def delete_guild(guild) do guild_id = Util.resolve_guild_id(guild) @@ -464,25 +510,30 @@ defmodule Crux.Rest.Functions do ### Guild Channel + @impl true def get_guild_channels(guild) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_channels(guild_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(&Structs.Util.raw_data_to_map(&1, Channel)) end + @impl true def create_guild_channel(guild, data) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_channels(guild_id) data = Map.new(data) - Request.new(:post, path, data) + :post + |> Request.new(path, data) |> Request.set_transform(Channel) end + @impl true def modify_guild_channel_positions(guild, channels) do guild_id = Util.resolve_guild_id(guild) @@ -492,32 +543,35 @@ defmodule Crux.Rest.Functions do Request.new(:patch, path, data) end - # TODO: Delete? - ### End Guild Channel ### Guild Member + @impl true def get_guild_member(guild, user) do guild_id = Util.resolve_guild_id(guild) user_id = Util.resolve_user_id(user) path = Endpoints.guild_members(guild_id, user_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(Member) end + @impl true def list_guild_members(guild, options) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_members(guild_id) data = Map.new(options) - Request.new(:get, path, data) + :get + |> Request.new(path, data) |> Request.set_transform(&Structs.Util.raw_data_to_map(&1, Member, :user)) end + @impl true def add_guild_member(guild, user, data) do guild_id = Util.resolve_guild_id(guild) user_id = Util.resolve_user_id(user) @@ -525,10 +579,12 @@ defmodule Crux.Rest.Functions do path = Endpoints.guild_members(guild_id, user_id) data = Map.new(data) - Request.new(:put, path, data) + :put + |> Request.new(path, data) |> Request.set_transform(Member) end + @impl true def modify_guild_member(guild, member, data) do guild_id = Util.resolve_guild_id(guild) user_id = Util.resolve_user_id(member) @@ -539,6 +595,7 @@ defmodule Crux.Rest.Functions do Request.new(:patch, path, data) end + @impl true def modify_current_users_nick(guild, nick, reason) do guild_id = Util.resolve_guild_id(guild) @@ -546,7 +603,8 @@ defmodule Crux.Rest.Functions do data = %{nick: nick} - Request.new(:patch, path, data) + :patch + |> Request.new(path, data) |> Request.set_reason(reason) end @@ -554,49 +612,55 @@ defmodule Crux.Rest.Functions do ### Guild Ban + @impl true def get_guild_bans(guild) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_bans(guild_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform( &Map.new(&1, fn %{user: user} = entry -> user = Structs.create(user, User) {user.id, %{entry | user: user}} end) ) - - # TODO, custom transform end + @impl true def get_guild_ban(guild, user) do guild_id = Util.resolve_guild_id(guild) user_id = Util.resolve_user_id(user) path = Endpoints.guild_bans(guild_id, user_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(&Map.update!(&1, :user, fn user -> Structs.create(user, User) end)) end + @impl true def create_guild_ban(guild, user, reason \\ nil) do guild_id = Util.resolve_guild_id(guild) user_id = Util.resolve_user_id(user) path = Endpoints.guild_bans(guild_id, user_id) - Request.new(:put, path) + :put + |> Request.new(path) |> Request.set_reason(reason) end + @impl true def remove_guild_ban(guild, user, reason \\ nil) do guild_id = Util.resolve_guild_id(guild) user_id = Util.resolve_user_id(user) path = Endpoints.guild_bans(guild_id, user_id) - Request.new(:delete, path) + :delete + |> Request.new(path) |> Request.set_reason(reason) end @@ -604,25 +668,30 @@ defmodule Crux.Rest.Functions do ### Guild Roles + @impl true def get_guild_roles(guild) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_roles(guild_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(&Structs.Util.raw_data_to_map(&1, Role)) end + @impl true def create_guild_role(guild, data) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_roles(guild_id) data = Map.new(data) - Request.new(:post, path, data) + :post + |> Request.new(path, data) |> Request.set_transform(Role) end + @impl true def add_guild_member_role(guild, member, role, reason) do guild_id = Util.resolve_guild_id(guild) user_id = Util.resolve_user_id(member) @@ -630,10 +699,12 @@ defmodule Crux.Rest.Functions do path = Endpoints.guild_member_roles(guild_id, user_id, role_id) - Request.new(:put, path) + :put + |> Request.new(path) |> Request.set_reason(reason) end + @impl true def remove_guild_member_role(guild, member, role, reason \\ nil) do guild_id = Util.resolve_guild_id(guild) user_id = Util.resolve_user_id(member) @@ -641,10 +712,12 @@ defmodule Crux.Rest.Functions do path = Endpoints.guild_member_roles(guild_id, user_id, role_id) - Request.new(:delete, path) + :delete + |> Request.new(path) |> Request.set_reason(reason) end + @impl true def modify_guild_role_positions(guild, data) do guild_id = Util.resolve_guild_id(guild) @@ -654,6 +727,7 @@ defmodule Crux.Rest.Functions do Request.new(:patch, path, data) end + @impl true def modify_guild_role(guild, role, data) do guild_id = Util.resolve_guild_id(guild) role_id = Util.resolve_role_id(role) @@ -661,17 +735,20 @@ defmodule Crux.Rest.Functions do path = Endpoints.guild_roles(guild_id, role_id) data = Map.new(data) - Request.new(:patch, path, data) + :patch + |> Request.new(path, data) |> Request.set_transform(Role) end + @impl true def delete_guild_role(guild, role, reason \\ nil) do guild_id = Util.resolve_guild_id(guild) role_id = Util.resolve_role_id(role) path = Endpoints.guild_roles(guild_id, role_id) - Request.new(:data, path) + :data + |> Request.new(path) |> Request.set_reason(reason) end @@ -679,23 +756,27 @@ defmodule Crux.Rest.Functions do ### Guild Prune + @impl true def get_guild_prune_count(guild, days) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_prune(guild_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_params(days: days) |> Request.set_transform(&Map.get(&1, :pruned)) end + @impl true def begin_guild_prune(guild, data) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_prune(guild_id) data = Keyword.new(data) - Request.new(:post, path) + :post + |> Request.new(path) |> Request.set_params(data) |> Request.set_transform(&Map.get(&1, :pruned)) end @@ -704,6 +785,7 @@ defmodule Crux.Rest.Functions do ### Guild Integration + @impl true def get_guild_integrations(guild) do guild_id = Util.resolve_guild_id(guild) @@ -712,6 +794,7 @@ defmodule Crux.Rest.Functions do Request.new(:get, path) end + @impl true def create_guild_integration(guild, data) do guild_id = Util.resolve_guild_id(guild) @@ -721,9 +804,9 @@ defmodule Crux.Rest.Functions do Request.new(:post, path, data) end + @impl true def modify_guild_integration(guild, integration, data) do guild_id = Util.resolve_guild_id(guild) - # TODO: Is this correct? integration_id = integration path = Endpoints.guild_integrations(guild_id, integration_id) @@ -732,6 +815,7 @@ defmodule Crux.Rest.Functions do Request.new(:patch, path, data) end + @impl true def delete_guild_integration(guild, integration) do guild_id = Util.resolve_guild_id(guild) integration_id = integration @@ -741,6 +825,7 @@ defmodule Crux.Rest.Functions do Request.new(:delete, path) end + @impl true def sync_guild_integration(guild, integration) do guild_id = Util.resolve_guild_id(guild) integration_id = integration @@ -754,6 +839,7 @@ defmodule Crux.Rest.Functions do ### Guild Embed + @impl true def get_guild_embed(guild) do guild_id = Util.resolve_guild_id(guild) @@ -762,6 +848,7 @@ defmodule Crux.Rest.Functions do Request.new(:get, path) end + @impl true def modify_guild_embed(guild, data) do guild_id = Util.resolve_guild_id(guild) @@ -775,21 +862,25 @@ defmodule Crux.Rest.Functions do ### Guild Invite + @impl true def get_guild_invites(guild) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_invites(guild_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(&Structs.Util.raw_data_to_map(&1, Invite, :code)) end + @impl true def get_guild_vanity_url(guild) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_invites(guild_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(&Map.get(&1, :code)) end @@ -797,33 +888,40 @@ defmodule Crux.Rest.Functions do ### Webhook + @impl true def list_guild_webhooks(guild) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_webhooks(guild_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(&Structs.Util.raw_data_to_map(&1, Webhook)) end + @impl true def list_channel_webhooks(channel) do channel_id = Util.resolve_channel_id(channel) path = Endpoints.channel_webhooks(channel_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(&Structs.Util.raw_data_to_map(&1, Webhook)) end + @impl true def get_webhook(user, token \\ nil) do user_id = Util.resolve_user_id(user) path = Endpoints.webhook(user_id, token) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(Webhook) end + @impl true def update_webhook(user, token \\ nil, data) do user_id = Util.resolve_user_id(user) @@ -834,10 +932,12 @@ defmodule Crux.Rest.Functions do |> Map.new() |> Util.resolve_image_in_map(:avatar) - Request.new(:patch, path, data) + :patch + |> Request.new(path, data) |> Request.set_transform(Webhook) end + @impl true def delete_webhook(user, token \\ nil) do user_id = Util.resolve_user_id(user) @@ -846,16 +946,20 @@ defmodule Crux.Rest.Functions do Request.new(:delete, path) end + @impl true def execute_webhook(%{id: id, token: token}, data) do execute_webhook(id, token, false, data) end + @impl true def execute_webhook(user, token, wait \\ false, data) + @impl true def execute_webhook(%{id: id, token: token}, wait, _, data) do execute_webhook(id, token, wait, data) end + @impl true def execute_webhook(user, token, wait, data) do user_id = Util.resolve_user_id(user) @@ -866,51 +970,61 @@ defmodule Crux.Rest.Functions do |> Map.new() |> Util.resolve_multipart() - Request.new(:post, path, data) + :post + |> Request.new(path, data) |> Request.set_headers(disposition) |> Request.set_params(wait: wait) |> Request.set_transform(Message) end + @impl true def execute_slack_webhook(%{id: id, token: token}, data) do execute_slack_webhook(id, token, false, data) end + @impl true def execute_slack_webhook(user, token, wait \\ false, data) + @impl true def execute_slack_webhook(%{id: id, token: token}, wait, _, data) do execute_slack_webhook(id, token, wait, data) end + @impl true def execute_slack_webhook(user, token, wait, data) do user_id = Util.resolve_user_id(user) path = Endpoints.webhook_slack(user_id, token) data = Map.new(data) - Request.new(:post, path, data) + :post + |> Request.new(path, data) |> Request.set_params(wait: wait) |> Request.set_transform(Message) end + @impl true def execute_github_webhook(%{id: id, token: token}, event, data) do execute_github_webhook(id, token, event, false, data) end + @impl true def execute_github_webhook(user, token, event, wait \\ false, data) + @impl true def execute_github_webhook(%{id: id, token: token}, event, wait, _, data) do execute_github_webhook(id, token, event, wait, data) end + @impl true def execute_github_webhook(user, token, event, wait, data) do user_id = Util.resolve_user_id(user) path = Endpoints.webhook_github(user_id, token) data = Map.new(data) - Request.new( - :post, + :post + |> Request.new( path, data ) @@ -923,17 +1037,20 @@ defmodule Crux.Rest.Functions do ### Guild Misc + @impl true def get_audit_logs(guild, data \\ []) do guild_id = Util.resolve_guild_id(guild) path = Endpoints.guild_audit_logs(guild_id) data = Map.new(data) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_params(data) |> Request.set_transform(AuditLog) end + @impl true def get_guild_voice_regions(guild) do guild_id = Util.resolve_guild_id(guild) @@ -942,31 +1059,38 @@ defmodule Crux.Rest.Functions do Request.new(:get, path) end + @impl true def get_invite(code) do path = Endpoints.invite(code) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(Invite) end def delete_invite(%{code: code}), do: delete_invite(code) + @impl true def delete_invite(code) do path = Endpoints.invite(code) - Request.new(:delete, path) + :delete + |> Request.new(path) |> Request.set_transform(Invite) end + @impl true def get_user(user) do user_id = Util.resolve_user_id(user) path = Endpoints.users(user_id) - Request.new(:get, path) + :get + |> Request.new(path) |> Request.set_transform(User) end + @impl true def modify_current_user(data) do path = Endpoints.me() @@ -975,18 +1099,22 @@ defmodule Crux.Rest.Functions do |> Map.new() |> Util.resolve_image_in_map(:avatar) - Request.new(:post, path, data) + :post + |> Request.new(path, data) |> Request.set_transform(User) end + @impl true def get_current_user_guilds(data) do path = Endpoints.me_guilds() data = Map.new(data) - Request.new(:get, path, data) + :get + |> Request.new(path, data) |> Request.set_transform(&Structs.Util.raw_data_to_map(&1, Guild)) end + @impl true def leave_guild(guild) do guild_id = Util.resolve_guild_id(guild) @@ -995,22 +1123,26 @@ defmodule Crux.Rest.Functions do Request.new(:delete, path) end + @impl true def create_dm(user) do user_id = Util.resolve_user_id(user) path = Endpoints.me_channels() data = %{recipient_id: user_id} - Request.new(:post, path, data) + :post + |> Request.new(path, data) |> Request.set_transform(Channel) end + @impl true def gateway() do path = Endpoints.gateway() Request.new(:get, path) end + @impl true def gateway_bot() do path = Endpoints.gateway_bot() diff --git a/lib/rest/handler/global.ex b/lib/rest/handler/global.ex index 2421102..8554193 100644 --- a/lib/rest/handler/global.ex +++ b/lib/rest/handler/global.ex @@ -57,6 +57,7 @@ defmodule Crux.Rest.Handler.Global do end @doc false + @impl true def init(_) do offsets = [] reset = 0 @@ -64,6 +65,7 @@ defmodule Crux.Rest.Handler.Global do end @doc false + @impl true def handle_call(:offset, _from, {offsets, _reset} = state) when length(offsets) <= 0 do {:reply, 0, state} end diff --git a/lib/rest/handler/handler.ex b/lib/rest/handler/handler.ex index df6db4f..33ca80c 100644 --- a/lib/rest/handler/handler.ex +++ b/lib/rest/handler/handler.ex @@ -3,8 +3,8 @@ defmodule Crux.Rest.Handler do defstruct [:name, :retries, :route, :remaining, :reset, :timer] - alias Crux.Rest.{Handler, Request, HTTP} alias Crux.Rest.Handler.{Global, State} + alias Crux.Rest.{Handler, HTTP, Request} require Logger @@ -55,6 +55,7 @@ defmodule Crux.Rest.Handler do ### Server @doc false + @impl true def init({name, route}) do Logger.debug(fn -> "[Crux][Rest][Handler][#{route}]: Starting" end) @@ -72,6 +73,7 @@ defmodule Crux.Rest.Handler do @doc false # Handler is idle, shutdown + @impl true def handle_info(:shutdown, %Handler{route: route} = state) do Logger.debug(fn -> "[Crux][Rest][Handler][#{route}]: Stopping idle handler" end) @@ -88,6 +90,7 @@ defmodule Crux.Rest.Handler do end # Queue request, clear timer + @impl true def handle_call({:queue, _request} = message, from, %Handler{timer: timer} = state) when not is_nil(timer) do :timer.cancel(timer) @@ -144,12 +147,10 @@ defmodule Crux.Rest.Handler do handle_call(message, from, state) else reset_time = - cond do - is_integer(state.reset) -> - max(state.reset - :os.system_time(:milli_seconds), 0) - - true -> - 0 + if is_integer(state.reset) do + max(state.reset - :os.system_time(:milli_seconds), 0) + else + 0 end {:ok, ref} = :timer.send_after(reset_time, :shutdown) @@ -169,17 +170,20 @@ defmodule Crux.Rest.Handler do {:ok, %HTTPoison.Response{headers: headers}} ) do date = - List.keyfind(headers, "Date", 0) + headers + |> List.keyfind("Date", 0) |> parse_header() Global.add_offset(name, date) remaining = - List.keyfind(headers, "X-RateLimit-Remaining", 0) + headers + |> List.keyfind("X-RateLimit-Remaining", 0) |> parse_header() reset = - List.keyfind(headers, "X-RateLimit-Reset", 0) + headers + |> List.keyfind("X-RateLimit-Reset", 0) |> parse_header() if remaining && reset do @@ -208,7 +212,7 @@ defmodule Crux.Rest.Handler do reset - :os.system_time(:milli_seconds) end - if List.keyfind(headers, "X-RateLimit-Global", 0) |> parse_header() == true do + if headers |> List.keyfind("X-RateLimit-Global", 0) |> parse_header() == true do Global.set_global_wait(name, retry_after) end @@ -255,7 +259,7 @@ defmodule Crux.Rest.Handler do end end - defp parse_header({_name, value}), do: value |> String.to_integer() + defp parse_header({_name, value}), do: String.to_integer(value) defp wait(timeout, route) when timeout > 0 do Logger.debug("[Crux][Rest][Handler][#{route}]: Rate limited, waiting #{timeout}ms") diff --git a/lib/rest/handler/state.ex b/lib/rest/handler/state.ex index 932fa39..07f8de3 100644 --- a/lib/rest/handler/state.ex +++ b/lib/rest/handler/state.ex @@ -9,10 +9,9 @@ defmodule Crux.Rest.Handler.State do Version.modulesince("0.2.0") @doc false + @spec start_link(term()) :: GenServer.on_start() def start_link({name, _state} = args) do - name = - name - |> Module.concat(State) + name = Module.concat(name, State) GenServer.start_link(__MODULE__, args, name: name) end @@ -20,6 +19,7 @@ defmodule Crux.Rest.Handler.State do @doc false Version.since("0.2.0") + @spec init(term()) :: {:ok, term()} def init({name, state}) do name = name diff --git a/lib/rest/handler/supervisor.ex b/lib/rest/handler/supervisor.ex index de06825..530a514 100644 --- a/lib/rest/handler/supervisor.ex +++ b/lib/rest/handler/supervisor.ex @@ -6,10 +6,12 @@ defmodule Crux.Rest.Handler.Supervisor do use Supervisor + @spec start_link(term()) :: Supervisor.on_start() def start_link({name, _state} = args) do Supervisor.start_link(__MODULE__, args, name: name) end + @spec init(term()) :: {:ok, tuple()} def init({name, state}) do registry = Module.concat(name, Registry) @@ -27,6 +29,7 @@ defmodule Crux.Rest.Handler.Supervisor do Supervisor.init(children, strategy: :one_for_one) end + @spec start_child(Supervisor.supervisor(), String.t()) :: Supervisor.on_start_child() def start_child(name, route) do Supervisor.start_child( name, diff --git a/lib/rest/http.ex b/lib/rest/http.ex index d05b0b1..d6668fb 100644 --- a/lib/rest/http.ex +++ b/lib/rest/http.ex @@ -4,17 +4,21 @@ defmodule Crux.Rest.HTTP do # https://github.com/edgurgel/httpoison use HTTPoison.Base - alias Crux.Rest.{Endpoints} + alias Mix.Project + + alias Crux.Rest.Endpoints # See: https://discordapp.com/developers/docs/reference#user-agent - url = Mix.Project.config()[:source_url] - version = Mix.Project.config()[:version] + url = Project.config()[:source_url] + version = Project.config()[:version] @user_agent "DiscordBot (#{url}, v#{version})" + @spec process_request_body(term()) :: term() def process_request_body(""), do: "" def process_request_body({:multipart, _} = body), do: body def process_request_body(body), do: Poison.encode!(body) + @spec process_request_headers(Keyword.t()) :: Keyword.t() def process_request_headers(headers) do headers |> Keyword.put_new(:"content-type", "application/json") @@ -36,6 +40,7 @@ defmodule Crux.Rest.HTTP do ) end + @spec request(Crux.Rest.Request.t()) :: :ok | {:ok, term()} | {:error, term()} def request(%Crux.Rest.Request{ method: method, path: path, @@ -60,7 +65,8 @@ defmodule Crux.Rest.HTTP do options :: Keyword.t() ) :: :ok | {:ok, term()} | {:error, term()} def request(method, route, body, headers, options) do - super(method, Endpoints.base_url() <> route, body, headers, options) + method + |> super(Endpoints.base_url() <> route, body, headers, options) |> handle_response() end diff --git a/lib/rest/request.ex b/lib/rest/request.ex index 73a57e4..dc6b9f2 100644 --- a/lib/rest/request.ex +++ b/lib/rest/request.ex @@ -4,6 +4,7 @@ defmodule Crux.Rest.Request do """ alias Crux.Rest.Version + alias Crux.Structs require Version Version.modulesince("0.2.0") @@ -109,7 +110,7 @@ defmodule Crux.Rest.Request do def transform(%__MODULE__{transform: struct}, data) when is_atom(struct) do - Crux.Structs.create(data, struct) + Structs.create(data, struct) end def transform(%__MODULE__{transform: fun}, data) diff --git a/lib/rest/util.ex b/lib/rest/util.ex index 8fca42b..274e6f1 100644 --- a/lib/rest/util.ex +++ b/lib/rest/util.ex @@ -96,7 +96,7 @@ defmodule Crux.Rest.Util do @spec resolve_multipart(map()) :: {{:multipart, list()} | map(), list()} def resolve_multipart(%{files: [_ | _] = files} = data) do - form_data = Enum.map(files, &transform_attachment/1) + multipart_files = Enum.map(files, &transform_attachment/1) form_data = if map_size(data) > 1 do @@ -105,9 +105,9 @@ defmodule Crux.Rest.Util do |> Map.delete(:files) |> Poison.encode!() - [{"payload_json", payload_json}] + [{"payload_json", payload_json} | multipart_files] else - form_data + multipart_files end {{:multipart, form_data}, [{"content-type", "multipart/form-data"}]} diff --git a/mix.exs b/mix.exs index a780cee..9083520 100644 --- a/mix.exs +++ b/mix.exs @@ -47,7 +47,8 @@ defmodule Crux.Rest.MixProject do git: "https://github.com/spaceeec/ex_doc", branch: "feat/umbrella", only: :dev, - runtime: false} + runtime: false}, + {:credo, "~> 1.0.0", only: [:dev, :test], runtime: false} ] end end diff --git a/mix.lock b/mix.lock index 1ca7827..0e25543 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,8 @@ %{ + "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, "certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"}, + "credo": {:hex, :credo, "1.0.4", "d2214d4cc88c07f54004ffd5a2a27408208841be5eca9f5a72ce9e8e835f7ede", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, "crux_structs": {:git, "https://github.com/spaceeec/crux_structs.git", "814bc3f1d7ffa067d253e9541bf78fbd933b7b65", []}, "earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"}, "ex_doc": {:git, "https://github.com/spaceeec/ex_doc", "ba85b84d56c24a42db725858b07f7b2cdc4a37b7", [branch: "feat/umbrella"]}, @@ -8,6 +10,7 @@ "hackney": {:hex, :hackney, "1.13.0", "24edc8cd2b28e1c652593833862435c80661834f6c9344e84b6a2255e7aeef03", [:rebar3], [{:certifi, "2.3.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.2", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, "httpoison": {:hex, :httpoison, "1.1.1", "96ed7ab79f78a31081bb523eefec205fd2900a02cda6dbc2300e7a1226219566", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, "idna": {:hex, :idna, "5.1.2", "e21cb58a09f0228a9e0b95eaa1217f1bcfc31a1aaa6e1fdf2f53a33f7dbd9494", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, + "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},