CDON API RESTful Api GET request - python-3.x

I'm currently working on fetching customer data from cdon, it's an e-commerce platform. They have their API documentation here:
CDON Api Docu
First let me show you my code:
myToken = '<token here>'
myUrl = 'https://admin.marketplace.cdon.com/api/reports/d8578ef8-723d-46cb-bb08-af8c9b5cca4c'
head = {'Authorization': 'token {}'.format(myToken),
'Status':'Online',
'format':'json'}
filters = '?filter={"Status":["Online"],"format": ["json"] }}'
response = requests.get(myUrl + filters, headers=head)
report = response.json()
print(report.products)
This is returning only the parameters. like for example at at this JSON: CDON Github
Status has a value Online this online is a group of itemsthat I only want to get.
What I'm trying to get is a response like this:
{
"Products": [
{
"SKU": "322352",
"Title": "Fabric Cover",
"GTIN": "532523626",
"ManufacturerArticleNumber": "",
"StatusCDON": "Online",
"ExposeStatusCDON": "Buyable",
"InStock": 0,
"InStockCDON": 0,
"CurrentPriceSE": null,
"OrdinaryPriceSE": null,
"CurrentPriceCDONSE": 299.0000,
"OrdinaryPriceCDONSE": null,
"CurrentPriceDK": null,
"OrdinaryPriceDK": null,
"CurrentPriceCDONDK": null,
"OrdinaryPriceCDONDK": null,
"CurrentPriceNO": null,
"OrdinaryPriceNO": null,
"CurrentPriceCDONNO": null,
"OrdinaryPriceCDONNO": null,
"CurrentPriceFI": null,
"OrdinaryPriceFI": null,
"CurrentPriceCDONFI": null,
"OrdinaryPriceCDONFI": null
},
Which means the full list of the items that are Online
How should I put this... among all the API's I tried this one is very confusing, is this even RestFul? If I can achieve the python equivalent of this C# sample code:
public string Post(Guid repordId, string path)
{
var filter = new JavaScriptSerializer().Serialize(new
{
States = new[] { "0" } // Pending state
});
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair("ReportId", repordId.ToString()),
new KeyValuePair("format", "json"),
new KeyValuePair("filter", filter)
});
var httpClient = new HttpClient() { BaseAddress = new Uri("https://admin.marketplace.cdon.com/") };
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("api", ApiKey);
var response = httpClient.PostAsync(path, content).Result;
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync().Result;
}
I may be able to undestand how this API works, the response that I got was taken manually from their report function in JSON format.
Image
I made many attempts and at that code ( my code ) I stopped, being on this for 4 hours made me give up and ask. Trust that I have searched as many references as I could. It's really confusing.
How do I get the response that I want? Filtering via url? or via header? is this even restful? Help T_T

The documentation states in the first line, emphasis mine:
In order to generate a report you perform a POST call to the reports API with the parameters you wish to use for the report.
Your Python code does not make a POST request, you are trying a GET request. The documentation goes on
[...] to filter on Swedish orders you set the CountryCodes
attribute to “Sweden” and to get returned and cancelled orders you set
the States attribute to 2 and 3. So in the end the filter would look
like this:
{
"CountryCodes": [ "Sweden" ],
"States": ["2", "3"]
}
So you need to prepare a filter object (a dictionary in Python) with the filters you want. Luckily the Python syntax for dictionaries is equivalent (Python is flexible and also allows single-quoted strings):
filter = {
'CountryCodes': [ 'Sweden' ],
'States': [ '0' ]
}
The documentation goes on
You then post the parameters as form data (content-type:
application/x-www-form-urlencoded) so the request body would look like
this:
ReportId=d4ea173d-bfbc-48f5-b121-60f1a5d35a34&format=json&filter={"CountryCodes":["Sweden"],"States":["2","3"]}
application/x-www-form-urlencoded is the default for HTTP post, the requests module knows that and does this for you automatically. All you need to do is to prepare a data dict which will contain the data you want to post.
data = {
'ReportId': 'd4ea173d-bfbc-48f5-b121-60f1a5d35a34',
'format': 'json'
'filter': json.dumps(filter)
}
The filter parameter is supposed to be in JSON format. You must encode that yourself via json.dumps().
import json
head = { ... as above }
filter = { ... as above }
data = { ... as above }
response = requests.post(url, data, header=head)
I'll leave figuring out setting the Authorization header properly as an exercise for you. Partly because it isn't hard, partly because I have no intention of creating an API key with this website just for testing this and partly because it's entirely possible that your current header already works.

