We use the TFS Java API to fetch WorkItems from a TFS server:
TFSTeamProjectCollection collection = TFSTeamProjectCollectionUtils
.openTeamProjectCollection(serverUrl, credentials,
new DefaultConnectionAdvisor(Locale.getDefault(),
TimeZone.getDefault()));
WorkItemClient client = collection.getWorkItemClient();
List<WorkItem> result = new ArrayList<>();
try {
WorkItemCollection workItems = client.query(wiqlQuery, null, false);
for (int i = 0; i < workItems.size(); i++) {
WorkItem item = workItems.getWorkItem(i);
result.add(item);
}
return result;
} catch (TECoreException e) {
throw new ConQATException("Failed to fetch work items from TFS", e);
}
If I run the query select * from workitems I get all workitems on the server with all fields and all links. Since I'm only interested in some of the fields, I would like to restrict the query to only those and save some bandwidth/time: select ID, Title from workitems
This works fine, but now the links of the items are missing (i.e. item.getLinks() always returns an empty collection).
Is there a way to select the links other than select * from workitems?
After some more digging around, I found that you can create a link query and run it like this:
WorkItemLinkInfo[] infos = client.createQuery("select * from workitemlinks").runLinkQuery()
With this, you can get the links as WorkItemLinkInfo objects that contain the IDs of the target and source node and the link type.
The solution using WorkItemLinkInfo is correct.
Just as remark: Using a WIQL Query you only receive the attributes you were querying - which cannot be the set of links of a work item (therefore always empty). If you query a single workitem using
WorkItemClient client = TFSConnection.getClient();
WorkItem firstWorkItem = client.getWorkItemByID(id);
then you also get the LinkCollection using (containing RelatedLinks, ExternalLinks or HyperLinks)
LinkCollection linkcoll = firstWorkItem.getLinks()
Related
We have an application that loads all contacts stored in an account using the Microsoft Graph API. The initial call we issue is https://graph.microsoft.com/v1.0/users/{userPrincipalName}/contacts$count=true&$orderBy=displayName%20ASC&$top=100, but we use the Java JDK to do that. Then we iterate over all pages and store all loaded contacts in a Set (local cache).
We do this every 5 minutes using an account with over 3000 contacts and sometimes, the count of contacts we received due to using $count does not match the number of contacts we loaded and stored in the local cache.
Verifying the numbers manually we can say, that the count was always correct, but there are contacts missing.
We use the following code to achieve this.
public List<Contact> loadContacts() {
Set<Contact> contacts = new TreeSet<>((contact1, contact2) -> StringUtils.compare(contact1.id, contact2.id));
List<QueryOption> requestOptions = List.of(
new QueryOption("$count", true),
new QueryOption("$orderBy", "displayName ASC"),
new QueryOption("$top", 100)
);
ContactCollectionRequestBuilder pageRequestBuilder = null;
ContactCollectionRequest pageRequest;
boolean hasNextPage = true;
while (hasNextPage) {
// initialize page request
if (pageRequestBuilder == null) {
pageRequestBuilder = graphClient.users(userId).contacts();
pageRequest = pageRequestBuilder.buildRequest(requestOptions);
} else {
pageRequest = pageRequestBuilder.buildRequest();
}
// load
ContactCollectionPage contactsPage = pageRequest.get();
if (contactsPage == null) {
throw new IllegalStateException("request returned a null page");
} else {
contacts.addAll(contactsPage.getCurrentPage());
}
// handle next page
hasNextPage = contactsPage.getNextPage() != null;
if (hasNextPage) {
pageRequestBuilder = contactsPage.getNextPage();
} else if (contactsPage.getCount() != null && !Objects.equals(contactsPage.getCount(), (long) contacts.size())) {
throw new IllegalStateException(String.format("loaded %d contacts but response indicated %d contacts", contacts.size(), contactsPage.getCount()));
} else {
// done
}
}
log.info("{} contacts loaded using graph API", contacts.size());
return new ArrayList<>(contacts);
}
Initially, we did not put the loaded contacts in a Set by ID but just in a List. With the List we very often got more contacts than $count. My idea was, that there is some caching going on and some pages get fetched multiple times. Using the Set we can make sure, that we only have unique contacts in our local cache.
But using the Set, we sometimes have less contacts than $count, meaning some pages got skipped and we end up in the condition that throws the IllegalStateException.
Currently, we use microsoft-graph 5.8.0 and azure-identiy 1.4.2.
Have you experienced similar issues and can help us solve this problem?
Or do you have any idea what could be causing these inconsistent results?
Your help is very much appreciated!
I am using below code and it returns some information about "Filed Against"
attribute. But there I am not able to find attribute data. Please help
IAttribute someAttribute= workItemClient.findAttribute(projectAreaHandle, workItem.CATEGORY_PROPERTY, monitor);
Using below code to find out the work item by Id :
workItemClient = (IWorkItemClient) repo.getClientLibrary(IWorkItemClient.class);
int id = new Integer("339406").intValue();
IWorkItem workItem = workItemClient.findWorkItemById(id, IWorkItem.FULL_PROFILE, monitor);
using this work item I want to fetch parent and children like Epic and story work items related to the work item. And then there attributes like story status, story planned for etc.
From this thread:
You can't just put a string in there, I think.
You have to find the category object from the string and then put in the ICategory object.
That means:
private static String CATEGORY_NAME = "UI1";
List<ICategory> findCategories = workItemCommon.findCategories(projectArea, ICategory.FULL_PROFILE, monitor);
for(ICategory category : findCategories) {
if(category.getName().contains(CATEGORY_NAME)){
filedAgainstAttribute = QueryableAttributes.getFactory(IWorkItem.ITEM_TYPE).findAttribute(projectArea, IWorkItem.CATEGORY_PROPERTY, auditableClient, monitor);
filedAgainstExpression = new AttributeExpression(filedAgainstAttribute, AttributeOperation.EQUALS, category);
}
}
We are trying to automate the project migration from one Rally workspace to other. Everything seems to work fine like we are able to migrate project and related releases/iterations/userstories/tasks from one workspace to another workspace.
But while trying to migrate BE Initiative/BE Feature/CPM Feature we are getting some exception related to Null Pointer exception but the error we are getting in Response doesn't seem to give much info.
A sample of code is -
String oldProjectObjectId = "12345";
String newProjectObjectId = "67890";
String oldRallyWorkspaceObjectId = "32145";
String newRallyWorkspaceObjectId = "67894";
QueryResponse beInitiativeResponse = queryRally("portfolioitem/beinitiative", "/project/"+this.oldProjectObjectId, "/workspace/"+this.oldRallyWorkspaceObjectId);
int beInitiativeCount = beInitiativeResponse.getTotalResultCount();
if(beInitiativeCount >0){
JsonArray initiativeArray = beInitiativeResponse.getResults();
for(int i=0; i< initiativeArray.size();i++){
JsonObject beInitiativeObject = initiativeArray.get(i).getAsJsonObject();
String oldBeInitiativeObjectId = beInitiativeObject.get("ObjectID").getAsString();
String oldBeInitiativeName = beInitiativeObject.get("_refObjectName").getAsString();
String owner = getObjectId(beInitiativeObject, "Owner");
JsonObject BeInitiativeCreateObject = getJsonObject(oldBeInitiativeName, "/project/"+this.newProjectObjectId, "/workspace/"+this.newRallyWorkspaceObjectId, owner);
CreateResponse beInitiativeCreateResponse = createInRally("portfolioitem/beinitiative", BeInitiativeCreateObject);
if(beInitiativeCreateResponse.wasSuccessful()){
String newBeInitiativeObjectId = beInitiativeCreateResponse.getObject().get("ObjectID").getAsString();
String mapKey = oldBeInitiativeObjectId;
String mapValue= newBeInitiativeObjectId;
this.beInitiativesHashMap.put(mapKey, mapValue);
}
else{
String[] errorList;
errorList = beInitiativeCreateResponse.getErrors();
for (int j = 0; j < errorList.length; j++) {
System.out.println(errorList[j]);
}
}
}
}
queryRally and createInRally functions use Rally rest client to fetch and create the required projects and associated attributes like releases, iterations etc.
After executing CreateResponse beInitiativeCreateResponse = createInRally("portfolioitem/beinitiative", BeInitiativeCreateObject); when it's trying to execute if(beInitiativeCreateResponse.wasSuccessful()) it is instead going to else block and thus printing the below mentioned error.
An unexpected error has occurred.We have recorded this error and will begin to investigate it. In the meantime, if you would like to speak with our Support Team, please reference the information below:java.lang.NullPointerException2017-12-05 11:01 AM PST America/Los_Angeles
But the important point that is when trying to migrate projects and it's related attributes like release/iterations etc. withing same Rally workspace the above piece of code works just fine.
Update1:
While analysing the issue I made the following observations -
The workspace in which I am trying to create the BeInitiative doesn't have BEinitiative, Be Feature, CPM Feature options in Portfolio items dropdown. Rather it has Theme, Initiative and Feature options in it.
Therefore, I think I was getting the previouly mentioned error. Now I made the following changes to the code.
CreateResponse beInitiativeCreateResponse = createInRally("portfolioitem/theme", themeCreateObject);
So now instead of creating the BEInitiative I am trying to create the theme only in new workspace but getting the following error -
Requested type name \"/portfolioitem/theme\" is unknown.
The object that i am passing to CreateResponse function is -
{"Name":"xyz","Project":"/project/1804","Workspace":"/workspace/139"}
Also code for createInRally function is as mentioned below -
public CreateResponse createInRally( String query, JsonObject object) throws IOException{
CreateRequest createRequest = new CreateRequest(query, object);
CreateResponse createResponse = restApi.create(createRequest);
return createResponse;
}
The Unknown Type error was occurring as a result of not passing the workspace's object id in which we were trying to create the portfolio item.
So after modifying the createInRally function to include the workspace object id we were able to create the initiative portfolio item.
The modified createInRally function is as shown below-
CreateRequest createRequest = new CreateRequest(query, object);
createRequest.addParam("workspace", "/workspace/1333333333");
CreateResponse createResponse = restApi.create(createRequest);
return createResponse;
So this is definitely an error in the web services api. You should never get 500 responses with an unhandled nullpointer. My initial guess is that when you're creating your new object some field on it is still referencing an object in the old workspace, and when we try to correctly hook up all the associations it fails to read one of those objects in the new workspace. Can you provide some more information about what your actual object you're sending to create looks like? Specifically what object relationships are you including (that may not be valid in the new workspace)?
I tried to implement pagination in google app engine (Java), but I am not able to achieve. It is working only forward pagination and reverse pagination is not able to achieved.
I tried storing the previous cursor value through HTTP request as below:
JSP file:
<a href='/myServlet?previousCursor=${previousCursor}'>Previous page</a>
<a href='/myServlet?nextCursor=${nextCursor}'>Next page</a>
Servlet file:
String previousCursor = req.getParameter("previousCursor");
String nextCursor = req.getParameter("nextCursor");
String startCursor = null;
if(previousCursor != null){
startCursor = previousCursor;
}
if(nextCursor != null){
startCursor = nextCursor;
}
int pageSize = 3;
FetchOptions fetchOptions = FetchOptions.Builder.withLimit(pageSize);
if (startCursor != null) {
fetchOptions.startCursor(Cursor.fromWebSafeString(startCursor));
}
Query q = new Query("MyQuery");
PreparedQuery pq = datastore.prepare(q);
QueryResultList<Entity> results = pq.asQueryResultList(fetchOptions);
for (Entity entity : results) {
//Get the properties from the entity
}
String endCursor = results.getCursor().toWebSafeString();
req.setAttribute("previousCursor", startCursor);
req.setAttribute("nextCursor", endCursor);
With this I am able to retain the previous cursor value, but unfortunately the previous cursor seems to be invalid.
I also tried using reverse() method, but it is of no use. It work same as forward.
So is the any way to implement proper pagination (forward and backword) in google app engine (Java)?
I found similar one that was posted in 2010. Here also the answer was to use Cursor. But as I shown above it is not working.
Pagination in Google App Engine with Java
If you are familiar with JPA you can give it a try.
Have tested it and pagination works in GAE.
I think they support JPA 1.0 as of now.
What I tried was, created an Employee entity.
Created DAO layer and persisted few employee entities.
To have a paginated fetch, did this:
Query query = em.createQuery("select e from Employee e");
query.setFirstResult(0);
query.setMaxResults(2);
List<Employee> resultList = query.getResultList();
(In this example we get first page which has 2 entities. Argument to
setFirstResult would be start index and argument to setMaxResult would be your page size)
You can easily change the arguments to query.setFirstResult and setMaxResults
and have a pagination logic around it.
Hope this helps,
Regards,
I'm to get a list of the users photos (one's they've been tagged in) using FQL.
Basically I've go an array object like so: _imageAddressArray.
I can retrieve the users' photos using graphApi so I know it works, problem with graphAPi is that it's too slow (+15 seconds min for 100 photos).
So far I've got:
//New Stuff
FQL fql = new FQL(facebook);
String FQLResult = null;
try
{
_userGallery = graphApi.getPhotosMy(_noOfPhotos);
FQLResult = fql.fqlQuery("SELECT object_id, src_small FROM photo");
}
catch (EasyFacebookError e)
{
e.printStackTrace();
}
System.out.println("FQL Result" + FQLResult);
This returns the error: 601, any ideas anyone?
Of course ideally FQLResult will be a string[] (string array)
You're getting an error because you don't have a WHERE clause in your FQL statement that references one of the indexed columns -- shown with a "*" here
To get the photos using FQL that your user has been tagged in, try this:
SELECT object_id, src_small FROM photo WHERE object_id IN
(SELECT object_id FROM photo_tag WHERE subject = me())