Programmatically creating an Assign in a Flowchart Workflow - workflow

I need to programmatically define a serializable flowchart Windows Workflow that accepts input arguments and returns a result. I understand how to create these workflows using a designer, but I need to do it in code and have the flowchart workflow be serializable (so no lambda expressions).
I'm having trouble getting the "To" field of the Assign. The code below creates a flowchart workflow of a WriteLine followed by an Assign.
var ab = new ActivityBuilder<string> {
Name = "ActivityBuilt",
Implementation = new Flowchart {
StartNode = new FlowStep {
Action = new WriteLine { Text = new VisualBasicValue<string>("greeting") },
Next = new FlowStep {
Action = new Assign {
DisplayName = "Set result",
To = new OutArgument<string>(new VisualBasicReference<string> {
ExpressionText = "results"}),
Value = new VisualBasicValue<string>("bye")
}
}
}
}
};
ab.Properties.Add(new DynamicActivityProperty {
Name = "greeting",
Type = typeof (InArgument<string>),
Value = "hello"});
ab.Properties.Add(new DynamicActivityProperty {
Name = "results",
Type = typeof (OutArgument<string>),
Value = "bye"});
// Convert the ActivityBuilder to a callable activity
using (var sw = new StringWriter()) {
using (var xw = ActivityXamlServices.CreateBuilderWriter(new XamlXmlWriter(sw, new XamlSchemaContext()))) {
XamlServices.Save(xw, LastCreated);
}
using (var sr = new StringReader(sw.ToString())) {
var wf = ActivityXamlServices.Load(sr);
return wf;
}
}
The above code fails when I try to convert from ActivityBuilder to Activity saying "Failed to create a 'OutArgument' from the text 'bye'." This works fine if I don't use the OutArgument and just pass things in.
My question is what to put in the To property? How do I reference the OutArgument I add to the ActivityBuilder.Properties? A VisualBasicReference isn't an OutArgument. Am I making this more difficult than it needs to be?
Thanks for any hints!
Edit: I want to create a workflow programmatically. The workflow needs to have input arguments and return results (output arguments).
I understand how to create the workflow and how to declare and use input arguments. I'm using an ActivityBuilder to create the workflow and to set the InArgument via the ActivityBuilder's properties. I create the workflow from the ActivityBuilder by serializing to XAML and then deserializing using ActivityXamlServices.Load.
What I don't understand is how to get a result from the workflow. I assume it involves an OutArgument. How/where do I add an OutArgument to the workflow? I thought the code snippet I gave would assign a value to an OutArgument, but the call to ActivityXamlServices.Load fails with a complaint that it is unable to create the OutArgument.
Is the approach of using ActivityBuilder correct?
Is the "To" field of the Assign action properly referencing an OutArgument?
How do I make the ActivityBuilder aware of the OutArgument and still be able to convert to an Activity / workflow?
Hope this clarifies my problem.

There are atleast 3 problems with the code:
The Value of the Assign needs to be an InArgument().
The value you are trying to read from is named "greeting" not "bye".
The OutArgument named "results" can't have a default value.
Try the following code:
var ab = new ActivityBuilder<string>
{
Name = "ActivityBuilt",
Implementation = new Flowchart
{
StartNode = new FlowStep
{
Action = new WriteLine { Text = new VisualBasicValue<string>("greeting") },
Next = new FlowStep
{
Action = new Assign
{
DisplayName = "Set result",
To = new OutArgument<string>(new VisualBasicReference<string>
{
ExpressionText = "results"
}),
Value = new InArgument<string>(new VisualBasicValue<string>("greeting"))
}
}
}
}
};
ab.Properties.Add(new DynamicActivityProperty
{
Name = "greeting",
Type = typeof(InArgument<string>),
Value = "hello"
});
ab.Properties.Add(new DynamicActivityProperty
{
Name = "results",
Type = typeof(OutArgument<string>)
});
// Convert the ActivityBuilder to a callable activity
using (var sw = new StringWriter())
{
using (var xw = ActivityXamlServices.CreateBuilderWriter(new XamlXmlWriter(sw, new XamlSchemaContext())))
{
XamlServices.Save(xw, ab);
}
using (var sr = new StringReader(sw.ToString()))
{
var wf = ActivityXamlServices.Load(sr);
WorkflowInvoker.Invoke(wf);
}
}

Related

Setting URL in UploadCollectionItem in UI5 1.38.4

