Aha!<>VSTS Integration - Aha! adjusted Stack Rank in VSTS - tips on reverting? - azure-devops

While integrating with VSTS, I accidentally wiped out the stack rank in VSTS for my 100+ features. My first choice would be to undo the import. Does anyone know how I could revert the action on either the Aha or VSTS side? My second choice would be to edit one by one. I can go into the details of each story and see the original stack rank, but I can't seem to be able to find where I could then reenter it one by one.

If there isn't any other action performed on these work items after that, the quick way is getting the Stack Rank value in the previous revision of the work items and updating the work items with the returned stack rand via coding. Following is the code sample to update the value of Stack Rank to previous version for a single work item:
using Microsoft.TeamFoundation.Client;
using System;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
namespace ConsoleX
{
class Program
{
static void Main(string[] args)
{
Uri url = new Uri("https://vstsaccount.visualstudio.com");
TfsTeamProjectCollection ttpc = new TfsTeamProjectCollection(url);
WorkItemStore wis = ttpc.GetService<WorkItemStore>();
int workitemid = 12;
WorkItem wi = wis.GetWorkItem(workitemid);
int previousrevision = wi.Revision - 2;
string previousstackrank = wi.Revisions[previousrevision].Fields["Stack Rank"].Value.ToString();
wi.Fields["Stack Rank"].Value = previousstackrank;
wi.Save();
}
}
}
For you scenario, just add some code to query that 100+ features and update them one by one.

Related

Microsoft Bot Framework - Multi turn context is lost after the first interaction

I recently moved my code from SDK v3 to v4 and I am trying to take advantage of the multi-turn features.
I have looked over the samples from GitHub. The samples work well for multi-turn but one issue I noticed is that it recognizes the context only if the prompt is clicked immediately after the initial answer (with prompts) is shown.
I would like to be able to identify, at any given time that a prompt is clicked. I am storing all the previous prompts in the state object (dialogInstance.State) already. I have a custom host, which sends the replytoid and using that I can get the appropriate state.
The problem is though, I am not able to get to a point where I can use the dialoginstance.State.
The sample code uses the DialogExtensions class. The "DialogExtensions" class tries to gather the previous context by checking if the result from the ContinueDialogAsync method returns null or not.
DialogExtensions class with multi-turn
When there is no previous context (no previous answer with prompts), then the call to the ContinueDialogAsync returns a result with Empty Status.
I am thinking this where I need to check the dialogstate and if the new message refers to any of the old messages at any given point, it can then start to continue the old conversation.
I am not sure if that is even possible.
Any help/pointers would be appreciated.
thanks,
I eventually ended up implementing something that will work for custom bot host/direct channel bot client.
The whole point is that the call to the qnamaker api should happen with the old context object, whenever an option is chosen, even if it is out-of-context.
First let me explain how it works in the current version of the code.
The way the bot code was trying to solve multi turn dialog, was by storing the current answer if it had prompts/options and returning the conversation state in "waiting" mode. When the next question is received, it would automatically assume that the new question is part of the prompts/options of the old question. It would then pass the oldstate along to the QnAMaker.
What I noticed is that, even if the question in the second turn is not part of the prompts/options (something the user has typed manually and is a completely different question), it would still send the oldstate object to the QnAMaker.
The QnAMaker api call seem to ignore oldstate if the new question is not part of the prompts/options of the oldstate. It will work correctly by fetching the answer for the new question that was typed manually.
This was the key. If we can focus on what gets to the qnamaker then we can solve our original problem.
I realized that having the bot return a waiting state is only a mechanism to create a condition to extract the oldstate in the next turn. However, if I can rebuild the oldstate anytime when there is an option chosen, then the call to the qnamaker would work equally well.
This is what I have done now.
In my custom bot host code (which is a direct line client), I am sending the ReplyToID field populated with the original question whenever a prompt is clicked. Then in the bot code, I have changed it so that if there is a replytoid present, then build a new oldstate object with the data from the reply to id. Below is the QnABotState class that represents the oldstate. its a very simple class containing previous qna question id and the question text.
public int PreviousQnaId { get; set; }
public string PreviousUserQuery { get; set; }
QnABoState class
Now, the problem was the Activity object contains ReplyToId but does not contain ReplyToQuery (or something like that). Activity object is used to send data from bot client to the bot. So, either I would have to use a different field or send the PreviousUserQuery as an empty string. I had a hunch that it would work with just the previousqnaid.
//starting the process to get the old context (create an object that will hold the Process function's current state from the dialog state)
//if there is replyToId field is present, then it is a direct channel request who is replying to an old context
//get the reply to id from summary field
var curReplyToId = "";
curReplyToId = dialogContext.Context.Activity.ReplyToId;
var curReplyToQuery = "";
var oldState = GetPersistedState(dialogContext.ActiveDialog);
//if oldstate is null also check if there is replytoid populated, if it is then it maybe a new conversation but it is actually an "out of turn option" selection.
if (oldState == null)
{
if (!string.IsNullOrEmpty(curReplyToId))
{
//curReplyToId is not empty. this is an option that was selected out-of-context
int prevQnaId = -1;
int.TryParse(curReplyToId, out prevQnaId);
oldState = new QnABotState() { PreviousQnaId = prevQnaId, PreviousUserQuery = curReplyToQuery };
}
}
With that in place, my call to the qnamaker api would receive an oldstate object even if it is called out-of-context.
I tried the code and it worked. Not having the previous qna query did not make a difference. It worked with just the PreviousQnaId field being populated.
However, please note, this will not work for other channels. It would work for channels where you can set the ReplyToId field, such as the Direct Channel Client.
here is the code from my bot host:
// to create a new message
Activity userMessage = new Activity
{
From = new ChannelAccount(User.Identity.Name),
Text = questionToBot,
Type = ActivityTypes.Message,
Value = paramChatCode,// + "|" + "ShahID-" + DateTime.Now.TimeOfDay,
Id = "ShahID-" + DateTime.Now.TimeOfDay,
ChannelData = botHostId//this will be added as the bot host identifier
};
//userMessage.Type = "imBack";
if (paramPreviousChatId > 0)
{
//we have a valid replytoid (as a part of dialog)
userMessage.ReplyToId = paramPreviousChatId.ToString();
}