Related

Netsuite restlet: src property must be a valid json object

I'm attempting to write a RESTlet script to pull over some work order data to another web application (Tulip), but when I attempt to run the script, I receive the following error output:
"src property must be a valid json object"
My get request does have the "Content-Type: application/json" header.
Here is the script I'm attempting to run for reference (the script is very basic at the moment as I just try to figure this all out):
function getWOSoftLock(){
var WOColumns = new Array();
WOColumns[0] = new nlobjSearchColumn('tranid');
WOColumns[1] = new nlobjSearchColumn('status');
var filter = new nlobjSearchFilter(
'status',
null,
'is',
'Released',
null
);
var dataoutput = nlapiSearchRecord(
'workorder',
null,
filter,
WOColumns
);
return dataoutput
}
Any help would be much appreciated as I'm new at this. If I do not include the Content-Type: application/json header, I receive "Invalid data format. You should return text."
The SS1 search results were funny beasts and did not used to serialize properly.
You should do something like:
function getWOSoftLock() {
var dataoutput = nlapiSearchRecord(
'workorder',
null,
new nlobjSearchFilter('status', null, 'is', 'Released'),
[
new nlobjSearchColumn('tranid'),
new nlobjSearchColumn('status')
]
);
if(!dataoutpout) {
nlapiLogExecution('AUDIT', 'No released work orders');
return [];
}
return dataoutput.map(function(res){
return {
tranid:res.getValue('tranid'),
status:res.getValue('status')
};
});
}

Training Microsoft Custom Vision model via rest api

