Hot questions for Using Spree in api

Question:

How do I permit parameters for a Spree API request?

I added a new parameter, :note, in the following file:

spree_api/app/helpers/spree/api/api_helpers.rb

Spree::Api::ApiHelpers.module_eval do
   @@order_attributes = [
     :id, :number, :item_total, :total, :ship_total, :state, :adjustment_total,
     :user_id, :created_at, :updated_at, :completed_at, :payment_total,
     :shipment_state, :payment_state, :email, :special_instructions, :channel,
     :included_tax_total, :additional_tax_total,      :display_included_tax_total,
     :display_additional_tax_total, :tax_total, :currency, :notes
   ]
end

However, every time I send a :note parameter, I get the following response in my console:

Unpermitted parameters: id, number, item_total, total, state, adjustment_total, user_id, completed_at, payment_total, shipment_state, payment_state, created_at, updated_at, currency, additional_tax_total, channel, included_tax_total, notes

What's the correct way to add permitted params to a Spree API call?

I'm using the following gems:

spree (2.4.3.beta)
  spree_api (= 2.4.3.beta)

My Rails version is: Rails 4.1.8


Answer:

For order_attributes you would use the following line

I believe this in your order_decorator.rb:

Spree::PermittedAttributes.order_attributes << :your_attribute

Question:

