This question may very well be a general API question, but the API I am using is the Connectwise Tickets API.
I'm writing in VB and I'm getting my list of tickets(i) then I'm setting the following variable:
currentTicket = tickets(i)
So that I can reference value like currentTicket.Source.Name and save the info to a DB.
As long as the Connectwise user put something into the "Source" field, everything works fine. I can reference that property and log it to the database or whatever else I want to do. If they left it blank though, even trying to look at currentTicket.Source.Name stops my program in it's tracks. It doesn't crash or error out, it just doesn't go past the line of code referencing the empty field.
Since it won't even let me reference currentTicket.Source.Name for example, I am unable to check to see if currentTicket.Source.Name = "" or Is Nothing.
What am I missing? Is there a way to check and see if the property even exists for a given API response?
Any help would be appreciated.
EDIT:
OK so I decided to take the Ticket object and grab the raw json and send it to the command line output.
When a ticket has the source field filled in, this is what that section of the JSON looks like:
...
},
"servicelocation": {
"id":4,
"name":"remote",
"_info":{
"location_href":"https://API_url"
}
},
"source":{
"id":4,
"name":"Email Connector",
"_info": {
"source_href":"https://API_url"
}
},
"severity": "Medium",
"impact":"Medium",
...
When the source field in the application has been left blank, the JSON looks like this instead:
...
},
"servicelocation": {
"id":4,
"name":"remote",
"_info":{
"location_href":"https://API_url"
}
},
"severity": "Medium",
"impact":"Medium",
...
That behavior is probably normal, I'm obviously just missing the knowledge of how to deal with it. I feel like I should be able to test if the property of the object is non-existent the same way I test if it's null or "", using Tickets(i).Source.Name, but I get no exceptions, no errors, no crashes, the program just sits their waiting for a response to "What's Source Name's value?"
I mean I suppose I could parse out the entire JSON response, create my own private object, assign values as they exist and then set my own property values so I could then set mySourceName = "" When it doesn't exist in the JSON response, but that seems like a lot of work when I only care about like 10 fields and it's a pretty large json response.
Is that the normal way of doing things with APIs?
Related
Overview
I'm using Ember data and have a JSONAPI. Everything works fine until I have a more complex object (let's say an invoice for a generic concept) with an array of items called lineEntries. The line entries are not mapped directly to a table so need to be stored as raw JSON object data. The line entry model also contains default and computed values. I wish to store the list data as a JSON object and then when loaded back from the store that I can manipulate it as normal in Ember as an array of my model.
What I've tried
I've looked at and tried several approaches, the best appear to be (open to suggestions here!):
Fragments
Replace problem models with fragments
I've tried making the line entry model a fragment and then referencing the fragment on the invoice model as a fragmentArray. Line entries add to the array as normal but default values don't work (should they?). It creates the object and I can store it in the backend but when I return it, it fails with either a normalisation issue or a serialiser issue. Can anyone state the format the data be returned in? It's confusing as normalising the data seems to require JSONAPI but the fragment requires JSON serialiser. I've tried several combinations but no luck so far. My line entries don't have actual ids as the data is saved and loaded as a block. Is this an issue?
DS.EmbeddedRecordsMixin
Although not supported in JSONAPI, it sounds possible to use JSONAPI and then switch to JSONSerializer or RESTSerializer for the problem models. If this is possible could someone give me a working example and the JSON format that should be returned by the API? I have header authorisation and other such data so would I still be able to set this at the application level for all request not using my JSONAPI?
Ember-data-save-relationships
I found an add on here that provides an add on to do this. It seems more involved than the other approaches but when I've tried this I can send the data up by setting a the data as embedded. Great! But although it saves it doesn't unwrap it correct and I'm back with the same issues.
Custom serialiser
Replace the models serialiser with something that takes the data and sends it as plain JSON data and then deserialises back into something Ember can use. This sounds similar to the above but I do the heavy lifting. The only reason to do this is because all examples for the above solutions are quite light and don't really show how to set this up with an actual JSONAPI set up that would need it.
Where I am and what I need
Basically all approaches lead to saving the JSON fine but the return JSON from the server not being the correct format or the deserialisation failing but it's unclear what it should be or what needs to change without breaking the existing JSONAPI models that work fine.
If anyone know the format for return API data it may resolve this. I've tried JSONAPI with lineEntries returning the same format as it saved. I've tried placing relationship sections like the add on suggested and I've also tried placing relationship only data against the entries and an include section with all the references. Any help on this would be great as I've learned a lot through this but deadlines a looming and I can't see a viable solution that doesn't break as much as it fixes.
If you are looking for return format for relational data from the API server you need to make sure of the following:
Make sure the relationship is defined in the ember model
Return all successes with a status code of 200
From there you need to make sure you return relational data correctly. If you've set the ember model for the relationship to {async: true} you need only return the id of the relational model - which should also be defined in ember. If you do not set {async: true}, ember expects all relational data to be included.
return data with relationships in JSON API specification
Example:
models\unicorn.js in ember:
import DS from 'ember-data';
export default DS.Model.extend({
user: DS.belongsTo('user', {async: true}),
staticrace: DS.belongsTo('staticrace',{async: true}),
unicornName: DS.attr('string'),
unicornLevel: DS.attr('number'),
experience: DS.attr('number'),
hatchesAt: DS.attr('number'),
isHatched: DS.attr('boolean'),
raceEndsAt: DS.attr('number'),
isRacing: DS.attr('boolean'),
});
in routes\unicorns.js on the api server on GET/:id:
var jsonObject = {
"data": {
"type": "unicorn",
"id": unicorn.dataValues.id,
"attributes": {
"unicorn-name" : unicorn.dataValues.unicornName,
"unicorn-level" : unicorn.dataValues.unicornLevel,
"experience" : unicorn.dataValues.experience,
"hatches-at" : unicorn.dataValues.hatchesAt,
"is-hatched" : unicorn.dataValues.isHatched,
"raceEndsAt" : unicorn.dataValues.raceEndsAt,
"isRacing" : unicorn.dataValues.isRacing
},
"relationships": {
"staticrace": {
"data": {"type": "staticrace", "id" : unicorn.dataValues.staticRaceId}
},
"user":{
"data": {"type": "user", "id" : unicorn.dataValues.userId}
}
}
}
}
res.status(200).json(jsonObject);
In ember, you can call this by chaining model functions. For example when this unicorn goes to race in controllers\unicornracer.js:
raceUnicorn() {
if (this.get('unicornId') === '') {return false}
else {
return this.store.findRecord('unicorn', this.get('unicornId', { backgroundReload: false})).then(unicorn => {
return this.store.findRecord('staticrace', this.get('raceId')).then(staticrace => {
if (unicorn.getProperties('unicornLevel').unicornLevel >= staticrace.getProperties('raceMinimumLevel').raceMinimumLevel) {
unicorn.set('isRacing', true);
unicorn.set('staticrace', staticrace);
unicorn.set('raceEndsAt', Math.floor(Date.now()/1000) + staticrace.get('duration'))
this.set('unicornId', '');
return unicorn.save();
}
else {return false;}
});
});
}
}
The above code sends a PATCH to the api server route unicorns/:id
Final note about GET,POST,DELETE,PATCH:
GET assumes you are getting ALL of the information associated with a model (the example above shows a GET response). This is associated with model.findRecord (GET/:id)(expects one record), model.findAll(GET/)(expects an array of records), model.query(GET/?query=&string=)(expects an array of records), model.queryRecord(GET/?query=&string=)(expects one record)
POST assumes you at least return at least what you POST to the api server from ember , but can also return additional information you created on the apiServer side such as createdAt dates. If the data returned is different from what you used to create the model, it'll update the created model with the returned information. This is associated with model.createRecord(POST/)(expects one record).
DELETE assumes you return the type, and the id of the deleted object, not data or relationships. This is associated with model.deleteRecord(DELETE/:id)(expects one record).
PATCH assumes you return at least what information was changed. If you only change one field, for instance in my unicorn model, the unicornName, it would only PATCH the following:
{
data: {
"type":"unicorn",
"id": req.params.id,
"attributes": {
"unicorn-name" : "This is a new name!"
}
}
}
So it only expects a returned response of at least that, but like POST, you can return other changed items!
I hope this answers your questions about the JSON API adapter. Most of this information was originally gleamed by reading over the specification at http://jsonapi.org/format/ and the ember implementation documentation at https://emberjs.com/api/data/classes/DS.JSONAPIAdapter.html
I'm performing API testing of basic CRUD functionality. For a record creation, I need to take the response, modify a field, and save the full thing off as a file so i can be recalled for an Update.
Here is what occurs for the creation.
CREATE POST Body
{
"id": 0,
"name": "apiTest: Code Rate ${__Random(1,10000000)}",
"deletable": false,
"codePeriods": null
}
CREATE RESPONSE Body
{
"name": "apiTest: Code Rate 869531",
"id": 1257745140,
"deletable": true,
"codePeriods": null,
"lastChangedDateTime": "03/01/2016 10:13:09",
"lastChangedTime": 36789410,
"createdUser": {
"id": 1003941890,
"userName": "N9SFBulkUser"
},
"lastChangedDate": 736024,
"lastChangedUser": {
"id": 1003941890,
"userName": "N9SFBulkUser"
},
"createdDateTime": "03/01/2016 10:13:09"
}
I need to change the "name" field in order to perform an UPDATE on the record.
As of now, I have:
a RegEx to extract the name field value and save it. (newCodeRate)
a Save Response to a file to save off the entire response. (newCodeRateFile)
another HTTP Request to update the record where:
Body Data = ${__fileToString(${__eval(${newCodeRateFile})},,)}
As you can see, right now it's just taking the previous response, saving it to a file and then being re-sent. This is not a proper UPDATE as the database sees nothing has changed and just ignores it. Sure, I get a 200 OK response, but it's misleading as nothing was updated. You can tell this because the Creation and Update date/times still match.
I was thinking maybe I need a BSF PostProcessor where (using Javascript):
var data = prev.getResponseDataAsString();
var object = JSON.parse(data);
vars.put("name", object.name);
But not being a developer by trade, I'm not sure how what to do with this and how to save the new name value into the saved recallable file.
I don't think you have JSON in BSF JavaScript, it is not part of Rhino
I don't think you need to store response into a file and read it, you can do it in memory.
So:
Change your __Random function to store generated value into a JMeter Variable like:
${__Random(1,10000000,randomNumber)}
Add Regular Expression Extractor as a child of CREATE request and configure it as follows:
Reference Name: anything meaningful, i.e. body
Regular Expression: (?s)(^.*)
Template: $1$
Add __Beanshell function as UPDATE request body, it should look like:
${__BeanShell(return vars.get("body").replaceAll(vars.get("randomNumber")\,"${__Random(1,10000000)}");,)}
See How to Use JMeter Functions posts series for more comprehensive information on JMeter functions.
I am using the Gmail API in Google Apps Script (which is basically Javascript), and I need to make as few calls to the API as possible, for efficiency and speed.
I'm using Users.messages: list to list the messages in a user's mailbox, and the response includes an array called messages, and for each message it includes an id and a threadId, like so:
"messages": [
{
"id": "152b93b1111c33e2",
"threadId": "152b922266c33e2"
},
{
"id": "152b93338c98cb3",
"threadId": "152b922266c33e2"
} ...
But I need the response to include more information about each message, so that I don't have to make a separate Users.messages:get call for each message.
The APIs Explorer on the Users.messages: list page says you can use the fields parameter to "specify which fields to include in a partial response."
When I click "Use fields editor" to select the three items I need, it fills the following in to the field:
messages(id,internalDate,payload)
Then when I execute the command, it shows that the GET command should look like this:
https://www.googleapis.com/gmail/v1/users/test#test.com/messages?fields=messages(id%2CinternalDate%2Cpayload)&key={YOUR_API_KEY}
However, the messages array in the results does not include the internalDate or the payload fields. It just includes the message id only, like so:
"messages": [
{
"id": "152b93b1111c33e2"
},
{
"id": "152b93338c98cb3"
} ...
It also does not include the threadId anymore, but it DOES continue to include the threadId if I select that as one of the fields, like so:
messages(id,threadId)
and the URL looks like this...
https://www.googleapis.com/gmail/v1/users/test#test.com/messages?fields=messages(id%2CthreadId)&key={YOUR_API_KEY}
And the result looks exactly like the first result above, where we weren't using the fields parameter.
So I know the fields parameter is actually doing something.
Thinking this might just be a limitation of the APIs Explorer, I tried making the API call in Google Apps script, but it still does not include the fields I need.
You are almost there.
When listing messages, theid and threadId of each message is all you get. You then have to get each message separately.
If you e.g. just want the internalDate of the message, it is in this request it should be specified in the fields parameter.
Request
GET https://www.googleapis.com/gmail/v1/users/me/messages/152b792a91c9c391?fields=internalDate&key={YOUR_API_KEY}
Response
{
"internalDate": "1454778787000"
}
http://apidocs.mailchimp.com/api/2.0/campaigns/list.php
I would expect that something like
{
"apikey": "12345",
"filters": {
"list_id": "abcde",
"exact": true
}
}
to return a list of total=1 if there is one campaign with list_id = "abcde", or total=0 if there isn't (using exact=true). However, instead I get the full list. Is this by design? Am I missing some other setting to filter out all mismatches? Is there a way to deal with this?
OK, so this is what I have come up with, barring some other resolution:
When the result set is returned, do a sanity check on the first result (data.data[0]).
If there is a true match, then data.data[0].list_id = list_id
If there was no match, and the full set has come back, then data.data[0].list_id cannot = list_id
This extra step is not ideal, imo, but it does do the trick.
if data.data[0].list_id === list_id
// the list is a match, return the results
else
// the list is not a match, do something else
Here's another solution:
The full-response will provide an error object if no match
"errors": [{
"filter": "list_id",
"value": "abcde",
"code": 200,
"error": "Invalid MailChimp List ID: abcde"
}]
and an empty object for a set of matches
"errors": []
So you can first process your result by looking for this error object and making the appropriate decision.
I received some clarification from MailChimp (and post it here with permission) that this is as-intended:
To provide some more information, the behavior with the campaigns/list call completing, even with an invalid filter, is expected behavior. As you mention, an error array is included in the return, and it would be possible to check that array for any errors received to determine whether or not the call completed as expected.
I can certainly see how that may be unexpected, to receive the full list of campaigns when attempting to filter. If you'd prefer that an error would be returned, without the campaign data returned, we'd recommend adding that suggestion to our feedback form here: http://mailchimp.com/contact/feedback/ That's certainly something our developers could consider for future versions of the API.
I'm still not sure why they do it this way, but I've added feedback suggesting that they return an empty result (so as not to bulk up the response with irrelevant data), or to improve the documentation.
What is the right way to updated the Model in the view, say after a successful API POST. I've a textarea, something like in a Twitter, where a user can enter text and post. The entered text must show up soon after it is posted successfully.
How to achieve this? Should I make another call to get the posts separately or is there any other way to do this?
My Code looks like
feedsResolve.getFeeds().then(function(feeds){
$scope.feeds = feeds;
}
where feedsResolve is a service returning a promise
$scope.postFeed = function(){
var postObj = Restangular.all('posts');
postObj.post( $scope.feed.text ).then(function(res){
//res contains only the new feed id
})
}
How do I update the $scope.feeds in the view?
I assume you are posting a new post and that generally posts look like:
{
id: 42,
text: 'This is my text'
}
In this case you can do something like:
$scope.postFeed = function(){
var postObj = Restangular.all('posts');
var feedText = $scope.feed.text;
postObj.post( feedText ).then(function(res){
$scope.feeds.push({ id: res.id, text: feedText});
})
};
A better practice when writing restful service though is to just have your POST return an actual JSON object with the new feed that was added (not just the id). If that were the case you could just add it to your feeds array.
If your JSON object is complex, this practice is the most common an easiest way to handle this without needing extra requests to the server. Since you already are on the server, and you've likely already created the object (in order to be able to insert it into the database), all you have to do is serialize it back out to the HTTP response. This adds little to no overhead and gives the client all the information it needs to effortlessly update.