Django Testing Post request exhaust the data - django-1.9

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

Related

Bulk Item Setup endpoint on Walmart no working with JSON

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.

Why am I getting "no implicit conversion of String into Integer" when trying to get Nested JSON attribute?

My Rails app is reading in JSON from a Bing API, and creating a record for each result. However, when I try to save one of the nested JSON attributes, I'm getting Resource creation error: no implicit conversion of String into Integer.
The JSON looks like this:
{
"Demo": {
"_type": "News",
"readLink": "https://api.cognitive.microsoft.com/api/v7/news/search?q=european+football",
"totalEstimatedMatches": 2750000,
"value": [
{
"provider": [
{
"_type": "Organization",
"name": "Tuko on MSN.com"
}
],
"name": "Hope for football fans as top European club resume training despite coronavirus threat",
"url": "https://www.msn.com/en-xl/news/other/hope-for-football-fans-as-top-european-club-resume-training-despite-coronavirus-threat/ar-BB12eC6Q",
"description": "Bayern have returned to training days after leaving camp following the outbreak of coronavirus. The Bundesliga is among top European competitions suspended."
}
}
The attribute I'm having trouble with is [:provider][:name].
Here's my code:
def handle_bing
#terms = get_terms
#terms.each do |t|
news = get_news(t)
news['value'].each do |n|
create_resource(n)
end
end
end
def get_terms
term = ["European football"]
end
def get_news(term)
accessKey = "foobar"
uri = "https://api.cognitive.microsoft.com"
path = "/bing/v7.0/news/search"
uri = URI(uri + path + "?q=" + URI.escape(term))
request = Net::HTTP::Get.new(uri)
request['Ocp-Apim-Subscription-Key'] = accessKey
response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
http.request(request)
end
response.each_header do |key, value|
# header names are coerced to lowercase
if key.start_with?("bingapis-") or key.start_with?("x-msedge-") then
puts key + ": " + value
end
end
return JSON(response.body)
end
def create_resource(news)
Resource.create(
name: news['name'],
url: news['url'],
description: news['description'],
publisher: news['provider']['name']
)
end
I looked at these questions, but they didn't help me:
Extract specific field from JSON nested hashes
No implicit conversion of String into Integer (TypeError)?
Why do I get "no implicit conversion of String into Integer (TypeError)"?
UPDATE:
I also tried updating the code to:
publisher: news['provider'][0]['name'], but I received the same error.
because "provider" is an array.
it should be accessed with index.
[:value][0][:provider][0][:name]
same goes with "value".

Converting python to ruby params issue

I'm converting some python code to ruby. It's going ok so far, except I'm running into some issues with parameters. The python code is:
def sign_policy(policy):
signed_policy = base64.b64encode(policy)
signature = base64.b64encode(hmac.new(
app.config.get('AWS_CLIENT_SECRET_KEY'), signed_policy, hashlib.sha1).
digest())
return { 'policy': signed_policy, 'signature': signature }
def sign_headers(headers):
headers = bytearray(headers, 'utf-8') # hmac doesn't want unicode
return {
'signature': base64.b64encode(hmac.new(
app.config.get('AWS_CLIENT_SECRET_KEY'), headers, hashlib.sha1).
digest())
}
def s3_signature():
request_payload = request.get_json()
if request_payload.get('headers'):
response_data = sign_headers(request_payload['headers'])
else:
response_data = sign_policy(request.data)
return jsonify(response_data)
My ruby version so far is:
def create
puts params[:headers]
if params[:headers].present?
response_data = sign_headers(params[:headers])
else
response_data = sign_policy(params)
end
render json: response_data
end
private
def sign_policy(policy)
signed_policy = Base64.encode64(policy).gsub("\n","")
signature = Base64.encode64(
OpenSSL::HMAC.digest(
OpenSSL::Digest.new('sha1'),
AWS_SECRET_KEY, signed_policy)
).gsub("\n","")
return { 'policy': signed_policy, 'signature': signature }
end
def sign_headers(headers)
#headers = bytearray(headers, 'utf-8')
return {
'signature': Base64.encode64(
OpenSSL::HMAC.digest(
AWS_SECRET_KEY, headers, OpenSSL::Digest.new('sha1')
))
}
end
I'm running into the following issue: no implicit conversion of ActionController::Parameters into String, which makes it obvious whats wrong (Params is a hash and it needs to be a string)..However, what is being passed in the python code? I'm missing what I should be passing?
Most probably you need to pass a single value, String or any other, it depends on the data type you need to pass to use Base64.encode64(policy).
As you're passing params[:headers] in the sign_headers method call, in the case of the sign_policy call, you're passing params which is the whole ActionController::Parameters, try debugging the values you sent on params to send the needed value.

Can't get values from json stored in database

I'm trying to write app which logs json msgs to db. That part I got. The problem comes with getting that json back. I can't get values from it.
I've tried to get raw msgs from db and getting values from this ( json gem seems not to see it as json)
I've tried to parse it via .to_json , but it doesn't seem to work either. Maby you have some idea how to get it?
Thanks in advance
table:
mjl_pk bigserial
mjl_body text <- JSON is stored here
mjl_time timestamp
mjl_issuer varchar
mjl_status varchar
mjl_action varchar
mjl_object varchar
mjl_pat_id varchar
mjl_stu_id varchar
code:
#Include config catalog, where jsonadds is
$LOAD_PATH << 'config'
#Requirements
require 'sinatra'
require 'active_record'
require 'json'
require 'jsonadds'
require 'RestClient'
#Class for db
class Mjl < ActiveRecord::Base
#table name
self.table_name = "msg_json_log"
#serialization
serialize :properties, JSON
#overwrite ActiveRecord id with "mjl_pk"
def self.primary_key
"mjl_pk"
end
end
#Get json msg and write it to db
post '/logger' do
content_type :json
#Check if msg is json
if JSON.is_json?(params[:data])
#insert data into db
msg = Mjl.create(:mjl_body => params[:data] ,:mjl_issuer => 'LOGGER', :mjl_action => 'test', :mjl_object =>'obj')
else
#else return error
puts "Not a JSON \n" + params[:data]
end
end
#Get json with id = params[:id]
get '/json/:id' do
content_type :json
#Get json from db
json_body = Mjl.where(mjl_pk: params[:id]).pluck(:mjl_body)
puts json_body
json_body = json_body.to_json
puts json_body
#Get 'patientData' from json
puts json_body['key']
puts json_body[0]['key']
end
Output:
{
"key": "I am a value",
"group": {
"key2": "Next value",
"key3": "Another one"
},
"val1": "Val"
}
["{\n \"key\": \"I am a value\",\n \"group\": {\n \"key2\": \"Next value\",\n \"key3\": \"Another one\"\n },\n \"val1\": \"Val\"\n}"]
key
<--empty value from 'puts json_body[0]['key']'
I've also created a JSON Log in my project like this, this might help you...
In Controller
current_time = Time.now.strftime("%d-%m-%Y %H:%M")
#status_log = {}
#arr = {}
#arr[:status_id] = "2.1"
#arr[:status_short_desc] = "Order confirmed"
#arr[:status_long_desc] = "Order item has been packed and ready for shipment"
#arr[:time] = current_time
#status_log[2.1] = #arr
#status_log_json = JSON.generate(#status_log)
StoreStock.where(:id => params[:id]).update_all(:status_json => #status_log_json)
In View
#json_status = JSON.parse(ps.status_json) # ps.status_json contails raw JSON Log
= #json_status['2.1']['status_long_desc']
Hope this might help you.
when you are doing
json_body = Mjl.where(mjl_pk: params[:id]).pluck(:mjl_body)
you already have json
so no need doing
json_body = json_body.to_json
Since doing this you get the string representation of json, just remove that line and you will get all the values.
Finally I've found how to do it.
I saw explanation on JSON methods at:
from json to a ruby hash?
Code which works for me:
json_body = Mjl.where(mjl_pk: params[:id]).pluck(:mjl_body)
puts json_body
jparsed = JSON.parse(json_body[0])
puts jparsed['key']

How to flatten a JSON response when using ActiveResource?

I have an API and a client app, and I am using rails with ActiveResource.
I have a Recruiter model that inherits from ActiveResource::Base
Let's say on the client side I write:
dave = Recruiter.new(email: "email#recruiter.com", password: "tyu678$--è", full_name: "David Blaine", company: "GE")
dave.save
The request I send is formatted like so:
{"recruiter":{
"email": "email#recruiter.com",
"password": "tyu678$--è",
"full_name": "David Blaine",
"company": "GE"
}
}
and the Json response I get from the API is formatted like:
{"recruiter":{
"email": "email#recruiter.com",
"password": "tyu678$--è",
"full_name": "David Blaine",
"company": "GE",
"foo": "bar"
},
"app_token":"ApfXy8YYVtsipFLvJXQ"
}
The problem is that this will let me access the app token with dave.app_token but I can't for instance write dave.foo, which raises an error.
Is there a way to flatten the response or read through it recursively si that I can access all of my instance's attributes while keeping the API response formatted as it is?
Looking through the whole ActiveResource process, you can overwrite the load method in your Recruiter model.
I just added the code in the #HACK section which "flatten" your attributes.
def load(attributes, remove_root = false, persisted = false)
raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
#prefix_options, attributes = split_options(attributes)
# HACK
if !attributes[:app_token].nil?
attributes[:recruiter]["app_token"] = attributes[:app_token]
attributes = attributes[:recruiter]
end
# /HACK
if attributes.keys.size == 1
remove_root = self.class.element_name == attributes.keys.first.to_s
end
attributes = ActiveResource::Formats.remove_root(attributes) if remove_root
attributes.each do |key, value|
#attributes[key.to_s] =
case value
when Array
resource = nil
value.map do |attrs|
if attrs.is_a?(Hash)
resource ||= find_or_create_resource_for_collection(key)
resource.new(attrs, persisted)
else
attrs.duplicable? ? attrs.dup : attrs
end
end
when Hash
resource = find_or_create_resource_for(key)
resource.new(value, persisted)
else
value.duplicable? ? value.dup : value
end
end
self
end

Resources