Eclipse P2 IUQuery alway has empty result

I try to use eclipse P2 to enable a tool of mine for auto-updating itself on eclipse start up. While doing so, I want to use an UpdateOperation which is only suited to "my" feature with id "my.feature.id". Whenever this query gets issued in an eclipse installation it has an empty result and thus nothing to update.
So: How do I use the QueryUtil right to get a collection containing only my feature for update as input for an UpdateOperation?
The following method is called when wanting to start the update on eclipse start up:
public class P2Util {
public static IStatus checkForUpdates(IProvisioningAgent agent, IProgressMonitor monitor) {
ProvisioningSession session = new ProvisioningSession(agent);
IQuery<IInstallableUnit> query = QueryUtil.createLatestQuery(QueryUtil.createIUQuery("my.feature.id"));
UpdateCheckActivator.info("Update Query Expression: " + query.getExpression());
IProfileRegistry registry= (IProfileRegistry)agent.getService(IProfileRegistry.SERVICE_NAME);
IProfile profile= registry.getProfile(IProfileRegistry.SELF);
IQueryResult<IInstallableUnit> result = profile.query(query, monitor);
Set<IInstallableUnit> unitsForUpdate = result.toUnmodifiableSet();
UpdateOperation operation = new UpdateOperation(session, unitsForUpdate);
}
}
The solution is very simple but confusing at first. The query trying to find features with "my.feature.id" was querying for the wrong id.
In the feature.xml of my feature it is "my.feature.id" but the installable unit gets for unobvious reasons the suffix "feature.group". If one adds this to the id in the query, you get the correct result and the update operation succeeds as expected.

CQ5 / AEM5.6 Workflow: Access workflow instance properties from inside OR Split