What parameters are mandatory for an UploadCollectionItem with the URL parameter set will show the file when the filename is clicked.
I am using a factory to handle files coming from different locations.
attachmentFactory(sId, context) {
const modelObj = context.getModel().getProperty(context.getPath());
const uploadListItem = new SAPUploadCollectionItem();
// If __metadata exists, attachment entry is from odata, if not then it's a FileEntry object.
if (modelObj.__metadata) {
uploadListItem.setFileName(modelObj.FILE_NAME);
uploadListItem.setMimeType(modelObj.MIME_CODE);
uploadListItem.setUrl("https://upload.wikimedia.org/wikipedia/commons/4/49/Koala_climbing_tree.jpg");
}
else {
uploadListItem.setFileName(modelObj.name);
uploadListItem.setMimeType(modelObj.type);
uploadListItem.setUrl("https://upload.wikimedia.org/wikipedia/commons/4/49/Koala_climbing_tree.jpg");
}
return uploadListItem;
}
I get an exception in UI5 when I press the link in the function
UploadCollection.prototype._triggerLink = function(oEvent, oContext) {
var iLine = null;
var aId;
if (oContext.editModeItem) {
//In case there is a list item in edit mode, the edit mode has to be finished first.
sap.m.UploadCollection.prototype._handleOk(oEvent, oContext, oContext.editModeItem, true);
if (oContext.sErrorState === "Error") {
//If there is an error, the link of the list item must not be triggered.
return this;
}
oContext.sFocusId = oEvent.getParameter("id");
}
aId = oEvent.oSource.getId().split("-");
iLine = aId[aId.length - 2];
sap.m.URLHelper.redirect(oContext.aItems[iLine].getProperty("url"), true);
};
oContext.aItems is an array but the source.getId() value is "__item9-ta_filenameHL" so __item9 is not found in oContext.aItems
I'm not sure if this is a bug or I'm setting up my UploadCollectionItem incorrectly
I had to set the sId of the UploadCollectionItem to be the sId that was passed into the factory.

Custom properties are not updated for the word via openXML

I am trying to update custom properties of word document thru Open XML programming but it seems the updated properties are not getting saved properly for the word document. So when I opening document after successful execution of the update custom property code, I am getting the message box which is "This document contains field that may refer to other files; Do you want to update the fields in the Document?" If I am pressing 'NO' button then all the update properties would not be saved to the document. If we are going for yes option then it will update properties but I need to save the properties explicitly. Please suggest to save properties to the document without getting confirmation message or corrupting the document. :)
the code snippet is given as below,
public void SetCustomValue(
WordprocessingDocument document, string propname, string aValue)
{
CustomFilePropertiesPart oDocCustomProps = document.CustomFilePropertiesPart;
Properties props = oDocCustomProps.Properties;
if (props != null)
{
//logger.Debug("props is not null");
foreach (var prop in props.Elements<CustomDocumentProperty>())
{
if (prop != null && prop.Name == propname)
{
//logger.Debug("Setting Property: " + prop.Name + " to value: " + aValue);
prop.Remove();
var newProp = new CustomDocumentProperty();
newProp.FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}";
newProp.Name = prop.Name;
VTLPWSTR vTLPWSTR1 = new VTLPWSTR();
vTLPWSTR1.Text = aValue;
newProp.Append(vTLPWSTR1);
props.AppendChild(newProp);
props.Save();
}
}
int pid = 2;
foreach (CustomDocumentProperty item in props)
{
item.PropertyId = pid++;
}
props.Save();
}
}
I am using .Net framework 3.5 with Open XML SDK 2.0 and Office 2013.
Try this one
var CustomeProperties = xmlDOc.CustomFilePropertiesPart.Properties;
foreach (CustomDocumentProperty customeProperty in CustomeProperties)
{
if (customeProperty.Name == "DocumentName")
{
customeProperty.VTLPWSTR = new VTLPWSTR("My Custom Name");
}
else if (customeProperty.Name == "DocumentID")
{
customeProperty.VTLPWSTR = new VTLPWSTR("FNP.SMS.IQC");
}
else if (customeProperty.Name == "DocumentLastUpdate")
{
customeProperty.VTLPWSTR = new VTLPWSTR(DateTime.Now.ToShortDateString());
}
}
//Open Word Setting File
DocumentSettingsPart settingsPart = xmlDOc.MainDocumentPart.GetPartsOfType<DocumentSettingsPart>().First();
//Update Fields
UpdateFieldsOnOpen updateFields = new UpdateFieldsOnOpen();
updateFields.Val = new OnOffValue(true);
settingsPart.Settings.PrependChild<UpdateFieldsOnOpen>(updateFields);
settingsPart.Settings.Save();
you have to update your document fields on open.

Cannot create/update a Rally TestCaseResult with a reference to a specified TestSet

