# frozen_string_literal: true require 'luna_park/http/send' module LunaPark module Http class Request # Business description for this request, help you # make the domain model more expressive # # @example Get users request # request = Request.new( # title: 'Get users list', # method: :get, # url: 'https://example.com/users' # ) # # request.title # => 'Get users list' attr_accessor :title # Http method of current request, defines a set of # request methods to indicate the desired action to # be performed for a given resource # # @example Get users request # request.method # => :get # # @example With argument (delegates `Object.method(name)` to save access to the original Ruby method) # request.method(:foo) # => #<Method: LunaPark::Http::Request#foo> def method(name = nil) name.nil? ? @method : super(name) end attr_writer :method # Http url to send request # # @example Get users request # request.url # => 'http://example.com/users' attr_accessor :url # Body of http request (defaults is `nil`) # # @example Get users request # # request = Request.new( # title: 'Get users list', # method: :get, # url: 'http://example.com/users', # body: JSON.generate({message: 'Hello!'}) # ) # request.body # => "{\"message\":\"Hello!\"}"' attr_accessor :body # Http request headers (defaults is `{}`) # # @example Get users request # json_request.headers # => {} attr_accessor :headers # Http read timeout, is the timeout for reading # the answer. This is useful to make sure you will not # get stuck half way in the reading process, or get # stuck reading a 5 MB file when you're expecting 5 KB of JSON # (default is 10) # # @example Get users request # json_request.read_timout # => 10 attr_accessor :read_timeout # Http open timeout, is the timeout for opening # the connection. This is useful if you are calling # servers with slow or shaky response times. (defaults is 10) # # @example Get users request # json_request.open_timeout # => 10 attr_accessor :open_timeout # Time when request is sent # # @example before request is sent # request.sent_at # => nil # # @example after request has been sent # request.sent_at # => 2020-05-04 16:56:20 +0300 attr_reader :sent_at # Create new request # # @param title business description (see #title) # @param method Http method of current request (see #method) # @param url (see #url) # @param body (see #body) # @param headers (see #headers) # @param read_timeout (see #read_timeout) # @param open_timeout (see #open_timeout) # @param driver is HTTP driver which use to send this request # rubocop:disable Metrics/ParameterLists, Layout/LineLength def initialize(title:, method: nil, url: nil, body: nil, headers: nil, open_timeout: nil, read_timeout: nil, driver:) @title = title @method = method @url = url @body = body @headers = headers @read_timeout = read_timeout @open_timeout = open_timeout @driver = driver @sent_at = nil end # rubocop:enable Metrics/ParameterLists, Layout/LineLength # Send current request (we cannot call this method `send` because it # reserved word in ruby). It always return Response object, even if # the server returned an error such as 404 or 502. # # @example correct answer # request = Http::Request.new( # title: 'Get users list', # method: :get, # url: 'http:://yandex.ru' # ) # request.call # => <LunaPark::Http::Response @code=200 @body="Hello World!" @headers={}> # # @example Server unavailable # request.call # => <LunaPark::Http::Response @code=503 @body="" @headers={}> # # After sending the request, the object is frozen. You should dup object to resend request. # # @note This method implements a facade pattern. And you better use it # than call the Http::Send class directly. # # @return LunaPark::Http::Response def call @sent_at = Time.now driver.call(self).tap { freeze } end # Send the current request. It returns a Response object only on a successful response. # If the response failed, the call! method should raise an Erros::Http exception. # # After call! request you cannot change request attributes. # # @example correct answer # request = Http::Request.new( # title: 'Get users list', # method: :get, # url: 'https://example.com/users' # ) # request.call # => <LunaPark::Http::Response @code=200 @body="Hello World!" @headers={}> # # @example Server unavailable # request.call # => <LunaPark::Http::Response @code=503 @body="" @headers={}> # # After sending the request, the object is frozen. You should dup object to resend request. # # @note This method implements a facade pattern. And you better use it # than call the Http::Send class directly. # # @return LunaPark::Http::Response def call! @sent_at = Time.now driver.call!(self).tap { freeze } end # When object is duplicated, we should reset send timestamp def initialize_dup(_other) super @sent_at = nil end # This method shows if this request has been already sent. # # @return Boolean def sent? !@sent_at.nil? end # This method return which driver are use, to send current request. def driver @driver ||= self.class.default_driver end # @example inspect get users index request # request = LunaPark::Http::Request.new( # title: 'Get users', # method: :get, # url: 'https://example.com/users' # ) # # request.inspect # => "<LunaPark::Http::Request @title=\"Get users\" # # @url=\"http://localhost:8080/get_200\" @method=\"get\" # # @headers=\"{}\" @body=\"\" @sent_at=\"\">" def inspect "<#{self.class.name} " \ "@title=#{title.inspect} " \ "@url=#{url.inspect} " \ "@method=#{method.inspect} " \ "@headers=#{headers.inspect} " \ "@body=#{body.inspect} " \ "@sent_at=#{sent_at.inspect}>" end # @example # request.to_h # => {:title=>"Get users", # :method=>:get, # :url=>"http://localhost:8080/get_200", # :body=>nil, # :headers=>{}, # :read_timeout=>10, # :open_timeout=>10} def to_h { title: title, method: method, url: url, body: body, headers: headers, read_timeout: read_timeout, open_timeout: open_timeout, sent_at: sent_at } end end end end