TL;DR version:
In CQ workflows, is there a difference between what's available to the OR Split compared to the Process Step?
Is it possible to access the /history/ nodes of a workflow instance from within an OR Split?
How?!
The whole story:
I'm working on a workflow in CQ5 / AEM5.6.
In this workflow I have a custom dialog, which stores a couple of properties on the workflow instance.
The path to the property I'm having trouble with is: /workflow/instances/[this instance]/history/[workItem id]/workItem/metaData and I've called the property "reject-or-approve".
The dialog sets the property fine (via a dropdown that lets you set it to "reject" or "approve"), and I can access other properties on this node via a process step (in ecma script) using:
var actionReason;
var history = workflowSession.getHistory(workItem.getWorkflow());
// loop backwards through workItems
// and as soon as we find a Action Reason that is not empty
// store that as 'actionReason' and break.
for (var index = history.size() - 1; index >= 0; index--) {
var previous = history.get(index);
var tempActionReason = previous.getWorkItem().getMetaDataMap().get('action-message');
if ((tempActionReason != '')&&(tempActionReason != null)) {
actionReason = tempActionReason;
break;
}
}
The process step is not the problem though. Where I'm having trouble is when I try to do the same thing from inside an OR Split.
When I try the same workflowSession.getHistory(workItem.getWorkflow()) in an OR Split, it throws an error saying workItem is not defined.
I've tried storing this property on the payload instead (i.e. storing it under the page's jcr:content), and in that case the property does seem to be available to the OR Split, but my problems with that are:
This reject-or-approve property is only relevant to the current workflow instance, so storing it on the page's jcr:content doesn't really make sense. jcr:content properties will persist after the workflow is closed, and will be accessible to future workflow instances. I could work around this (i.e. don't let workflows do anything based on the property unless I'm sure this instance has written to the property already), but this doesn't feel right and is probably error-prone.
For some reason, when running through the custom dialog in my workflow, only the Admin user group seems to be able to write to the jcr:content property. When I use the dialog as any other user group (which I need to do for this workflow design), the dialog looks as though it's working, but never actually writes to the jcr:content property.
So for a couple of different reasons I'd rather keep this property local to the workflow instance instead of storing it on the page's jcr:content -- however, if anyone can think of a reason why my dialog isn't setting the property on the jcr:content when I use any group other than admin, that would give me a workaround even if it's not exactly the solution I'm looking for.
Thanks in advance if anyone can help! I know this is kind of obscure, but I've been stuck on it for ages.
a couple of days ago i ran into the same issue. The issue here is that you don't have the workItem object, because you don't really have an existing workItem. Imagine the following: you are going through the workflow, you got a couple of workItems, with means, either process step, either inbox item. When you are in an or split, you don't have existing workItems, you can ensure by visiting the /workItems node of the workflow instance. Your workaround seems to be the only way to go through this "issue".
I've solved it. It's not all that elegant looking, but it seems to be a pretty solid solution.
Here's some background:
Dialogs seem to reliably let you store properties either on:
the payload's jcr:content node (which wasn't practical for me, because the payload is locked during the workflow, and doesn't let non-admins write to its jcr:content)
the workItem/metaData for the current workflow step
However, Split steps don't have access to workItem. I found a fairly un-helpful confirmation of that here: http://blogs.adobe.com/dmcmahon/2013/03/26/cq5-failure-running-script-etcworkflowscriptscaworkitem-ecma-referenceerror-workitem-is-not-defined/
So basically the issue was, the Dialog step could store the property, but the OR Split couldn't access it.
My workaround was to add a Process step straight after the Dialog in my workflow. Process steps do have access to workItem, so they can read the property set by the Dialog. I never particularly wanted to store this data on the payload's jcr:content, so I looked for another location. It turns out the workflow metaData (at the top level of the workflow instance node, rather than workItem/metaData, which is inside the /history sub-node) is accessible to both the Process step and the OR Split. So, my Process step now reads the workItem's approveReject property (set by the Dialog), and then writes it to the workflow's metaData node. Then, the OR Split reads the property from its new location, and does its magic.
The way you access the workflow metaData from the Process step and the OR Split is not consistent, but you can get there from both.
Here's some code: (complete with comments. You're welcome)
In the dialog where you choose to approve or reject, the name of the field is set to rejectApprove. There's no ./ or anything before it. This tells it to store the property on the workItem/metaData node for the current workflow step under /history/.
Straight after the dialog, a Process step runs this:
var rejectApprove;
var history = workflowSession.getHistory(workItem.getWorkflow());
// loop backwards through workItems
// and as soon as we find a rejectApprove that is not empty
// store that as 'rejectApprove' and break.
for (var index = history.size() - 1; index >= 0; index--) {
var previous = history.get(index);
var tempRejectApprove = previous.getWorkItem().getMetaDataMap().get('rejectApprove');
if ((tempRejectApprove != '')&&(tempRejectApprove != null)) {
rejectApprove = tempRejectApprove;
break;
}
}
// steps up from the workflow step into the workflow metaData,
// and stores the rejectApprove property there
// (where it can be accessed by an OR Split)
workItem.getWorkflowData().getMetaData().put('rejectApprove', rejectApprove);
Then after the Process step, the OR Split has the following in its tabs:
function check() {
var match = 'approve';
if (workflowData.getMetaData().get('rejectApprove') == match) {
return true;
} else {
return false;
}
}
Note: use this for the tab for the "approve" path, then copy it and replace var match = 'approve' with var match = 'reject'
So the key here is that from a Process step:
workItem.getWorkflowData().getMetaData().put('rejectApprove', rejectApprove);
writes to the same property that:
workflowData.getMetaData().get('rejectApprove') reads from when you execute it in an OR Split.
To suit our business requirements, there's more to the workflow I've implemented than just this, but the method above seems to be a pretty reliable way to get values that are entered in a dialog, and access them from within an OR Split.
It seems pretty silly that the OR Split can't access the workItem directly, and I'd be interested to know if there's a less roundabout way of doing this, but for now this has solved my problem.
I really hope someone else has this same problem, and finds this useful, because it took me waaay to long to figure out, to only apply it once!