With Rally, I need to
Update a TestCaseResult with a new TestSet Ref.
OR
Create a new TestCaseResult by copying everything from a previous TestCaseResult and just changing the TestSet Ref.
I am trying to do the same through the Java REST toolkit from Rally. It uses the JSON REST API internally, it seems.
When I try to do this with CreateRequest or UpdateRequest, I get an error from the API "Could not set value for Test Set: null"
Is it not possible to update the TestSet of a TestCaseResult (whether existing or newly created)?
Here's some sample code I am using (showing create testcaseresult from existing by changing testset).
//get testcaseresult object
GetRequest tcrReq = new GetRequest("/testcaseresult/12345.js");
tcrReq.setFetch(new Fetch("FormattedID", "Name"));
GetResponse tcrResponse = restApi.get(tcrReq);
//update testcaseresult object with new testset
JsonObject tsRef = new JsonObject();
tsRef.addProperty("_ref", "https://rally1.rallydev.com/slm/webservice/1.39/testset/1029348.js");
tcrResponse.getObject().add("TestSet",tsRef);
tcrResponse.getObject().remove("_ref");
//Create API for new testcaseresult object
CreateRequest createRequest = new CreateRequest("testcaseresult", tcrResponse.getObject());
CreateResponse createResponse = restApi.create(createRequest);
if(createResponse.wasSuccessful()){
System.out.println(createResponse.getObject());
}else{
String[] ss = createResponse.getErrors();
for(int i=0; i<ss.length; i++){
System.out.println(ss[i]);
}
}
Can you please help to understand whether I am doing something wrong or is this a Rally limitation?
I believe the reason that you are getting the "Could not set value for Test Set: null" error message is that there is an "invisible" constraint on TestCaseResults whereby the TestCase to which they are associated must be scheduled into the TestSet of interest, before the TestCaseResult can be assigned that TestSet as an attribute.
Unfortunately, there's no TestSet attribute on TestCases, so you have to query the TestCases collection off of the TestSet, and then loop through that collection to check and see if the parent TestCase is a member of that collection. Once verified that the TestCase is in that TestSet's collection of TestCases, then you can proceed to successfully update a member TestCaseResult with that TestSet attribute of interest. I tested the below and it works as expected.
Here's a code snippet illustrating how to accomplish this:
// Create and configure a new instance of RallyRestApi
// Connection parameters
String rallyURL = "https://rally1.rallydev.com";
String wsapiVersion = "1.38";
String applicationName = "RestExample_UpdateTestSetOnTestCaseResult";
// Credentials
String userName = "user#company.com";
String userPassword = "password";
RallyRestApi restApi = new RallyRestApi(
new URI(rallyURL),
userName,
userPassword);
restApi.setWsapiVersion(wsapiVersion);
restApi.setApplicationName(applicationName);
// Ref to Test Case Result of Interest
String testCaseResultRef = "/testcaseresult/1234567891.js";
GetRequest testCaseResultRequest = new GetRequest(testCaseResultRef);
GetResponse testCaseResultResponse = restApi.get(testCaseResultRequest);
JsonObject testCaseResultObj = testCaseResultResponse.getObject();
// Get the Test Case Result's Parent Test Case
JsonObject testCase = testCaseResultObj.get("TestCase").getAsJsonObject();
String testCaseRef = testCase.get("_ref").getAsString();
GetRequest testCaseRequest = new GetRequest(testCaseRef);
GetResponse testCaseResponse = restApi.get(testCaseRequest);
JsonObject testCaseObj = testCaseResponse.getObject();
System.out.println(testCaseRef);
// Ref to Test Set of Interest
String testSetRef = "/TestSet/12345678910.js";
// Get the Test Set of interest
GetRequest testSetRequest = new GetRequest(testSetRef);
GetResponse testSetResponse = restApi.get(testSetRequest);
JsonObject testSetObject = testSetResponse.getObject();
// Grab the Test Cases in this Test Set
JsonArray testCasesInTestSet = testSetObject.get("TestCases").getAsJsonArray();
// Loop through and see if our Test Case of interest is a member
boolean testCaseIsInSet = false;
for (int i=0; i<testCasesInTestSet.size(); i++) {
JsonObject thisTestCase = testCasesInTestSet.get(i).getAsJsonObject();
String thisTestCaseRef = thisTestCase.get("_ref").getAsString();
if (thisTestCaseRef.equals(testCaseRef)) {
testCaseIsInSet = true;
}
}
if (testCaseIsInSet) {
// Update Test Set on Existing Test Case Result
try {
//Add Test Set
System.out.println("\nUpdating Existing Test Case Result's Test Set attribute...");
testCaseResultObj.addProperty("TestSet", testSetRef);
UpdateRequest updateExistTestCaseResultRequest = new UpdateRequest(testCaseResultRef, testCaseResultObj);
UpdateResponse updateExistTestCaseResultResponse = restApi.update(updateExistTestCaseResultRequest);
if (updateExistTestCaseResultResponse.wasSuccessful()) {
System.out.println("Updated Test Case Result with new Test Set");
String[] updateExistTestCaseResultWarnings;
updateExistTestCaseResultWarnings = updateExistTestCaseResultResponse.getWarnings();
System.out.println("Warning(s) occurred updating Test Case Result: ");
for (int i=0; i<updateExistTestCaseResultWarnings.length;i++) {
System.out.println(updateExistTestCaseResultWarnings[i]);
}
} else {
String[] updateExistTestCaseResultErrors;
updateExistTestCaseResultErrors = updateExistTestCaseResultResponse.getErrors();
System.out.println("Error occurred updating Test Case Result: ");
for (int i=0; i<updateExistTestCaseResultErrors.length;i++) {
System.out.println(updateExistTestCaseResultErrors[i]);
}
}
} catch (Exception e) {
System.out.println("Exception occurred while updating Tags on existing Test Case: ");
e.printStackTrace();
}
finally {
//Release all resources
restApi.close();
}
} else {
System.out.println("Unable to Update Test Case Result with specified Test Set");
System.out.println("Parent Test Case is not a member of this Test Set");
}
}
When updating the TestSet you can just set the value as its ref- you don't need the wrapper object.
tcrResponse.getObject().add("TestSet", "/testset/1029348.js");

