# typed: strict # frozen_string_literal: true require "faraday" require "nexus/invision/errors/bad_request" require "nexus/invision/errors/unexpected_response" require "nexus/invision/requests/add_member_to_secondary_group" require "nexus/invision/requests/create_forum_post" require "nexus/invision/requests/create_forum_topic" require "nexus/invision/requests/create_member_warning" require "nexus/invision/requests/delete_member" require "nexus/invision/requests/edit_member" require "nexus/invision/requests/list_forum_topics" require "nexus/invision/requests/remove_member_from_secondary_group" require "nexus/invision/requests/edit_member_primary_group" require "nexus/invision/resources/group" require "nexus/invision/resources/login_link" require "nexus/invision/resources/member" require "nexus/invision/resources/page" require "nexus/invision/resources/post" require "nexus/invision/resources/topic" require "nexus/invision/resources/user" require "nexus/invision/resources/warning" require "sorbet-runtime" require "uri" module Nexus module Invision class HTTPMethod < T::Enum enums do GET = new POST = new DELETE = new end end class Client extend T::Sig sig { params(connection: Faraday::Connection).void } def initialize(connection) @connection = connection end class << self extend T::Sig sig do params( base_url: URI::HTTP, api_key: String, block: T.nilable(T.proc.params(arg0: Faraday::Connection).void), ).returns(Client) end def build(base_url:, api_key:, &block) new( Faraday.new(url: base_url.to_s, headers: { "Accept" => "application/json" }) do |builder| builder.request(:authorization, :basic, api_key, "") builder.request(:url_encoded) builder.response(:json) block&.call(builder) end, ) end end sig { params(request: Requests::ListForumTopics).returns(Resources::Page[Resources::Topic]) } def list_forum_topics(request) response = request( http_method: HTTPMethod::GET, endpoint: URI("forums/topics"), params: request.serialize, ) body = response.body Resources::Page[Resources::Topic].new( page: body.fetch("page"), per_page: body.fetch("perPage"), total_results: body.fetch("totalResults"), total_pages: body.fetch("totalPages"), results: body.fetch("results").map { |result| Resources::Topic.from_hash(result) }, ) end sig { params(request: Requests::CreateForumTopic).returns(Resources::Topic) } def create_forum_topic(request) response = request( http_method: HTTPMethod::POST, endpoint: URI("forums/topics"), params: request.serialize, ) Resources::Topic.from_hash(response.body) end sig { params(request: Requests::CreateForumPost).returns(Resources::Post) } def create_forum_post(request) response = request( http_method: HTTPMethod::POST, endpoint: URI("forums/posts"), params: request.serialize, ) Resources::Post.from_hash(response.body) end sig { params(nexusmods_member_id: Integer).returns(Resources::LoginLink) } def login_link(nexusmods_member_id) response = request( http_method: HTTPMethod::GET, endpoint: URI("core/loginlinks/#{nexusmods_member_id}"), ) Resources::LoginLink.from_hash(response.body) end sig { params(nexusmods_member_id: Integer).returns(Resources::Member) } def get_member(nexusmods_member_id) response = request( http_method: HTTPMethod::GET, endpoint: URI("core/members/#{nexusmods_member_id}"), ) Resources::Member.from_hash(response.body) end sig { params(request: Requests::EditMemberPrimaryGroup).returns(Resources::Member) } def edit_member_primary_group(request) response = request( http_method: HTTPMethod::POST, endpoint: URI("core/members/#{request.member_id}"), params: request.serialize.except("member_id"), ) Resources::Member.from_hash(response.body) end sig { params(request: Requests::AddMemberToSecondaryGroup).returns(Resources::User) } def add_member_to_secondary_group(request) response = request( http_method: HTTPMethod::POST, endpoint: URI("core/members/#{request.member_id}/secgroup/#{request.group_id}"), ) Resources::User.from_hash(response.body) end sig { params(request: Requests::RemoveMemberFromSecondaryGroup).returns(Resources::User) } def remove_member_from_secondary_group(request) response = request( http_method: HTTPMethod::DELETE, endpoint: URI("core/members/#{request.member_id}/secgroup/#{request.group_id}"), ) Resources::User.from_hash(response.body) end sig { params(request: Requests::CreateMemberWarning).returns(Resources::Warning) } def create_member_warning(request) response = request( http_method: HTTPMethod::POST, endpoint: URI("core/members/#{request.member_id}/warnings"), params: request.serialize.except("member_id"), ) Resources::Warning.from_hash(response.body) end sig { params(member_id: Integer).returns(Resources::User) } def ban_member(member_id) response = request( http_method: HTTPMethod::POST, endpoint: URI("core/bans/#{member_id}"), ) Resources::User.from_hash(response.body) end sig { params(member_id: Integer).returns(Resources::User) } def unban_member(member_id) response = request( http_method: HTTPMethod::DELETE, endpoint: URI("core/bans/#{member_id}"), ) Resources::User.from_hash(response.body) end sig { params(request: Requests::DeleteMember).void } def delete_member(request) # 1 is keep member name, 0 is anonymize content values = request.serialize.except("member_id").merge({ "contentAnonymize" => request.keep_member_name ? 1 : 0 }) request( http_method: HTTPMethod::DELETE, endpoint: URI("core/members/#{request.member_id}"), params: values, ) end sig { params(request: Requests::EditMember).returns(Resources::User) } def edit_member(request) response = request( http_method: HTTPMethod::POST, endpoint: URI("core/members/#{request.member_id}"), params: request.serialize.except("member_id"), ) Resources::User.from_hash(response.body) end private sig do params( http_method: HTTPMethod, endpoint: URI, params: T::Hash[String, T.untyped], ).returns(Faraday::Response) end def request(http_method:, endpoint:, params: {}) response = case http_method when HTTPMethod::GET @connection.get(endpoint, params) when HTTPMethod::POST @connection.post(endpoint, params) when HTTPMethod::DELETE @connection.delete(endpoint, params) else T.absurd(http_method) end handle_error(response) unless response.success? response end ERROR_CODE_KEY = "errorCode" ERROR_MESSAGE_KEY = "errorMessage" sig { params(response: Faraday::Response).void } def handle_error(response) body = response.body if bad_request_error?(body) raise Errors::BadRequest.new(body[ERROR_MESSAGE_KEY], body[ERROR_CODE_KEY], response.env) end raise Errors::UnexpectedResponse.new("Unexpected response", response.env) end sig { params(body: Object).returns(T::Boolean) } def bad_request_error?(body) body.is_a?(Hash) && body.key?(ERROR_MESSAGE_KEY) && body.key?(ERROR_CODE_KEY) end end end end