HTTParty: Post action is resulting in error Net::HTTPServerException (403 "Forbidden")

260 Views Asked by At

I am trying to implement post action using httparty gem and this is what I have. I am running everything in docker and I have code below that will run as active job. I is in one service and I am trying to make post to api in other service. I am able to do get but not having any luck with post. I looked and searched a lot online but I am not sure what is it I am doing wrong. I always get error 403 at self.class.post line. I also tried to do a postman call to api and I am able to hit the api but with the code below its not even reaching to the other service.

Any help is appreciated. Thanks.

require 'uri'

class CustomerProductAPI
  include HTTParty
  format :json

  def initialize(customer_product_id)
    @customer_product = CustomerProduct.find(customer_product_id)
    @customer = Customer.find(@customer_product.student_id)
    @product = Product.find(@customer_product.product_id)
    self.class.base_uri environment_based_uri + '/customer_product_api'
  end

  def create_customer_product
    uri = URI(self.class.base_uri + "/customer/#{customer.id}")
    self.class.post(uri, body: body_hash).response.value
  end

  private

  attr_reader :customer_product, :customer, :product

  def body_hash
    {
      token: ENV['CUSTOMER_PRODUCT_API_TOKEN'],
      customer: customer.name,
      product: product.name,
    }
  end

  def environment_based_uri
    ENV['CUSTOMER_PRODUCT_URL']
  end
end
1

There are 1 best solutions below

0
max On

While we can't actually be sure of exactly what the server thats accepting the response expects you're definately doing quite a few non-idiomatic things here which will aggrevate trouble shooting.

  • base_uri should just be set in the class body. Not in initialize for each instance. You also do not need to construct a URI with HTTParty. Just pass a path and it will construct the request uri relative to the base_uri.
  • When getting configuration from ENV use ENV.fetch instead of the bracket accessors as it will raise a KeyError instead of just letting a nil sneak through.
  • Your HTTP client class should not be concerned with querying the database and handling the potential errors that can occur if the records cannot be found. That should be the responsibility of the controller/job/service object that calls the client. Since you're only actually using three simple attributes it doesn't actually need records at all as input and its actually better that it doesn't have to know about your models and their assocations (or lack thereof in this case).
class CustomerProductAPI
  # lets you stub/inspect the constant
  CUSTOMER_PRODUCT_URL = ENV.fetch('CUSTOMER_PRODUCT_URL') + '/customer_product_api'

  include HTTParty
  format :json
  base_uri CUSTOMER_PRODUCT_URL 

  def initialize(id:, product_name:, customer_name:)
    @id = id
    @product_name = product_name
    @customer_name = customer_name
  end

  def create_customer_product
    self.class.post("/customer/#{@id}", body: {
      token: ENV.fetch('CUSTOMER_PRODUCT_API_TOKEN'),
      customer: @customer_name,
      product: @product_name
    })
    # don't return .response.value as it will make error handling impossible. 
    # either handle unsuccessful responses here or return the whole response 
    # for the consumer to handle it.
  end
end