How do I easily query a running Eclipse for its installed Features?

I'm building an Eclipse Feature that has a requirement that amounts to this... The Feature must be able to be uninstalled automatically if the user has his license revoked.
If you want more background information about that, Here is another question that I've asked on this topic.
Thanks to the answers on that question, I've been trying to learn the Eclipse p2 director api. I've found some classes that look useful here
I've been trying to instantiate one of these classes in my code, but with no luck yet.
I've been reading the help documentation, and I'm getting kind of lost in it all.
I'm stuck because I have a need to supply the OperationFactory with a collection of IInstallableUnit objects.
private void scheduleUninstallOperationJob(
Collection<? extends IVersionedId> toUninstall)
{
OperationFactory oFactory = new OperationFactory();
Collection<URI> repos = null;
UninstallOperation op = null;
try {
op = oFactory.createUninstallOperation(toUninstall, repos, null);
} catch (ProvisionException e) {
e.printStackTrace();
}
IStatus result = op.resolveModal(null);
if (result.isOK()) {
op.getProvisioningJob(null).schedule();
}
}
I don't see any way to easily ask the running instance of Eclipse to give me the collection of currently installed InstallableUnits so that I can easily pass the one I want to uninstall to the OperationFactory.createUninstallOperation() method.
I've tried using Eclipse source code as an example, but the code that I have found is the org.eclipse.equinox.p2.ui.ProvisioningUI, and it's tightly coupled to the UI that is used when manually uninstalling InstallableUnits. This code also uses code that is in the dreaded Eclipse internal packages which I would like to avoid using if possible.
Thank you for your consideration, Trace
This code gets the collection of IUs which are managed by the profile of the currently running system:
/**
* This Activator informs user about the IUs which are currently installed in
* the running environment.
*
* This code is intended for demo only and should be much more defensive for
* production use.
*
* #author Ilya Shinkarenko
*
*/
public class SelfInformerActivator extends Plugin {
#Override
public void start(final BundleContext ctx) throws Exception {
super.start(ctx);
ServiceReference<IProvisioningAgentProvider> sr = ctx.getServiceReference(IProvisioningAgentProvider.class);
IProvisioningAgentProvider agentProvider = ctx.getService(sr);
URI p2InstanceURI = null; // myself
final IProvisioningAgent agent = agentProvider.createAgent(p2InstanceURI);
IProfileRegistry regProfile = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME);
IProfile profileSelf = regProfile.getProfile(IProfileRegistry.SELF);
IQuery<IInstallableUnit> query = QueryUtil.createIUAnyQuery();
//This is what you need:
IQueryResult<IInstallableUnit> allIUs = profileSelf.query(query, new NullProgressMonitor());
//Let's output it:
Iterator<IInstallableUnit> iterator = allIUs.iterator();
while (iterator.hasNext()) {
IInstallableUnit iu = iterator.next();
System.out.println(iu);
}
}
}
There was very little traffic on this question. I attribute that to the odd nature of the requirement that is driving this development.
Well, it seems that those requirements have changed, and I will no longer be needing to programmatically uninstall Eclipse Features based on a revoked license. Therefore I will stop researching how to do this for now.
If you're looking to solve this issue to, feel free to contact me by leaving a comment here, or answering this question with another question. :)

