I am trying to submit a feed to Walmarts API following this guide and this api documentation
In their guide it says
Send an item feed to Walmart with payload in either XML or JSON format
I am sending this JSON
{
"MPItemFeedHeader":{
"version":"1.0",
"sellingChannel":"mpsetupbymatch",
"locale":"en"
},
"MPItem":[
{
"Item":{
"productIdentifiers":{
"productId":"042666047173",
"productIdType":"UPC"
},
"ShippingWeight":2,
"price":420.69,
"sku":"RICKS-12345"
}
}
]
}
Via a POST request like so:
# Submits a feed to Walmart
# #param feed_data [Hash] data that will be submited with the feed
# #param type [String] Enum: "item" "RETIRE_ITEM" "MP_ITEM" "MP_WFS_ITEM" "MP_ITEM_MATCH" "MP_MAINTENANCE" "SKU_TEMPLATE_MAP" "SHIPPING_OVERRIDES"
def submit_feed(feed_data, type)
# To add a param to a multipart POST request you need to append the params to the URL
endpoint = "https://marketplace.walmartapis.com/v3/feeds?feedType=" + type
headers = self.api_client.headers.with_indifferent_access
uri = URI(endpoint)
request = Net::HTTP::Post.new(uri)
# Add Required Headers
request['Authorization'] = headers["Authorization"]
request['WM_SEC.ACCESS_TOKEN'] = headers["WM_SEC.ACCESS_TOKEN"]
request['WM_QOS.CORRELATION_ID'] = headers["WM_QOS.CORRELATION_ID"]
request['WM_SVC.NAME'] = headers["WM_SVC.NAME"]
request['Accept'] = headers["Accept"]
# Set form wants an array of arrays, basically the first item is the key and the second the value
request.set_form([["file", feed_data]], 'multipart/form-data')
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
end
The feed successfully submits, but when i check the status this is the response:
{
"feedId":"6DCFAA97311140358D6D842488B24333#AQMBCgA",
"feedStatus":"ERROR",
"shipNode":null,
"submittedBy":null,
"feedSubmissionDate":1627595359155,
"ingestionErrors":{
"ingestionError":[
{
"type":"DATA_ERROR",
"code":"ERR_EXT_DATA_0801001",
"field":"IB",
"description":"Unexpected character '{' (code 123) in prolog; expected '\u003c'\n at [row,col {unknown-source}]: [1,1]"
}
]
},
"itemsReceived":0,
"itemsSucceeded":0,
"itemsFailed":0,
"itemsProcessing":0,
"offset":0,
"limit":50,
"itemDetails":{
"itemIngestionStatus":[
]
},
"additionalAttributes":null
}
Judging by the error message
Unexpected character '{' (code 123) in prolog; expected '\u003c'\n at [row,col {unknown-source}]: [1,1]
It seems like they are expecting me to be sending an XML file, i can't figure out what it is i am doing wrong.
They require that the data is sent as multipart so i can't set the Content-Type to application/json
Is there anything i am missing to tell them in the request that the payload is JSON?
I figured it out with the help of this answer on another question.
You are better off referencing this than Walmarts official documentation
You need to submit a post request to the "https://marketplace.walmartapis.com/v3/feeds endpoint appending your type on the url ?feedType=[something]
You need to make sure that your "Content-Type" is "application/json" if you are sending them JSON.
You do not need to send it multipart like the documentation suggests, just send your entire JSON file as the body and it should work correctly.
Here is my new ruby code to get this done:
# Submits a feed to Walmart
# #param feed_data [Hash] data that will be submited with the feed
# #param type [String] Enum: "item" "RETIRE_ITEM" "MP_ITEM" "MP_WFS_ITEM" "MP_ITEM_MATCH" "MP_MAINTENANCE" "SKU_TEMPLATE_MAP" "SHIPPING_OVERRIDES"
def submit_feed(feed_data, type)
# To add a param to a multipart POST request you need to append the params to the URL
endpoint = "https://marketplace.walmartapis.com/v3/feeds?feedType=" + type
uri = URI.parse(endpoint)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.post(uri.path, feed_data, self.api_client.headers)
end
If someone from Walmart is looking at this question, please improve your documentation.
Related
Trying to parse the json string with JSON.parse I get unexpected token error
require "net/http"
require "uri"
url = URI.parse("https://url-goes-here")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
req = Net::HTTP::Get.new(url.request_uri)
req['Accept'] = 'application/json'
res = Net::HTTP.new(url.host, url.port).start do |http|
http.request(req)
end
json = JSON.parse(res)
puts json
Data in response looks like JSONP, rather old method of doing rpc. Instead of returning plain JSON it outputs some_js_callback_function_name({here_goes: the_json}), usually there's also a parameter that controls the function name.
To get json from it - trim the function call before parsing:
json_data = res.body.gsub(/\A[^(]+\(/, '').gsub(/\)\s*\z/, '')
json = JSON.parse(json_data)
I'm attempting pull json data from an api, iterate through a hash of JSON data, append the new data to an empty hash and then check the pulled data for whether the key 'next_page' has any value..breaking the loop if the value is returned nill. I'm pulling the first batch of data from the api successfully, but my loop seems to be incorrect as no data is being inserted into the empty hash i've created.
Any ideas would be deeply appreciated. Thanks for giving my question a read!
require 'net/http'
require 'uri'
require 'json'
#imports APR User data from the zendesk api and populates the database with it.
uri = URI.parse("https://tester.zendesk.com/api/v2/users.json")
request = Net::HTTP::Get.new(uri)
request.content_type = "application/json"
request.basic_auth("cbradford#tester.com", "tester32")
req_options = {
use_ssl: uri.scheme == "https",
}
#response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
puts #response.body
puts #response.message
puts #response.code
res = #response.body
users = res["users"]
data = {}
if users["next_page"]
newUri = users.fetch('next_page')
uriLoop = URI.parse(newUri)
request = Net::HTTP::Get.new(uriLoop)
request.content_type = "application/json"
request.basic_auth("cbradford#tester.com", "tester32")
req_options = {
use_ssl: uriLoop.scheme == "https",
}
#responseLoop = Net::HTTP.start(uriLoop.hostname, uriLoop.port, req_options) do |http|
http.request(request)
end
resLoop = JSON.parse(#responseLoop.body)
puts resLoop
users = resLoop["users"]
data.concat(users)
end
puts data
puts "hash created Successfully!"
You always omit the first fetched users. Before checking for the presence of next_page you never store the fetched users, and thus data remains empty.
See here:
users = res["users"]
data = {} ## ---data is initialised but never filled?
if users["next_page"]
Secondly I suspect that res["users"] is an array, and since you write data.concat(users) later that actually data should also be an array.
So your code should be fixed be replacing the above mentioned three lines as follows:
data = res["users"]
if users["next_page"]
class SampleTest(APITestCase):
def setUp(self):
self.id = 1
self.api_url = 'api/check_customers'
self.token ='##############'
def test_check_customer(self):
self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + self.token)
response = self.client.post(self.api_url, data={'id': self.id})
self.assertEqual(response.status_code, status.HTTP_200_OK)
When I test this code I get the error message which I have set for checking the emptyness of parameter like
{'message': 'id is empty', 'status': 'failed'}
and the way I check this is in views
class Customer(APIView):
def post(self, request, *args, **kwargs):
if "id" not in request.data:
content = {"status": "failed",
"message": "id is empty"}
return Response(content, status=STATUS.HTTP_400_BAD_REQUEST)
I am using DRF 3.3.0 and Django 1.9
response = self.client.post(self.api_url, data={'id': self.id}, format='json')
This works Fine. Default format type is multipart that has to be json while passing dictionary
As a quick background: I've already successfully set up a small application that will connect to Yahoo's API and set my lineups on a daily basis using PUT requests on my team's roster per the Yahoo Developer's Guide.
Specifically, where I'm having problems now:
POST
Using POST, players can be added and/or dropped from a team, or trades
can be proposed. The URI for POSTing to transactions collection is:
http://fantasysports.yahooapis.com/fantasy/v2/league//transactions
The input XML format for a POST request to the transactions API for
replacing one player with another player in a team is:
<fantasy_content>
<transaction>
<type>add/drop</type>
<players>
<player>
<player_key>{player_key}</player_key>
<transaction_data>
<type>add</type>
<destination_team_key>{team_key}</destination_team_key>
</transaction_data>
</player>
<player>
<player_key>{player_key}</player_key>
<transaction_data>
<type>drop</type>
<source_team_key>{team_key}</source_team_key>
</transaction_data>
</player>
</players>
</transaction>
</fantasy_content>
My method for making a request is as follows:
def self.make_signed_request(address, method, user_id, input_file=nil )
# format http method and return false if not accepted API method
method = method.upcase
return false if ( method != "GET" ) && ( method != "POST" ) && ( method != "PUT")
# find user
user = User.find(user_id)
if ( user.yahoo_access_token_expiration.nil? || user.yahoo_access_token_expiration < Time.now )
# expired token, so renew
YahooOAuth.renew_token(user_id)
user.reload
end
# normalize text HMAC-SHA1
digest = OpenSSL::Digest.new('sha1')
nonce = rand(10 ** 30).to_s.rjust(30,'0')
timestamp = Time.now.to_i
text = method + "&" + CGI.escape("#{address}") + "&" + CGI.escape("oauth_consumer_key=#{YAHOO_OAUTH_API[:CLIENT_ID]}&oauth_nonce=#{nonce}&oauth_signature_method=HMAC-SHA1&oauth_timestamp=#{timestamp}&oauth_token=#{CGI.escape(user.yahoo_access_token)}&oauth_version=1.0")
# create key for HMAC-SHA1
key = CGI.escape("#{YAHOO_OAUTH_API[:CLIENT_SECRET]}")+ "&" + CGI.escape("#{user.yahoo_access_token_secret}")
# create HMAC-SHA1 signature for api request
hmac = OpenSSL::HMAC.digest(digest, key, text)
signature_sha1 = CGI.escape(Base64.strict_encode64(hmac))
# make API request
response = nil
if method == "GET"
response = Curl.get("#{address}?oauth_consumer_key=#{YAHOO_OAUTH_API[:CLIENT_ID]}&oauth_nonce=#{nonce}&oauth_signature_method=HMAC-SHA1&oauth_timestamp=#{timestamp}&oauth_token=#{CGI.escape(user.yahoo_access_token)}&oauth_version=1.0&oauth_signature=#{signature_sha1}")
elsif method == "POST"
# return "Not implemented"
response = Curl.post("#{address}?oauth_consumer_key=#{YAHOO_OAUTH_API[:CLIENT_ID]}&oauth_nonce=#{nonce}&oauth_signature_method=HMAC-SHA1&oauth_timestamp=#{timestamp}&oauth_token=#{CGI.escape(user.yahoo_access_token)}&oauth_version=1.0&oauth_signature=#{signature_sha1}", input_file) do |http|
http.headers['Accept'] = 'application/xml'
http.headers['Content-Type'] = 'application/xml'
end
elsif method == "PUT"
response = Curl.put("#{address}?oauth_consumer_key=#{YAHOO_OAUTH_API[:CLIENT_ID]}&oauth_nonce=#{nonce}&oauth_signature_method=HMAC-SHA1&oauth_timestamp=#{timestamp}&oauth_token=#{CGI.escape(user.yahoo_access_token)}&oauth_version=1.0&oauth_signature=#{signature_sha1}", input_file) do |http|
http.headers['Accept'] = 'application/xml'
http.headers['Content-Type'] = 'application/xml'
end
end
# return raw XML result
return response.body
end
When I make my request --
def add_drop(players)
# players are added in [{player_key}, {add/drop}, {faab_bid (or nil if not FAAB)}] form
url = "http://fantasysports.yahooapis.com/fantasy/v2/league/#{self.league.league_key}/transactions"
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.fantasy_content {
xml.transaction {
xml.type "add/drop"
xml.faab_bid players[0][2] unless players[0][2].nil?
xml.players {
players.each do |player|
xml.player {
xml.player_key player[0]
xml.transaction_data {
xml.type player[1] # this will be "add" or "drop"
# adds use "destination_team_key," drops use "source_team_key"
if player[1] == "add"
xml.destination_team_key self.team_key
else
xml.source_team_key self.team_key
end
} # transaction_data
} # player
end
} # players
} # transaction
} # fantasy_content
end # builder
xml_input = builder.to_xml
YahooOAuth.make_signed_request(url, 'put', self.user.id, xml_input)
end
--the XML generated is shown below --
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<fantasy_content>\n <transaction>\n <type>add/drop</type>\n <players>\n <player>\n <player_key>357.p.10171</player_key>\n <transaction_data>\n <type>add</type>\n <destination_team_key>370.l.4801.t.7</destination_team_key>\n </transaction_data>\n </player>\n <player>\n <player_key>357.p.9317</player_key>\n <transaction_data>\n <type>drop</type>\n <source_team_key>370.l.4801.t.7</source_team_key>\n </transaction_data>\n </player>\n </players>\n </transaction>\n</fantasy_content>\n"
-- and the two responses I'll get from Yahoo are:
> <?xml version="1.0" encoding="UTF-8"?> <error xml:lang="en-us"
> yahoo:uri="http://fantasysports.yahooapis.com/fantasy/v2/league/370.l.4801/transactions/?oauth_consumer_key=dj0yJmk9dHBIa0h4ekhTSVBnJmQ9WVdrOVlUZHFkMnhMTXpJbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD1lNQ--&oauth_nonce=892057444475304460343340318332&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1492097395&oauth_token=A%3D2.86iTDxtT4nQ.wxTcn33mgnA8dm3AeME87YRjNthVjxiwfhqKr_oWt0HBgbBeS2DC_dNObN72m0ucgi7CsSFaRjpc5IcnZ_bpJNTC3pUXc37bgH8f0S4irpyXLz8s9ONAYYB.LIDT0sOUvdTgk_lPDnlr1GlPhCe7u54Sq.m_vwq1JQlNUzEVrEs3kOW9wiXk17BditA9OGaVE.tuepvpthDRVBhOye8qjb_ic7UZtT.lvccoGEdgvcShHSyg.YYcnShl7ks23G01hAcXrfGveEk0UncWKNmma42cYbg7bzSOY9ZZj3_hvU5rK3SjB1ADPe8bsIEe_Ba9KBhYxlWd9iyyAR_bloL9n0eeL_OQ6PoR4uGJ6VMUDn9n_ovDGvfgAfvtJs15pCcXPhYusuo1S7SJF1O3fLtR8TitmU1qW88x3SenY2U50dlRG9Y73iNUdnyYBpIHLg._pPkms26QhnuxSFfqpXcGleGXOuZ0.TNOE3Cp8VbLEOEIg6QkavgGLKyFetYhSQ879T4rfhfeEoWvwkjsO1BL2Y3n4Hp9cgfU4y3wZvT.b8qhP7QY0UTYtZkyYH.sydFUXUCec.yVGm29S.s.2N96tfr4qWaI0qntRE.X5MVdwfbhz94n9JshmduqurjKRLlMYVWnLZ_Yderm0HDvT7dnowjyUwBx2UxUKJooauQnNU67QQECmh.HZqcm_OBysLABvdtTtaPhnvJ1QViN_UUjslToVPOs1oyxoTNRbL0VL8yxJc&oauth_version=1.0&oauth_signature=UVmcj2S8c5vqkRgAxsdQ3MQZI54%3D"
> xmlns:yahoo="http://www.yahooapis.com/v1/base.rng"
> xmlns="http://www.yahooapis.com/v1/base.rng">
> <description>subresource 'transactions' not supported.</description>
> <detail/> </error>
and
> <?xml version="1.0" encoding="UTF-8"?> <error xml:lang="en-us"
> yahoo:uri="http://fantasysports.yahooapis.com/fantasy/v2/league/370.l.4801/transactions?oauth_consumer_key=dj0yJmk9dHBIa0h4ekhTSVBnJmQ9WVdrOVlUZHFkMnhMTXpJbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD1lNQ--&oauth_nonce=503128074504907304111221170641&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1492097198&oauth_token=A%3D2.86iTDxtT4nQ.wxTcn33mgnA8dm3AeME87YRjNthVjxiwfhqKr_oWt0HBgbBeS2DC_dNObN72m0ucgi7CsSFaRjpc5IcnZ_bpJNTC3pUXc37bgH8f0S4irpyXLz8s9ONAYYB.LIDT0sOUvdTgk_lPDnlr1GlPhCe7u54Sq.m_vwq1JQlNUzEVrEs3kOW9wiXk17BditA9OGaVE.tuepvpthDRVBhOye8qjb_ic7UZtT.lvccoGEdgvcShHSyg.YYcnShl7ks23G01hAcXrfGveEk0UncWKNmma42cYbg7bzSOY9ZZj3_hvU5rK3SjB1ADPe8bsIEe_Ba9KBhYxlWd9iyyAR_bloL9n0eeL_OQ6PoR4uGJ6VMUDn9n_ovDGvfgAfvtJs15pCcXPhYusuo1S7SJF1O3fLtR8TitmU1qW88x3SenY2U50dlRG9Y73iNUdnyYBpIHLg._pPkms26QhnuxSFfqpXcGleGXOuZ0.TNOE3Cp8VbLEOEIg6QkavgGLKyFetYhSQ879T4rfhfeEoWvwkjsO1BL2Y3n4Hp9cgfU4y3wZvT.b8qhP7QY0UTYtZkyYH.sydFUXUCec.yVGm29S.s.2N96tfr4qWaI0qntRE.X5MVdwfbhz94n9JshmduqurjKRLlMYVWnLZ_Yderm0HDvT7dnowjyUwBx2UxUKJooauQnNU67QQECmh.HZqcm_OBysLABvdtTtaPhnvJ1QViN_UUjslToVPOs1oyxoTNRbL0VL8yxJc&oauth_version=1.0&oauth_signature=oNaLute5djkIryUEKq0zF6prVFU%3D"
> xmlns:yahoo="http://www.yahooapis.com/v1/base.rng"
> xmlns="http://www.yahooapis.com/v1/base.rng">
> <description>Invalid XML POSTed. Error code 25 at line 3, column 0 of input XML.</description>
> <detail/> </error>
I'm honestly not sure what differentiates between the two responses, as I've gotten both responses with what I'm pretty sure are the same XML inputs and POST parameters.
Does anybody have any insight?
Just ran into the same issue, and this Stack Overflow article was the only relevant link I found on the web. Decided it was time I gave back to this community...
The problem is that one of the sample XMLs on Yahoo's transaction page is wrong. The sample with the { } placeholders and without the <faab_bid> node is correct, but the FAAB example below it has <destination_team_key> instead of <source_team_key> as part of the "drop" transaction. When I made sure to use "source_team_key" instead of "destination_team_key" for the drop set of nodes, the transaction started working.
I'm guessing that Error Code 25 is a generic error indicating that some part of the XML is wrong or unexpected. If this doesn't resolve your issue, try starting with the first add/drop sample XML and filling in values one by one.
Hope this helps.
-Igor
I am using Net::HTTP::Post to send a request to a pre-determined url, like so:
my_url = '/path/to/static/url'
my_string_parameter = 'objectName=objectInfo'
my_other_string_parameter = 'otherObjectName=otherObjectInfo'
request = Net::HTTP::Post.new(my_url)
request.body = my_string_parameter
However, I know that my_url expects two string parameters. I have both parameters ready (they're statically generated) to be passed in. Is there a way to pass in multiple strings - both my_string_parameter as well as my_other_string_parameter to a post request via Ruby on Rails?
EDIT: for clarity's sake, I'm going to re-explain everything in a more organized fashion. Basically, what I have is
my_url = 'path/to/static/url'
# POST requests made to this url require 2 string parameters,
# required_1 and required_2
param1 = 'required_1=' + 'param1_value'
param2 = 'requred_2=' + 'param2_value'
request = request.NET::HTTP::Post.new(my_url)
If I try request.body = param1, then as expected I get an error saying "Required String parameter 'required_2' is not present". Same with request.body=param2, the same error pops up saying 'required_1' is not present. I'm wondering if there is a way to pass in BOTH parameters to request.body? Or something similar?
Try this.
uri = URI('http://www.example.com')
req = Net::HTTP::Post.new(uri)
req.set_form_data('param1' => 'data1', 'param2' => 'data2')
Alternative
uri = URI('http://www.example.com/')
res = Net::HTTP.post_form(uri, 'param1' => 'data1', 'param2' => 'data2')
puts res.body
For more request like Get or Patch you can refer This offical doc.
You can send it like this.
data = {'params' => ['parameter1', 'parameter2']}
request = Net::HTTP::Post.new(my_url)
request.set_form_data(data)
If your params are string:
url = '/path/to/controller_method'
my_string_parameter = 'objectName=objectInfo'
my_other_string_parameter = 'otherObjectName=otherObjectInfo'
url_with_params = "#{url}?#{[my_string_parameter, my_other_string_parameter].join('&')}"
request = Net::HTTP::Post.new(url_with_params)
If your params are hash It would be easier
your_params = {
'objectName' => 'objectInfo',
'otherObjectName' => 'otherObjectInfo'
}
url = '/path/to/controller_method'
url_with_params = "#{url}?#{your_params.to_query}"
request = Net::HTTP::Post.new(url_with_params)