While attempting to post an image to Spree's ProductImage API using HTTPoison, it's failing with the Rails error NoMethodError (undefined method 'permit' for #<ActionDispatch::Http::UploadedFile:0x007f94fa150040>). The Elixir code that I'm using to generate this request is:

 def create() do
    data = [
      {:file, "42757187_001_b4.jpeg",
      {"form-data", [{"name", "image[attachment]"}, {"filename", "42757187_001_b4.jpeg"}]},
          [{"Content-Type", "image/jpeg"}]
        }, {"type", "image/jpeg"}
    ]

    HTTPoison.post!("http://localhost:3000/api/v1/products/1/images", {:multipart, data}, ["X-Spree-Token": "5d096ecb51c2a8357ed078ef2f6f7836b0148dbcc536dbfc", "Accept": "*/*"])
  end

I can get this to work using Curl with the following call:

curl -i -X POST \
  -H "X-Spree-Token: 5d096ecb51c2a8357ed078ef2f6f7836b0148dbcc536dbfc" \
  -H "Content-Type: multipart/form-data" \
  -F "image[attachment]=@42757187_001_b4.jpeg" \
  -F "type=image/jpeg" \
  http://localhost:3000/api/v1/products/1/images

For comparison, here's a RequestBin capture of both the failing HTTPoison request followed by the successful Curl request: https://requestb.in/12et7bp1?inspect

What do I need to do in order to get HTTPoison to play nicely with this Rails API?


Answer:

The Content-Disposition line requires double quotes around the name and filename values. curl adds those automatically but Hackney passes the data you specify as-is, so you need to add the double quotes to the values yourself.

This:

[{"name", "image[attachment]"}, {"filename", "42757187_001_b4.jpeg"}]

should be:

[{"name", ~s|"image[attachment]"|}, {"filename", ~s|"42757187_001_b4.jpeg"|}]

(I'm only using the ~s sigil so that double quotes can be added without escaping them. ~s|""| is exactly the same as "\"\"".)

Question:

I've an e-commerce app which built using Spree, now i need to create a new version of the API for a certain controller and here's what i did

  • created a new controller in /api/v2/x_controller.rb
  • made an action index
  • add route for my controller in routes.rb
  • made a rabl file ( index.v2.rabl )

when i try to access the api my request hit the action but i keep getting error

ActionView::MissingTemplate (Missing template spree/api/v2/x/index)

i there any missing configuration i should do ?!


Answer:

Your RABL view should be under

app/views/api/v2/x/index.rabl

There is a naming convention for views in Rails. Typically, the views share their name with the associated controller action. For example, the index controller action of the articles_controller.rb will use the index.html.erb view file in the app/views/articles directory. The complete HTML returned to the client is composed of a combination of this ERB file, a layout template that wraps it, and all the partials that the view may reference.

Question:

Every time I'm trying to set the 'Access-Control-Allow-Origin' header for the Spree Commerce API, no header is being passed for Ajax calls.

I'm currently extending the base_controller.rb:

File: app/controllers/spree/api/base_controller_decorator.rb

after_filter :set_access_control_headers

def set_access_control_headers
  headers['Access-Control-Allow-Origin'] = '*'
  headers['Access-Control-Request-Method'] = '*'
end

The header is added to the requests every time I view a JSON directly from the browser. However, no header is visible when making AJAX calls...

Can anybody explain me how to do this? Is there a workaround for this issue?


Answer:

You don't need anything in your application controller, rather you need a gem:

Add gem 'rack-cors', :require => 'rack/cors' to your Gemfile, then bundle install

In config/application.rb configure rack-cors to accept requests from any origin. It belongs anywhere inside the Application class.

config.middleware.insert_before 0, "Rack::Cors" do
  allow do
    origins '*'
    resource '*', :headers => :any, :methods => [:get, :post, :options]
  end
end

origins '*' indicates you are allowing requests from any domain. If you want to restrict to a specific domain (i.e. internally open an api, but keep it closed to external users), change '*' to the specific domain name.

see https://github.com/cyu/rack-cors for more details

Question:

I've integrated Shippo with my Ruby on Rails Spree platform. Everything seems to be working great, except that when I go to create a transaction to print shipping labels, I get an error in the response.

Here's my response:

#<Transaction:0x3feee363470c[id=1b419434531e4b43b438c54b93e2a9f5] {"object_state"=>"VALID", "status"=>"ERROR", "object_created"=>"2017-06-27T23:11:54.567Z", "object_updated"=>"2017-06-27T23:11:55.330Z", "object_id"=>"xxxx", "object_owner"=>"----@gmail.com", "test"=>true, "rate"=>{"object_id"=>"xxxx", "amount"=>"6.52", "currency"=>"USD", "amount_local"=>"6.52", "currency_local"=>"USD", "provider"=>"USPS", "servicelevel_name"=>"Priority Mail", "servicelevel_token"=>"usps_priority", "carrier_account"=>"xxxx"}, "tracking_number"=>"", "tracking_status"=>nil, "tracking_history"=>[], "tracking_url_provider"=>"", "label_url"=>"", "commercial_invoice_url"=>nil, "messages"=>[#<Hashie::Mash code="" source="USPS" text="Request failed. Please try again or contact Shippo support at support@goshippo.com.">], "order"=>nil, "metadata"=>"", "parcel"=>"xxxx"}->#<Shippo::API::ApiObject created=2017-06-27 23:11:54 UTC id="1b419434531e4b43b438c54b93e2a9f5" owner="xxxx@xxxx.com" state=#<Shippo::API::Category::State:0x007fddbca5a2e8 @name=:state, @value=:valid> updated=2017-06-27 23:11:55 UTC>

Here's the code used to create the label:

def self.createLabel(order_info)
    shipping_info = order_info.shipping_address
    stock_location = order_info.store.stock_location

address_from = {
  :name => stock_location.name,
  :company => order_info.store.name,
  :street1 => stock_location.address1,
  :street2 => stock_location.address2,
  :city => stock_location.city,
  :state => "#{Spree::State.find(stock_location.state_id)}",
  :zip => stock_location.zipcode,
  :country => "#{Spree::Country.find(stock_location.country_id)}",
  :phone => stock_location.phone,
}

address_to = {
    :name => "#{shipping_info.firstname} #{shipping_info.lastname}",
    :company => shipping_info.company,
    :street1 => shipping_info.address1,
    :street2 => shipping_info.address2,
    :city => shipping_info.city,
    :state => "#{Spree::State.find(shipping_info.state_id)}",
    :zip => shipping_info.zipcode,
    :country => "#{Spree::Country.find(shipping_info.country_id)}",
    :phone => shipping_info.phone,
    :email => order_info.email
}
parcel = {
    :length => getLength(order_info),
    :width => getWidth(order_info),
    :height => getHeight(order_info),
    :distance_unit => :m,
    :weight => getWeight(order_info),
    :mass_unit => :lb
}

shipment = {
    :address_from => address_from,
    :address_to => address_to,
    :parcels => parcel
}

#Shippo Carrier ids
@ups = Rails.application.secrets.ups_shippo_id
@usps = Rails.application.secrets.usps_shippo_id
transaction = Shippo::Transaction.create(
    :shipment => shipment,
    :carrier_account => "#{@usps}",
    :servicelevel_token => "usps_priority",
    :label_file_type => "PDF",
    :async => false
)
end

Has anyone run into this issue before? I've looked at their documentation and cannot find any reason for a "status"=>"ERROR" message, when the "object_state"=>"VALID".

I'm happy to post more code if needed. Thanks.


Answer:

What I found with lots of trial and error, was that the products I was sending to Shippo actually exceeded the weight the shipping carrier would allow in a package. (This was because my database was a bunch of dummy data). Be sure to be setting your measurement units as well, here:

    parcel = {
      :length => getLength(order_info),
      :width => getWidth(order_info),
      :height => getHeight(order_info),
      :distance_unit => :m,
      :weight => getWeight(order_info),
      :mass_unit => :lb
    }

After changing the data in my database to have reasonable weights on the products, this error went away.

Question:

Using Spree 2.0.x how is it possible to update a shipments tracking details?

Here's a curl example that should update a shipment's tracking to 'yeah'. But it doesn't seem to do anything. What's wrong with it?

curl -i -X PUT -H "X-Spree-Token: b79756519374randomtokenb79756519374" -d "shipment\[tracking\]=yeah" http://url.com/api/orders/W647803480/shipments/H10788414786

In the server log I see the appropriate request;

Started PUT "/api/orders/W647803480/shipments/H10788414786" 

but the only data that gets updated is the 'updated_at' field of the order.

I've also tried sending "tracking=yeah" instead of the above but no joy.

So - how is it possible to use the API to set the tracking info?


Answer:

It turns out that in order to update shipment attributes it's necessary to use /ready in the url.

For the above example;

curl -i -X PUT -H "X-Spree-Token: b79756519374randomtokenb79756519374" -d "shipment\[tracking\]=yeah" http://url.com/api/orders/W647803480/shipments/H10788414786/ready

will work.

Question:

I have inherited a running spree Commerce website and need to work with its API. I have tried to access through URL [websitename]/api/ and api.[websitename].com but don't think I'm taking the right approach here.

Please, I want to know, does the API not come bundled when spree is installed, or does it have to be installed separately.

I've searched through Google and can find any instructions.


Answer:

There's not much in the way of documentation for it but there's a rubygem page: https://rubygems.org/gems/spree_api and some API docs here: http://guides.spreecommerce.com/api/summary.html

I believe you can enable it by simply adding the following to your gemfile:

gem "spree_api"