TFS 2010 API - Get work items from merge

I need to send an email on completion of a build in TFS 2010 which details the work items associated with check-ins that have been compiled as part of this build. This works no problem via use of the associatedChangesets variable available in the build workflow.
However, in a production situation, we will merge changes from our Development branch into a Release branch. At this point, the build considers there to have only been one change - which is the aforementioned merging of Development into Release. Obviously this is fairly useless as we need to find out which changes where made in the branch that was merged in, and the work items associated.
Does anyone know how to accomplish this using the TFS 2010 API? It seems to be fairly poorly documented from an API perspective. I know you can expand the merge history node in VS2010 but obviously this is no good as this data needs to be collected programatically so a report email can be sent.
OK... I think I found a solution to this although it's clunky and truth be told I'm not exactly sure how it works. But here goes - maybe it will point someone in the right direction.
var associatedWorkItems = new List<WorkItem>();
//Passed in from the build workflow (this variable is available under the 'Run On Agent' sequence as 'associatedChangesets'
IList<Changeset> associatedChangesets = context.GetValue(BuildAssociatedChangesets);
if (associatedChangesets.Count > 0)
{
var projectCollection =
new TfsTeamProjectCollection(new Uri("http://localhost:8080/tfs/DefaultCollection"));
VersionControlServer versionControlServer = projectCollection.GetService<VersionControlServer>();
foreach (var changeset in associatedChangesets)
{
//In order to view the individual changes, load the changeset directly from the VCS.
Changeset localChangeset = versionControlServer.GetChangeset(changeset.ChangesetId);
foreach (Change change in localChangeset.Changes)
{
//Find out what was merged in.
ChangesetMerge[] mergedChangesets = versionControlServer.QueryMerges(
null,
null,
change.Item.ServerItem,
new ChangesetVersionSpec(localChangeset.ChangesetId),
new ChangesetVersionSpec(localChangeset.ChangesetId),
null,
RecursionType.Full);
//Extract work item information from changesets being identified as merged.
foreach (var changesetMerge in mergedChangesets)
{
Changeset actualChange = versionControlServer.GetChangeset(changesetMerge.SourceVersion);
foreach (WorkItem item in actualChange.WorkItems)
{
if (!associatedWorkItems.Exists(w => w.Id == item.Id))
{
associatedWorkItems.Add(item);
}
}
}
}
}
}
Don't ask me exactly how QueryMerges works but all I'm doing here it saying show me what what merged as a part of a changeset checked in. You'll notice that the parameters ChangesetVersionSpec are the same - this means we're just looking at merges from this one changeset.
You'll get back an array of ChangesetMerge objects from QueryMerges(). In the ChangesetMerge class there is a property called SourceVersion - this is the ChangesetId of the original changeset merged in. Once we've got that we can use the VersionControlServer.GetChangeset() method to load the individual set and extract the WorkItem. This is then added to a list of WorkItems which can be manipulated in any way you want (in my case an email). I also used the .Exists() check to make sure the same WorkItem doesn't get recorded twice.
Note that even though you have the collection associatedChangesets from the build workflow, for some reason (for me at least), the Changes[] property inside associatedChangesets was never populated (hence loading each individual changeset using the VersionControlServer.GetChangeset() method as this seems to actually populate all the fields we need.
Like I say, 1. this is a clunky solution (lots of looping - some of which is probably unecessary), 2. I don't fully understand how this works although it seems to produce the required results - I came to this conclusion by doing a lot testing and debugging) and finally - it's the best I could come up with based on the woeful documentation provided by Microsoft.
Hope it helps someone!