I recently started using the "removeUnauthorizedSnapshots" parameter with the LBAPI to avoid permissions errors I was previously experiencing. Using the LBAPI to gather details for all the work items in our workspace is MUCH faster than the WSAPI, however since we have ~25,000 leaf stories in our workspace, this data must be gathered using more than one API request. When adding the "limit : Infinity" parameter to the request, you can see in the network traffic that while the second request was in fact made, the "removeUnauthorizedSnapshots" parameter was not included, therefore resulting in a permissions error.
Is there any plan to add official support for this parameter to the LBAPI, rather than adding it to the request manually?
Thanks!
In the meantime, here is a solution which uses the "loadPage" function, in place of "load":
var allRecords = [];
function getWorkItems(pageNumber) {
Ext.create('Rally.data.lookback.SnapshotStore', {
fetch : ['Name','ObjectID','PlanEstimate'],
filters : [{
property : '__At',
value : 'current'
},{
property : '_TypeHierarchy',
value : 'HierarchicalRequirement'
},{
property : 'Children',
value : null
}]
}).loadPage(pageNumber, {
params : {
compress : true,
removeUnauthorizedSnapshots : true
},
callback : function(records, operation, success) {
allRecords = Ext.Array.merge(allRecords, records);
if (operation.response.StartIndex + operation.response.PageSize >= operation.response.TotalResultCount) {
//All records loaded
} else {
getWorkItems(++pageNumber);
}
}
});
}(1);
I submitted a bug. Thank you for bringing it to our attention. As I commented in the other post we added a story to a backlog to add "removeUnauthorized" to a Rally.data.lookback.SnapshotStore config, but the workaround suggested there in a meantime is apparently flawed and the extra parameters are not applied to subsequent requests, as your scenario with number of total results exceeding 20K shows.
Related
So I have this lookback API request:
https://rally1.rallydev.com/analytics/v2.0/service/rally/workspace/xxxxxxx/artifact/snapshot/query.js?find={"ObjectID":92444754348,"__At":"2017-02-23T00:00:00Z"}&fields=true&start=0&pagesize=10&removeUnauthorizedSnapshots=true
How can I make that request using the Ext equivalent. I have tried many ways, including this one:
let snapshot = Ext.create('Rally.data.lookback.SnapshotStore', {
find: {
ObjectID: 92444754348,
__At: "2017-02-23T00:00:00Z"
}
});
return snapshot.load();
This example returns an object that has the field "raw", which to my understanding is supposed to have all the artifact's fields along with the values they had at the specified time. But, "raw" only has ObjectID, Project, _ValidFrom, and _ValidTo.
Right now I'm able to solve my issue by using an ajax GET request and parsing the JSON; but I would like to use the Ext solution instead (which seems to be the recommended one).
Thanks.
If you include a fetch in your config when you're creating the store it will autocreate the correct model for you.
let snapshot = Ext.create('Rally.data.lookback.SnapshotStore', {
find: {
ObjectID: 92444754348,
__At: "2017-02-23T00:00:00Z"
},
fetch: ['ObjectID'] //add all the fields you want here
});
fields=true is a nice shorthand to get all the data back, but the store/model have no idea how to interpret that...
The store also has config properties for compress, removeUnauthorizedSnapshots and most of the other parameters Lookback Api supports.
Any ideas on how can I list all the activities in my domain by using the new google+ domain's API in java?
The Developers' Live video shows at 4:00 minute mark that you can do something like this:
Plus.Activities.List listActivities = plus.activities().list("me", "domain");
The Link for this code is here.
But when I actually run the same line of code it shows me the following error.
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"location" : "collection",
"locationType" : "parameter",
"message" : "Invalid string value: 'domain'. Allowed values: [user]",
"reason" : "invalidParameter"
} ],
"message" : "Invalid string value: 'domain'. Allowed values: [user]"
}
The error makes sense as in the activities.list documentation it says that "user" is the only acceptable value for collection and not "domain."
So what should I do about this issue?
As you say, the only available way is to list posts by the currently logged user. You have to use user delegation (with service accounts) and loop over all users in the domain in order to get all published activities.
You can use the updated field on the response to check if there is anything new in a user's list of activities.
This line of thought applies to the whole Domains API: every operation is done on behalf of a user, there is no "admin" account with superpowers. This can be a limitation when acting on a big number of users, as you are forced to authenticate for each one in turn (if someone has an idea on how to achieve this in a more efficient way, please share!)
As the documentation sais, only "public" is allowed:
https://developers.google.com/+/api/latest/activities/list
However even using the code provided in the example in the API doc, after going through successful authentication I get 0 activities.
/** List the public activities for the authenticated user. */
private static void listActivities() throws IOException {
System.out.println("Listing My Activities");
// Fetch the first page of activities
Plus.Activities.List listActivities = plus.activities().list("me", "public");
listActivities.setMaxResults(100L);
// Pro tip: Use partial responses to improve response time considerably
listActivities.setFields("nextPageToken,items(id,url,object/content)");
ActivityFeed activityFeed = listActivities.execute();
// Unwrap the request and extract the pieces we want
List<Activity> activities = activityFeed.getItems();
System.out.println("Number of activities: " + activities.size());
// Loop through until we arrive at an empty page
while (activities != null) {
for (Activity activity : activities) {
System.out.println("ID " + activity.getId() + " Content: " +
activity.getObject().getContent());
}
// We will know we are on the last page when the next page token is null.
// If this is the case, break.
if (activityFeed.getNextPageToken() == null) {
break;
}
// Prepare to request the next page of activities
listActivities.setPageToken(activityFeed.getNextPageToken());
// Execute and process the next page request
activityFeed = listActivities.execute();
activities = activityFeed.getItems();
}
}
Anybody know how to get this to work?
when you use Google+ API you must use "public" but when you use Google+ Domains API you must use "user" parameter value.
I am using app SDK 2.0.I have the following code to retrieve the tasks for first 100 Team Members of the project for a particular Iteration
//getting the first 100 elements of the owners Array
owners = Ext.Array.splice(owners,0,100);
//Initial configuration of the filter object
var ownerFilter = Ext.create('Rally.data.QueryFilter', {
property: 'Owner.DisplayName',
operator:'=',
value: owners[0]
});
/*Creating the filter object to get all the tasks for 100 members in that project*/
Ext.Array.each(owners,function(member){
ownerFilter = ownerFilter.or(
Ext.create('Rally.data.QueryFilter',{
property: 'Owner.DisplayName',
operator:'=',
value: member
}));
});
//Iteration Filter for the Object
var iterationFilter = Ext.create('Rally.data.QueryFilter', {
property: 'Iteration.Name',
operator:'=',
value: 'Iteration 4.2'
});
var filter = ownerFilter.and(iterationFilter);
Rally.data.ModelFactory.getModel({
type: 'Task',
success: function(model) {
var taskStore = Ext.create('Rally.data.WsapiDataStore', {
model: model,
fetch:true,
limit:Infinity,
pageSize:200,
filters:[filter],
autoLoad:true,
projectScopeDown:true,
listeners:{
load:function(store,data,success) {
Ext.Array.each(data, function(record){
console.log(record.data);
});
}
}
});
}
});
This code is giving me a 413 error since the URL for the query is too large.The request URL has names of all 100 members of the project.How can I solve this problem ? Are there any efficient filtering options available?
Team Membership is a tough attribute to filter Artifacts on, since it resides as an attribute on Users and is a collection that can't be used in Queries.
In this case you might be better off doing more of the filtering client-side. Since Team Membership "approximates" or associates to Projects, you may wish to start by filtering your Tasks on Project, i.e. (Project.Name = "My Project"). Then narrow things down client side by looping through and ensuring each Task is owned by a member of the Team in your owners collection.
Alternatively, you might look to do something like use Tags on your Tasks that mirror the Team Names. You could then filter your Tasks server side by doing a query such as:
(Tags.Name contains "My Team")
I know these aren't ideal options and probably not the answer you're looking for, but, they are some ideas to avoid having to build a massive username-based query.
JavaScript
For example, I have the following JavaScript code (Dojo 1.6 is already loaded):
dojo.require("dojo.io.script")
// PART I
var jsonpArgs = {
url: "http://myapp.appspot.com/query",
content: {
id: "1234",
name: "Juan",
start_date: "2000-01-01",
callback: "recover"
}
};
// PART II
dojo.io.script.get(jsonpArgs).then(function(data) {
console.log(data);
});
// PART III
function recover(data) {
console.log(data);
}
Direct query from browser
I understand that my server will receive the query as though I typed the following into the address bar:
http://myapp.appspot.com/query?id=1234&name=Juan&start_date=2000-01-01&callback=recover
Expected response
If I directly queried my server using the browser address bar, I'll receive, in MIME type application/json and plaintext rendered in browser, something like this:
recover(
{
id: 1234,
name: Juan,
data: [
["2000-01-01", 1234],
["2000-01-02", 5678]
]
}
);
Problem
Now, looking back at Part II of the JavaScript, I'd execute the JSONP request with dojo.io.script.get(jsonpArgs). This returns a Deferred object, which I can take advantage of by chaining .then after it. Note that I defined the handler for the .then event to output that captured data to the console.
However, all I get in the console is an Event. I tried to search its data tree, but I could not find the data I expected.
Question
Where is the response for a JSONP request stored? How do I find it?
My server (which I control) only outputs a plaintext rendering of the data requested, wrapped in the callback function (here specified as recover), and specifies a application/json MIME type. Is there anything else I need to set up on my server, so that the response data is captured by the Deferred object?
Attempted solution
I can actually recover the response by defining the callback function (in this case recover in Part III of the JavaScript). However, in the Dojo tutorials, they just recovered the data using the Deferred (and .then) framework. How do I do it using Dojo Deferreds?
Update (using the Twitter example from Dojo tutorial)
Take for example this script from the Dojo tutorial, Getting Jiggy With JSONP. I edited it to log data to the console.
dojo.require("dojo.io.script");
dojo.io.script.get({
url: "http://search.twitter.com/search.json",
callbackParamName: "callback",
content: {q: "#dojo"}
}).then(function(data){
//we're only interested in data.results, so strip it off and return it
console.log(data); // I get an Object, not an Event, but no Twitter data when browsing the results property
console.log(data.results) // I get an array of Objects
return data.results;
});
For console.log(data), I get an Object, not an Event as illustrated by my case. Since the example implies that the data resides in data.results, I also try to browse this tree, but I don't see my expected data from Twitter. I'm at a loss.
For console.log(data.results), I get an array of Objects. If I query Twitter directly, this is what I'd get in plaintext. Each Object contains the usual tweet meta-data like username, time, user portrait, and the tweet itself. Easy enough.
This one hits me right on the head. The handler for the .then chain, an anonymous function, receives only one argument data. But why is it that the results property in console.log(data) and the returned object I get from console.log(data.results) are different?
I got it.
Manual callback implementation
function recover(data) {
console.log(data);
}
var jsonpArgs = {
url: "http://myapp.appspot.com/query",
content: {
id: "1234",
name: "Juan",
start_date: "2000-01-01",
callback: "recover"
};
dojo.io.script.get(jsonpArgs);
This is the request that my server will receive:
http://myapp.appspot.com/query?id=1234&name=Juan&start_date=2000-01-01&callback=recover
In this case, I'll expect the following output from my server:
recover({
id: 1234,
name: Juan,
data: [
["2000-01-01", 1234],
["2000-01-02", 5678]
]
});
Three things to note:
Server will expect callback in the query URL string. callback is implemented as a property of jsonpArgs.
Because I specified callback=recover, my server will attach recover( + the_data_I_need + ), returns the whole string to the browser, and browser will execute recover(the_data_I_need). This means...
That I'll have to define, for example, function recover(one_argument_only) {doAnythingYouWantWith(one_argument_only)}
The problem with this approach is that I cannot take advantage of Deferred chaining using .then. For example:
dojo.io.script.get(jsonpArgs).then(function(response_from_server) {
console.log(response_from_server);
})
This will give me an Event, with no trace of the expected response at all.
Taking advantage of Dojo's implementation of JSONP requests
var jsonpArgs = {
url: "http://myapp.appspot.com/query",
callbackParamName: "callback",
content: {
id: "1234",
name: "Juan",
start_date: "2000-01-01"
};
dojo.io.script.get(jsonpArgs);
This is the request that my server will receive:
http://myapp.appspot.com/query?id=1234&name=Juan&start_date=2000-01-01&callback=some_function_name_generated_by_dojo
In this case, I'll expect the following output from my server:
some_function_name_generated_by_dojo({
id: 1234,
name: Juan,
data: [
["2000-01-01", 1234],
["2000-01-02", 5678]
]
});
Things to note:
Note the property of jsonpArgs, callbackParamName. The value of this property must be the name of the variable (in the query URL string) expected by the server. If my server expects callbackfoo, then callbackParamName: "callbackfoo". In my case, my server expects the name callback, therefore callbackParamName: "callback".
In the previous example, I specified in the query URL callback=recover and proceeded to implement function recover(...) {...}. This time, I do not need to worry about it. Dojo will insert its own preferred function callback=some_function_name_generated_by_dojo.
I imagine some_function_name_generated_by_dojo to be defined as:
Definition:
function some_function_name_generated_by_dojo(response_from_server) {
return response_from_server;
}
Of course the definition is not that simple, but the advantage of this approach is that I can take advantage of Dojo's Deferred framework. See the code below, which is identical to the previous example:
dojo.io.script.get(jsonpArgs).then(function(response_from_server) {
console.log(response_from_server);
})
This will give me the exact data I need:
{
id: 1234,
name: Juan,
data: [
["2000-01-01", 1234],
["2000-01-02", 5678]
]
}
(ExtJS 4.0.7)
I'm using Model.save() to PUT an update to a server. Everything works fine and the server returns a simple JSON response {success: true} (HTTP status 200). Model.save() throws the following error, however:
Uncaught TypeError: Cannot read property 'data' of undefined
Here's where this is happening in the ExtJS code (src/data/Model.js):
save: function(options) {
...
callback = function(operation) {
if (operation.wasSuccessful()) {
record = operation.getRecords()[0]; <-- getRecords() return an empty array
me.set(record.data); <-- record is undefined, so .data causes error
...
}
I've figured out this is happening because Model.save() expects the server to respond with JSON for the entire object that was just updated (or created).
Does anyone know of a clever way to make Model.save() work when the server responds with a simple success message?
I was able to come up with a work-around by using a custom proxy for the model, and overriding the update function:
Ext.define('kpc.util.CustomRestProxy', {
extend: 'Ext.data.proxy.Rest',
alias: 'proxy.kpc.util.CustomRestProxy',
type: 'rest',
reader : {
root: 'data',
type: 'json',
messageProperty: 'message'
},
// Model.save() will call this function, passing in its own callback
update: function(operation, callback, scope) {
// Wrap the callback from Model.save() with our own logic
var mycallback = function(oper) {
// Delete the resultSet from the operation before letting
// Model.save's callback use it; this will
oper.resultSet = undefined;
callback(op);
};
return this.doRequest(operation, mycallback, scope);
}
});
In a nutshell, when my proxy is asked to do an update it makes sure operation.resultSet == undefined. This changes the return value for operation.getRecords() (which you can see in the code sample from my question). Here's what that function looks like (src/data/Operation.js):
getRecords: function() {
var resultSet = this.getResultSet();
return (resultSet === undefined ? this.records : resultSet.records);
}
By ensuring that resultSet == undefined, operation.getRecords returns the model's current data instead of the empty result set (since the server isn't returning a result, only a simple success message). So when the callback defined in save() runs, the model sets its data to its current data.
I investigate this problem and found truly simple answer. Your result must be like this:
{
success: true,
items: { id: '', otherOpt: '' }
}
And items property MUST be equal Model->Reader->root property (children in tree for example).
If you want to use items instead children you can use defaultRootProperty property in Store and configure your nested collections as you want.
PS
Object in items property must be fully defined because it replaces actual record in store.