I am working on a simple nodejs console utility that will upload images for the training of a Custom Vision model. I do this mainly because the customvision web app won't let you tag multiple images at once.
tl;dr: How to post images into the CreateImagesFromFiles API endpoint?
I cannot figure out how to pass images that I want to upload. The documentation just defines a string as a type for one of the properties (content I guess). I tried passing path to local file, url to online file and even base64 encoded image as a string. Nothing passed.
They got a testing console (blue button "Open API testing console" at the linked docs page) but once again... it's vague and won't tell you what kind of data it actually expects.
The code here isn't that relevant, but maybe it helps...
const options = {
host: 'southcentralus.api.cognitive.microsoft.com',
path: `/customvision/v2.0/Training/projects/${projectId}/images/files`,
method: 'POST',
headers: {
'Training-Key': trainingKey,
'Content-Type': 'application/json'
}
};
const data = {
images: [
{
name: 'xxx',
contents: 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAEklEQVR42mP8z8AARKiAkQaCAFxlCfyG/gCwAAAAAElFTkSuQmCC',
tagIds: [],
regions: []
}
],
tagIds: []
}
const req = http.request(options, res => {
...
})
req.write(JSON.stringify(data));
req.end();
Response:
BODY: { "statusCode": 404, "message": "Resource not found" }
No more data in response.
I got it working using the "API testing console" feature, so I can help you to identify your issue (but sorry, I'm not expert in node.js so I will guide you with C# code)
Format of content for API
You are right, the documentation is not clear about the content the API is waiting for. I made some search and found a project in a Microsoft's Github repository called Cognitive-CustomVision-Windows, here.
What is saw is that they use a class called ImageFileCreateEntry whose signature is visible here:
public ImageFileCreateEntry(string name = default(string), byte[] contents = default(byte[]), IList<System.Guid> tagIds = default(IList<System.Guid>))
So I guessed it's using a byte[].
You can also see in their sample how they did for this "batch" mode:
// Or uploaded in a single batch
var imageFiles = japaneseCherryImages.Select(img => new ImageFileCreateEntry(Path.GetFileName(img), File.ReadAllBytes(img))).ToList();
trainingApi.CreateImagesFromFiles(project.Id, new ImageFileCreateBatch(imageFiles, new List<Guid>() { japaneseCherryTag.Id }));
Then this byte array is serialized with Newtonsoft.Json: if you look at their documentation (here) it says that byte[] are converted to String (base 64 encoded). That's our target.
Implementation
As you mentioned that you tried with base64 encoded image, I gave it a try to check. I took my StackOverflow profile picture that I downloaded locally. Then using the following, I got the base64 encoded string:
Image img = Image.FromFile(#"\\Mac\Home\Downloads\Picto.jpg");
byte[] arr;
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
arr = ms.ToArray();
}
var content = Convert.ToBase64String(arr);
Later on, I called the API with no tags to ensure that the image is posted and visible:
POST https://southcentralus.api.cognitive.microsoft.com/customvision/v2.2/Training/projects/MY_PROJECT_ID/images/files HTTP/1.1
Host: southcentralus.api.cognitive.microsoft.com
Training-Key: MY_OWN_TRAINING_KEY
Content-Type: application/json
{
"images": [
{
"name": "imageSentByApi",
"contents": "/9j/4AAQSkZJRgA...TOO LONG FOR STACK OVERFLOW...",
"tagIds": [],
"regions": []
}
],
"tagIds": []
}
Response received: 200 OK
{
"isBatchSuccessful": true,
"images": [{
"sourceUrl": "imageSentByApi",
"status": "OK",
"image": {
"id": "GENERATED_ID_OF_IMAGE",
"created": "2018-11-05T22:33:31.6513607",
"width": 328,
"height": 328,
"resizedImageUri": "https://irisscuprodstore.blob.core.windows.net/...",
"thumbnailUri": "https://irisscuprodstore.blob.core.windows.net/...",
"originalImageUri": "https://irisscuprodstore.blob.core.windows.net/..."
}
}]
}
And my image is here in Custom Vision portal!
Debugging your code
In order to debug, you should 1st try to submit your content again with tagIds and regions arrays empty like in my test, then provide the content of the API reply

Getting image URL from Contentful entry id

I need to get an image URL from Contentful entry id.
I am getting such an JSON from Contentful query
{
"sys":{
"space":{
"sys":{
"type":"Link",
"linkType":"Space",
"id":"8v1e7eaw70p2"
}
},
"id":"1JfEwVlD9WmYikE8kS8iCA",
"type":"Entry",
"createdAt":"2018-02-28T18:50:08.758Z",
"updatedAt":"2018-02-28T18:50:08.758Z",
"revision":1,
"contentType":{
"sys":{
"type":"Link",
"linkType":"ContentType",
"id":"image"
}
},
"locale":"en-US"
},
"fields":{
"name":"heat",
"image":{
"sys":{
"type":"Link",
"linkType":"Asset",
"id":"6Inruq2U0M2kOYsSAu8Ywk"
}
}
}
}
I am using JS driver they provide:
client.getEntry()
so how to go thru that link: 6Inruq2U0M2kOYsSAu8Ywk ?
Unfortunately, the js SDK will not be able to resolve links when using the single entry endpoint i.e client.getEntry() because there won't be enough data.
When thing I always recommend to work around this is to use the collection endpoint with a query the desired id as a query param. This way you will always get the desired entry with all it's linked data.
Your code should look something like this
client.getEntries({'sys.id': '6Inruq2U0M2kOYsSAu8Ywk'})
.then(response => console.log(response.items[0].fields.image.fields.file.url))
I hope that helps.
Best,
Khaled
Use client.getEntries({'sys.id': '1JfEwVlD9WmYikE8kS8iCA'})
To get the entry fields and the asset fields.
You can also patch the assets to the fields by running this after fetching the data:
/* Patch all the assets to the fields */
const patchAssets = (fields, assets) => {
Object.keys(fields).forEach(function (key) {
let obj = fields[key];
if (obj.sys && obj.sys.linkType === 'Asset') {
const assetId = obj.sys.id;
const matchAsset = assets.find(asset => {
return asset.id === assetId;
});
obj.file = matchAsset;
}
});
return fields;
};
Another way to get image url is to use getAsset('<asset_id>'). So first, using the getEntry() method, you need to get the entry data, then extract the id from the field: fields.image.sys.id, and pass it to the getAsset method.

Groovy ws-lite REST post with json payload

I am trying to call a REST API via the ws-lite. Get works great but calling a post with a json payload not working. Bit of a noob but from github notes wiki i am not sure https://github.com/jwagenleitner/groovy-wslite
#Grab('com.github.groovy-wslite:groovy-wslite:1.1.2')
import wslite.rest.*
import groovy.json.JsonBuilder
def client = new RESTClient("https://d.la10.salesforceliveagent.com/chat/rest")
def response = client.get(path:'/System/SessionId', headers:['X-LIVEAGENT- API-VERSION':'36','X-LIVEAGENT-AFFINITY':'null'])
def skey = response.json.key
def sid = response.json.id
def stoken = response.json.affinityToken
client.post(path:'/Chasitor/ChasitorInit', headers:['X-LIVEAGENT-API-VERSION':'36','X-LIVEAGENT-SESSION-KEY':skey,'X-LIVEAGENT-AFFINITY':stoken,'X-LIVEAGENT-SEQUENCE':'1'])
{
type ContentType.application/json
json {
"organizationId": "00D28000000f5N9",
"deploymentId": "572280000008R6L",
"buttonId": "573D000000000OC",
"agentId": "005B0000000F3b2",
"doFallback": true,
"sessionId": sid,
"userAgent": "Lynx/2.8.8",
"screenResolution": "2560x1440",
"visitorName": "Jon A",
"prechatDetails": [],"prechatEntities": [],
"receiveQueueUpdates": true,
"isPost": true
}
}
Curious if anyone has an example using for a post with REST via ws-lite from groovy. I simply get compilation failures based on the json format. Any insight appreciated
The json method inside the post block is expecting a Map as the parameter so the following would work (basically change the braces to brackets):
....
....
client.post(path:'/Chasitor/ChasitorInit', headers:['X-LIVEAGENT-API-VERSION':'36','X-LIVEAGENT-SESSION-KEY':skey,'X-LIVEAGENT-AFFINITY':stoken,'X-LIVEAGENT-SEQUENCE':'1'])
{
type ContentType.application/json
json ["organizationId": "00D28000000f5N9",
"deploymentId": "572280000008R6L",
"buttonId": "573D000000000OC",
"agentId": "005B0000000F3b2",
"doFallback": true,
"sessionId": sid,
"userAgent": "Lynx/2.8.8",
"screenResolution": "2560x1440",
"visitorName": "Jon A",
"prechatDetails": [],"prechatEntities": [],
"receiveQueueUpdates": true,
"isPost": true]
}
Since the Map is the only parameter you should also be able to omit the brackets.

Gmail API - How to Request Only one Name/Value Pair from Email Payload Headers?

How can we capture only the email address in an api response for the gmail API. The
fields parameter is set to payload/headers, which returns way more data than we need in the response.
All we need is the value from one name/value pair in the JSON response; for example
The full response as we have it now looks something like this
{
"payload": {
"headers": [
{
"name": "Delivered-To",
"value": "xxxxxxx"
{
"name": "Received",
"value": "xxxxxxxx"
},
{
"name": "Received-SPF",
"value": "----"
},......
{
"name": "To",
"value": "xxxxxxx"
}, ...... E.T.C........E.T.C ......
/*All we want is one name/value pair to be returned e.g. */
{
"payload": {
"headers": [
{
"name": "X-Failed-Recipients",
"value": "............."
}
]
}
A better question might be is there a better way to capture bounced/returned mail than this via the gmail API?
Also, is it possible to request an XML response instead of JSON. How can that be done for the gmail API?
Thanks !!
You can do messages.get(format=METADATA, metadataIncludeHeaders=["To", "From", "Subject"]) for example, now to just request the specific headers you care about . Note this only works with the metadata format (it won't include the body also, if you want all the body you get the full email. Based on the list of headers, shouldn't be too hard to turn that into a map/dict of key => [list, of, values].
As to your second question, yes you can definitely request response in any format you want. That's a standard Google API question though. I can't find a good reference (surely some searching will) but typically you can set an "alt=xml" or "alt=" query parameter to get response in that format. Not sure how this is exposed in any particular client library.
First you need to get your message payload and then you want to use the getall method to return a list of headers and then you can use the getitem to pull any specific header you want from that list.
I converted the String into a String and took it as a JSON Array and iterated through it to take the JSON Object that I required.
private static DirtyMail getHeaderParts(DirtyMail mail, List<MessagePartHeader> headers)
{
try {
//Convert the header into JSON Array for easy processing of data.
JSONArray headerArray = new JSONArray(headers.toString());
for(int n = 0; n < headerArray.length() ; n++) {
JSONObject jsonObject = headerArray.getJSONObject(n);
//Pull date
if(jsonObject.get(Constants.DATE)!=null) {
mail.setDate(jsonObject.getString(Constants.DATE));
}
//Pull Subject
//Pull reply-to address
//Pull delivered-from address
//Pull delivered-to address
Log.d(TAG, "JSON Object : "+ jsonObject);
}
//Log.d(TAG,"header String: "+headers.toString());
} catch (Exception e) {
e.printStackTrace();
}
return mail;
}
I kept these values in the Constant class :
// Data pulled from inside the header
public static final String DATE = "Date";
public static final String SUBJECT = "Subject";
public static final String REPLY_TO = "Reply-To";
public static final String DELIVERED_TO = "Delivered-To";
public static final String FROM = "From";
I don't know if this is the best fix for this, but this works and gives the data as I require.

Resources