I'm trying to call this REST API
How is one expected to add these params? maxResult
Page token
all
filter
How do I technically send the query parameters?
What part of the payload or options?
I couldn't find an example.
/**
* Checks if dataset already exists in project.
*
* #return {boolean} Returns true if dataset already exists.
*/
function datasetExists() {
// Get a list of all datasets in project.
var url =
'https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}/datasets';
var options = {
method: 'GET',
contentType: 'application/json',
payload: ""
};
var response = authUrlFetchApp.fetch(url, options);
var result = JSON.parse(response.getContentText());
Logger.log(result);
if (result.entities) {
Logger.log('Dataset with ID = %s created.', dataSet.id);
// return a list of identified entities
return result.entities;
}
All the Google Cloud API use the same definition pattern.
Path parameter, which is in the path of the url, like that
https://bigquery.googleapis.com/xxx/yyy/<pathparameters>/zzz
Query parameters that come in the URL, but at the end, after a ? and separated with &. Most of the time it's tokenPage, pageSize, filters,...
https://bigquery.googleapis.com/xxx/yyy/<pathparameters>/zzz?queryparam1=value1&queryparam2=value2
The body to provide when you perform a POST, PUT, PATCH, DELETE. in JSON (therefore you need to add the content type header when you use it).
Finally, in term of security, the APIs requires a Bearer Access Token in the Authorization header
Related
First of all, thanks for build karate it's a very useful for test API's and UI's. We are using it to test a lot of our endpoints but we would like to know if there is a way or which is the best approach to handle requests with signature as part of the request in the header.
In our case we have two headers:
ApiKey: this value is always the same
Signature: this value depends on the request body content
Is there any way to inject the signature value just before the request is executed based on the request body content?
Here you can see two samples of the requests
Sample 1:
* url 'https://dev.sample.com'
* path '/api/user/getAll'
* header Content-Type = 'application/json'
* header ApiKey = 'XXX'
* header Signature = 'YYY'
And request { }
When method POST
Then status 200
Sample 2:
* url 'https://dev.sample.com'
* path '/api/user/getAll'
* header Content-Type = 'application/json'
* header ApiKey = 'XXX'
* header Signature = 'ZZZ'
And request { name: 'John' }
When method POST
Then status 200
Thanks
Karate has a "hook" for generating headers, but as of now it is not "aware" of the currently built request body + headers: https://github.com/intuit/karate#configure-headers
We got a similar request here, and are thinking of adding this capability: How to retrieve raw request contents before making a REST call in Karate DSL?
Maybe the OAuth examples will give you the way forward for your case for now: https://stackoverflow.com/a/55055111/143475
Feel free to raise an enhancement request, and we can get this in to the next version (with your help to test it). I'm thinking - what if you are able to call karate.get('request') from within the header JS function.
But for now all you need to do is do something like this:
* def body = { some: 'json' }
* karate.set('requestBody', body)
* url someUrl
* request body
* method post
And in the header.js function
function fn() {
var body = karate.get('requestBody');
var sign = Utils.sign(body);
return { Signature: sign };
}
EDIT: this will be implemented in Karate 1.0 onwards: https://github.com/intuit/karate/issues/1385
I'm using sails with the Blueprint API for the RESTful handling an am having an issue with a simple GET request. Take the following model for example. We'll call it Project:
module.exports = {
attributes: {
id: {
type: 'string',
unique: true
},
name: {
type: 'string'
},
displayName: {
type: 'string'
},
...
};
Since I have a ProjectController.js defined, Blueprint sets up a route for me at GET /project. If I make a request to this URL, I get all of the unfiltered results for this model, which is correct. There are also other params such as limit, sort, etc that can be used such as GET /project?limit=5 and I still get all of my results (assuming I don't have more than 5).
The problem lies when I provide a query param that isn't reserved for the API such as limit. Now, it uses this param as a filter criteria (GET /project?foo) and I get no results back.
My models are setup with sails.config.models.schema = true so that only fields defined in attributes will be populated as I do not want junk information stored. Seeing as how I've specified for the model to follow the schema, I would ideally like it to also ignore filter criteria that is not defined as an attribute. Essentially, GET /project?foo should return all results since foo isn't a valid attribute.
The main reason for this is there is a cache buster param sent with each request and Sails is interpreting it as a filter param thus skewing my results.
Thoughts?
In response to M U's follow-up comment, the idea here would be something that's generic and not specific to a model or controller. I looked into the idea of using policies, but then I would need to define that policy to be used on each controller which still wouldn't allow for a more specific filtering of params on a case-by-case basis.
The end goal here was to not have to define any methods in my controller since Blueprints implicitly handles them (eg. GET project/ would use Blueprints find() method automatically. Unfortunately I wasn't able completely get away with not having to define my own ProjectController.find() method, but it seems pretty reasonable since I can still call Blueprints middleware in a single line.
Here's an example utility module for stripping out params from the different scopes of the request (query, body and params). The comments hopefully speak for themselves:
requestUtils.js
var _ = require('lodash');
module.exports = {
sanitizeRequestParams: function (req, scope, opts) {
var scopes = ['query', 'body', 'params'],
// global params to strip, keep and convert. Additional configurations can be supplied
// in the `opts` argument
stripParams = ['_dc'],
keepParams = [],
convertParams = {
/**
* Convert the `page` param into a calculated `skip` param for ORM
*
* The client-side framework will include the following for model
* requests:
* ?_dc=XXX&limit=XX&page=XX&start=XX
*
* The only one ORM will not use for filter criteria is `limit`. So
* instead of using `page`, we will calculate the number of records
* to skip from the `page` and `limit` params and set that to the
* request instead.
*
* #param param The request param to be converted
* #param val The value of th =e request param
* #param req The Request object
* #returns {{skip: *}}
*/
'page': function (param, val, req) {
var limit = _.toInteger(req.query.limit);
if (!_.isInteger(limit)) {
limit = sails.config.blueprints.defaultLimit;
}
return {'skip': _.max([(_.toInteger(val) * limit) - limit, 0])};
}
};
// default to all if invalid scope specified
if (scopes.indexOf(scope) < 0) {
scope = 'all';
}
opts = opts || {};
// merge in user-provided configs with the defaults
stripParams = _.concat(stripParams, opts.strip || []);
keepParams = _.concat(keepParams, opts.keep || []);
convertParams = _.merge(convertParams, opts.convert || []);
// iterate over each of the scopes to process the params
_.forEach(scopes, function (currScope) {
if (scope === currScope || scope === 'all') {
// strip the defined params from the request, optionally
// keeping the ones marked as such
_.forEach(stripParams, function (param) {
if (keepParams.indexOf(param) < 0) {
// eg: deletes `request.query._dc`
delete req[currScope][param];
}
});
// iterate the params to be converted to a new param
_.forEach(convertParams, function (fn, param) {
var newParam;
// ensure the orig param exists before writing a new one
if (req[currScope][param]) {
newParam = fn(param, req[currScope][param], req);
// eg: deletes `request.query.page`
// adds `request.query.skip` with a calculated value
delete req[currScope][param];
_.merge(req[currScope], newParam);
}
});
}
});
}
};
ProjectController.js
module.exports = {
find: function (req, res) {
// before: ?_dc=XXXXX&page=XX&limit=XX&start=XX
// after: ?limit=XX&skip=XX
util.sanitizeRequestParams(req, 'query', {strip: ['start']});
// let Blueprints handle the `find` method like we never existed
return sails.hooks.blueprints.middleware.find(req, res);
}
};
There may be a far cleaner way to handle this such as lifecycle callbacks or hooks. I've not used Sails nor Node until now so it's all still green to me. Any feedback is appreciated.
i am trying to update a specific document by using rest service control.
I have set up the control (documentJsonService, pathInfo, form name etc)
I know that i have to perform a post (patch) request to the url of the service followed by /unid/ (same way as i ve done to read the document using the same rest).
I have an input field and a button. I want to enter a value to the field, press the button and update a field in the document with the value. How can i do this request?
This is the js function i used in worklight to update a document in Domino:
function updateDoc(docId,newValue) {
var identity = Base64.encode("myDominoUsername:myDominoPassword");
path = "/Databases/Temp/dominoApp.nsf/BestRestTest.xsp/updateService/unid/"+docId;
var input = {
method : 'post',
returnedContentType : 'json',
headers:{
Authorization: "Basic "+"b4lzdD234GG3M6SdW1XI=" //base64 encoding of your //credentials, see above. The function Base64.encode can be found by googling about it in //js. So you replace this string with identity variable.
"X-HTTP-Method-Override":"PATCH",
"Content-Type":"application/json"
},
body: {
contentType: 'application/json; charset=utf-8',
content: JSON.stringify({"theField":newValue})
},
path : path
};
return WL.Server.invokeHttp(input);
}
Now on the Domino side i have added an extension library REST control (alternatively you can do it with Domino Data Service). The properties i ve added are:
pathInfo: whatever you want
service: documentJsonService
computeWithForm:true
defaultItems:true
formName:myformName
This is just client side javascript, so you can do it in a similar way from an XPage.
I'm using backbone.js to interact with a REST API that, when posting to it to create a new resource, responds with a status of 201, a 'Location' header pointing to the resource's URI, but an empty body.
When I create a new model at the moment, its successful, but the local representation of the model only contains the properties I explicitly set, not any of the properties that would be set on the server (created_date, etc.)
From what I understand, Backbone would update its representation of the model with data in the body, if there were any. But, since there isn't, it doesn't.
So, clearly, I need to use the location in the Location header to update the model, but what's the best way to do this.
My current mindset is that I would have to parse the url from the header, split out the id, set the id for the model, then tell the model to fetch().
This seems really messy. Is there a cleaner way to do it?
I have some influence over the API. Is the best solution to try to get the API author to return the new model as the body of the response (keeping the 201 and the location header as well)?
Thanks!
Sounds like you will have to do a little customization.
Perhaps override the parse method and url method of your model class inherited from
Backbone.Model.
The inherited functions are:
url : function() {
var base = getUrl(this.collection);
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
},
parse : function(resp) {
return resp;
},
and you could try something like:
parse: function(resp, xhr) {
this._url = xhr.getResponseHeader('location')
return resp
}
url: function() {
return this._url
}
Yes, backbone.js really wants the result of a save (be it PUT or POST) to be a parseable body which can be used to update the model. If, as you say, you have influence over the API, you should see if you can arrange for the content body to contain the resource attributes.
As you point out, its makes little sense to make a second over-the-wire call to fully materialize the model.
It may be that a status code of 200 is more appropriate. Purists may believe that a 201 status code implies only a location is returned and not the entity. Clearly, that doesn't make sense in this case.
With Backbone 0.9.9, I couldn't get the accepted answer to work. The signature of the parse function seems to have changed in an older version, and the xhr object is no longer available in the function signature.
This is an example of what I did, to make it work with Backbone v0.9.9 and jQuery 1.8.3 (using a Deferred Object/Promise), relying on the jqXHR object returned by Backbone.Model.save() :
window.CompanyView = Backbone.View.extend({
// ... omitted other functions...
// Invoked on a form submit
createCompany: function(event) {
event.preventDefault();
// Store a reference to the model for use in the promise
var model = this.model;
// Backbone.Model.save returns a jqXHR object
var xhr = model.save();
xhr.done(function(resp, status, xhr) {
if (!model.get("id") && status == "success" && xhr.status == 201) {
var location = xhr.getResponseHeader("location");
if (location) {
// The REST API sends back a Location header of format http://foo/rest/companys/id
// Split and obtain the last fragment
var fragments = location.split("/");
var id = fragments[fragments.length - 1];
// Set the id attribute of the Backbone model. This also updates the id property
model.set("id", id);
app.navigate('companys/' + model.id, {trigger: true});
}
}
});
}
});
I did not use the success callback that could be specified in the options hash provided to the Backbone.Model.save function, since that callback is invoked before the XHR response is received. That is, it is pointless to store a reference to the jqXHR object and use it in the success callback, since the jqXHR would not contain any response headers (yet) when the callback is invoked.
Another other to solve this would be to write a custom Backbone.sync implementation, but I didn't prefer this approach.
I am building a REST API for my project. The API for getting a given user's INFO is:
api.com/users/[USER-ID]
I would like to also allow the client to pass in a list of user IDs. How can I construct the API so that it is RESTful and takes in a list of user ID's?
If you are passing all your parameters on the URL, then probably comma separated values would be the best choice. Then you would have an URL template like the following:
api.com/users?id=id1,id2,id3,id4,id5
api.com/users?id=id1,id2,id3,id4,id5
api.com/users?ids[]=id1&ids[]=id2&ids[]=id3&ids[]=id4&ids[]=id5
IMO, above calls does not looks RESTful, however these are quick and efficient workaround (y). But length of the URL is limited by webserver, eg tomcat.
RESTful attempt:
POST http://example.com/api/batchtask
[
{
method : "GET",
headers : [..],
url : "/users/id1"
},
{
method : "GET",
headers : [..],
url : "/users/id2"
}
]
Server will reply URI of newly created batchtask resource.
201 Created
Location: "http://example.com/api/batchtask/1254"
Now client can fetch batch response or task progress by polling
GET http://example.com/api/batchtask/1254
This is how others attempted to solve this issue:
Google Drive
Facebook
Microsoft
Subbu Allamaraju
I find another way of doing the same thing by using #PathParam. Here is the code sample.
#GET
#Path("data/xml/{Ids}")
#Produces("application/xml")
public Object getData(#PathParam("zrssIds") String Ids)
{
System.out.println("zrssIds = " + Ids);
//Here you need to use String tokenizer to make the array from the string.
}
Call the service by using following url.
http://localhost:8080/MyServices/resources/cm/data/xml/12,13,56,76
where
http://localhost:8080/[War File Name]/[Servlet Mapping]/[Class Path]/data/xml/12,13,56,76
As much as I prefer this approach:-
api.com/users?id=id1,id2,id3,id4,id5
The correct way is
api.com/users?ids[]=id1&ids[]=id2&ids[]=id3&ids[]=id4&ids[]=id5
or
api.com/users?ids=id1&ids=id2&ids=id3&ids=id4&ids=id5
This is how rack does it. This is how php does it. This is how node does it as well...
There seems to be a few ways to achieve this. I'd like to offer how I solve it:
GET /users/<id>[,id,...]
It does have limitation on the amount of ids that can be specified because of URI-length limits - which I find a good thing as to avoid abuse of the endpoint.
I prefer to use path parameters for IDs and keep querystring params dedicated to filters. It maintains RESTful-ness by ensuring the document responding at the URI can still be considered a resource and could still be cached (although there are some hoops to jump to cache it effectively).
I'm interested in comments in my hunt for the ideal solution to this form :)
You can build a Rest API or a restful project using ASP.NET MVC and return data as a JSON.
An example controller function would be:
public JsonpResult GetUsers(string userIds)
{
var values = JsonConvert.DeserializeObject<List<int>>(userIds);
var users = _userRepository.GetAllUsersByIds(userIds);
var collection = users.Select(user => new { id = user.Id, fullname = user.FirstName +" "+ user.LastName });
var result = new { users = collection };
return this.Jsonp(result);
}
public IQueryable<User> GetAllUsersByIds(List<int> ids)
{
return _db.Users.Where(c=> ids.Contains(c.Id));
}
Then you just call the GetUsers function via a regular AJAX function supplying the array of Ids(in this case I am using jQuery stringify to send the array as string and dematerialize it back in the controller but you can just send the array of ints and receive it as an array of int's in the controller). I've build an entire Restful API using ASP.NET MVC that returns the data as cross domain json and that can be used from any app. That of course if you can use ASP.NET MVC.
function GetUsers()
{
var link = '<%= ResolveUrl("~")%>users?callback=?';
var userIds = [];
$('#multiselect :selected').each(function (i, selected) {
userIds[i] = $(selected).val();
});
$.ajax({
url: link,
traditional: true,
data: { 'userIds': JSON.stringify(userIds) },
dataType: "jsonp",
jsonpCallback: "refreshUsers"
});
}