Using DynamicActivityProperty as OutArgument in ActivityBuilder

Greetings,
I'm trying to create a workflow using a ActivityBuilder, and then get the XAML.
This flow use a custom activity (WaitForInput) to handle bookmarks. This class inherits from NativeActivity.
I'm having a hard time finding a way to set 'Result' property of my WaitForInput activity, which expects a OutArgument.
Creating this same workflow by the VS designer, I could associate the boolean property 'MyResult' InOutArgument called 'wrapper'. Like this : [Wrapper.MyResult]
I would do this by code, and according to my research, I have to use DynamicActivityProperty.
The problem is that I don't know how to use my DynamicActivityProperty as OutArgument in this case.
This is an simplified version of the code:
var wrapper = new DynamicActivityProperty
{
Name = "Wrapper",
Type = typeof(InOutArgument<CommunicationWrapper>),
};
var activityBuilder = new ActivityBuilder();
activityBuilder.Properties.Add(wrapper);
var step1 = new FlowStep
{
//here's my problem
Action = new WaitForInput<bool> { BookmarkName = "step1", Result = ??? }
};
var flow = new Flowchart
{
StartNode = step1,
Nodes = { step1 }
};
I have founded a solution to my own problem
Result = new OutArgument<bool>(new VisualBasicReference<bool>
{ ExpressionText = "Wrapper.MyResult" }); }

A better way of representing File Attachment into a list(c#3.0)

I have written
List<Attachment> lstAttachment = new List<Attachment>();
//Check if any error file is present in which case it needs to be send
if (new FileInfo(Path.Combine(errorFolder, errorFileName)).Exists)
{
Attachment unprocessedFile = new Attachment(Path.Combine(errorFolder, errorFileName));
lstAttachment.Add(unprocessedFile);
}
//Check if any processed file is present in which case it needs to be send
if (new FileInfo(Path.Combine(outputFolder, outputFileName)).Exists)
{
Attachment processedFile = new Attachment(Path.Combine(outputFolder, outputFileName));
lstAttachment.Add(processedFile);
}
Working fine and is giving the expected output.
Basically I am attaching the file to the list based on whether the file is present or not.
I am looking for any other elegant solution than the one I have written.
Reason: Want to learn differnt ways of representing the same program.
I am using C#3.0
Thanks.
Is it looks better?
...
var lstAttachment = new List<Attachment>();
string errorPath = Path.Combine(errorFolder, errorFileName);
string outputPath = Path.Combine(outputFolder, outputFileName);
AddAttachmentToCollection(lstAttachment, errorPath);
AddAttachmentToCollection(lstAttachment, outputPath);
...
public static void AddAttachmentToCollection(ICollection<Attachment> collection, string filePath)
{
if (File.Exists(filePath))
{
var attachment = new Attachment(filePath);
collection.Add(attachment);
}
}
How about a little LINQ?
var filenames = new List<string>()
{
Path.Combine(errorFolder, errorFilename),
Path.Combine(outputFolder, outputFilename)
};
var attachments = filenames.Where(f => File.Exists(f))
.Select(f => new Attachment(f));