Example #1
0
        public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestMessage req, TraceWriter log)
        {
            log.Info($"++++++++++WorkItemUpdate was triggered!");
            HttpContent requestContent = req.Content;
            string      jsonContent    = requestContent.ReadAsStringAsync().Result;

            log.Info(jsonContent);

            JObject originalWI         = JObject.Parse(jsonContent);
            string  originalWorkItemId = Uri.UnescapeDataString((string)originalWI["resource"]["workItemId"]);

            log.Info(originalWorkItemId);

            string sourceVSTS = Uri.UnescapeDataString((string)originalWI["resourceContainers"]["collection"]["baseUrl"]);

            if (sourceVSTS.LastIndexOf('/') == sourceVSTS.Length - 1)
            {
                sourceVSTS = sourceVSTS.Substring(0, sourceVSTS.Length - 1);
            }
            string sourceTeamProject = Uri.UnescapeDataString((string)originalWI["resource"]["revision"]["fields"]["System.TeamProject"]);


            string oldValueRev = "";

            try
            {
                oldValueRev = Uri.UnescapeDataString((string)originalWI["resource"]["fields"]["System.Rev"]["oldValue"]);
                log.Info(oldValueRev);
            }
            catch (Exception)
            {
                log.Info("Revision didn't change");
                return(req.CreateResponse(HttpStatusCode.OK, "Revision didn't change"));
            }

            string destVSTS;
            string destPrj;
            string destPAT;

            Utility.SetVSTSAccountInfo("", sourceTeamProject, out destVSTS, out destPrj, out destPAT);

            string destWorkItemId = await Utility.GetDestinationWorkItemId(destVSTS, destPAT, destPrj, originalWorkItemId, log);

            if (destWorkItemId == "")
            {
                log.Info("WorkItem to be updated not found... Perhaps we should create it... :-)");
                return(req.CreateResponse(HttpStatusCode.OK, "WorkItem to be updated not found... Perhaps we should create it... :-)"));
            }
            else
            {
                try
                {
                    var jsonPatch = new JsonPatch();
                    if (originalWI["resource"]["fields"] == null)
                    {
                        log.Info("+++No Fields Changed+++");
                    }
                    else
                    {
                        foreach (JProperty property in ((JObject)originalWI["resource"]["fields"]).Properties())
                        {
                            try
                            {
                                log.Info(property.Name + " - " + Utility.JSONEncode((string)originalWI["resource"]["fields"][property.Name]["newValue"]));
                            }
                            catch (Exception)
                            {
                                log.Info("No newvalue -------should remove old one-----");
                                break;
                            }
                            if (property.Name == "System.Tags")
                            {
                                jsonPatch.Add("add", "/fields/System.Tags", null, Utility.JSONEncode((string)originalWI["resource"]["fields"][property.Name]["newValue"] + "; OriginalID=" + originalWorkItemId));
                            }
                            else if (property.Name.Contains("Kanban.Column") || property.Name.Contains("System.BoardColum"))
                            {
                                log.Info("Skip Kanban.Column(s)");
                            }
                            else if (property.Name == "Microsoft.VSTS.TCM.TestSuiteAudit")
                            {
                                string value = (string)originalWI["resource"]["fields"][property.Name]["newValue"];

                                string[] numbers = Regex.Split(value, @"\D+");
                                foreach (string origWI in numbers)
                                {
                                    int prova;
                                    if (Int32.TryParse(origWI, out prova))
                                    {
                                        string destWI = await Utility.GetDestinationWorkItemId(destVSTS, destPAT, destPrj, origWI, log);

                                        if (destWI != "")
                                        {
                                            value = value.Replace(origWI, destWI);
                                            if (value.StartsWith("Removed these test suites:"))
                                            {
                                                await Utility.DeleteTestSuite(destVSTS, destPAT, destPrj, destWI, log);
                                            }
                                            if (value.StartsWith("Removed these test cases:"))
                                            {
                                                await Utility.RemoveTestCaseFromTestSuite(destVSTS, destPAT, destPrj, destWorkItemId, destWI, log);
                                            }
                                        }
                                        if (value.StartsWith("Added these test cases:"))
                                        {
                                            string originalTestCase = await Utility.GetTestCaseById(sourceVSTS, destPAT, sourceTeamProject, originalWorkItemId, origWI, log);

                                            string destinationTestCase = await Utility.UpdateTestCase(destVSTS, destPAT, destPrj, originalTestCase, log);

                                            await Utility.AddTestCaseToTestSuite(destVSTS, destPAT, destPrj, destWorkItemId, destWI, destinationTestCase, log);
                                        }
                                    }
                                }
                                jsonPatch.Add("add", "/fields/" + property.Name, null, value);
                            }
                            else
                            {
                                jsonPatch.ParseAndAddProperty(property.Name, (string)originalWI["resource"]["fields"][property.Name]["newValue"]);
                            }
                        }
                    }
                    if (originalWI["resource"]["relations"] == null)
                    {
                        log.Info("+++No Relations+++");
                    }
                    else
                    {
                        if (originalWI["resource"]["relations"]["added"] == null)
                        {
                            log.Info("+++No Added Relations+++");
                        }
                        else
                        {
                            JArray relations = (JArray)originalWI["resource"]["relations"]["added"];
                            foreach (JObject relation in relations.Children())
                            {
                                foreach (JProperty property in relation.Properties())
                                {
                                    log.Info(property.Name + " - " + Utility.JSONEncode(property.Value.ToString()));
                                }

                                string rel    = relation.Properties().FirstOrDefault(x => x.Name == "rel").Value.ToString();
                                string relUrl = "";

                                if (rel == "AttachedFile")
                                {
                                    // attachments

                                    relUrl = relation.Properties().FirstOrDefault(x => x.Name == "url").Value.ToString();
                                    byte[] attachment = await Utility.GetSourceAttachment(relUrl + "?api-version=4.1", destPAT, log);

                                    log.Info("Attachment length: " + attachment.Length.ToString());

                                    string fileName = ((JObject)relation.Properties().FirstOrDefault(x => x.Name == "attributes").Value).Properties().FirstOrDefault(x => x.Name == "name").Value.ToString();
                                    log.Info(fileName);
                                    string jsonAttach = await Utility.CreateAttachment(destVSTS, destPAT, fileName, attachment, log);

                                    JObject jsonObj = JObject.Parse(jsonAttach);

                                    relUrl = Uri.UnescapeDataString((string)jsonObj["url"]);
                                    log.Info(relUrl);
                                }
                                else
                                {
                                    relUrl = await Utility.GetLinkedUrl(destVSTS, destPAT, destPrj, relation, log);
                                }

                                jsonPatch.AddCollection("add", "/relations/-", "",
                                                        "{" +
                                                        "  \"rel\": \"" + rel + "\"," +
                                                        "  \"url\": \"" + relUrl + "\"," +
                                                        "  \"attributes\": " + relation.Properties().FirstOrDefault(x => x.Name == "attributes").Value.ToString() +
                                                        "}");
                            }
                        }
                        if (originalWI["resource"]["relations"]["removed"] == null)
                        {
                            log.Info("+++No Removed Relations+++");
                        }
                        else
                        {
                            string destinationWorkItem = await Utility.GetDestinationWorkItem(destVSTS, destPAT, destPrj, destWorkItemId, log);

                            JObject destinationWI        = JObject.Parse(destinationWorkItem);
                            JArray  destinationRelations = (JArray)destinationWI["relations"];

                            if (destinationRelations == null)
                            {
                                log.Info("+++No Relations In Destination WorkItem+++");
                            }
                            else
                            {
                                JArray relations = (JArray)originalWI["resource"]["relations"]["removed"];
                                foreach (JObject relation in relations.Children())
                                {
                                    int  relationId = -1;
                                    bool found      = false;

                                    foreach (JObject destRelation in destinationRelations.Children())
                                    {
                                        relationId += 1;

                                        string relationRel = relation.Properties().FirstOrDefault(x => x.Name == "rel").Value.ToString();
                                        string relationURL = relation.Properties().FirstOrDefault(x => x.Name == "url").Value.ToString();

                                        if (relationRel == destRelation.Properties().FirstOrDefault(x => x.Name == "rel").Value.ToString())
                                        {
                                            if (relationRel.StartsWith("System.LinkTypes"))
                                            {
                                                if (await Utility.GetDestinationWorkItemId(destVSTS, destPAT, destPrj, Utility.GetWorkItemIdFromLinkUrl(relationURL), log) ==
                                                    Utility.GetWorkItemIdFromLinkUrl(destRelation.Properties().FirstOrDefault(x => x.Name == "url").Value.ToString()))
                                                {
                                                    found = true;
                                                    break;
                                                }
                                            }
                                            else if (relationRel == "AttachedFile")
                                            {
                                                string fileName     = ((JObject)relation.Properties().FirstOrDefault(x => x.Name == "attributes").Value).Properties().FirstOrDefault(x => x.Name == "name").Value.ToString();
                                                string destFileName = ((JObject)destRelation.Properties().FirstOrDefault(x => x.Name == "attributes").Value).Properties().FirstOrDefault(x => x.Name == "name").Value.ToString();
                                                string fileSize     = ((JObject)relation.Properties().FirstOrDefault(x => x.Name == "attributes").Value).Properties().FirstOrDefault(x => x.Name == "resourceSize").Value.ToString();
                                                string destFileSize = ((JObject)destRelation.Properties().FirstOrDefault(x => x.Name == "attributes").Value).Properties().FirstOrDefault(x => x.Name == "resourceSize").Value.ToString();

                                                string fileComment = "";
                                                try
                                                {
                                                    fileComment = ((JObject)relation.Properties().FirstOrDefault(x => x.Name == "attributes").Value).Properties().FirstOrDefault(x => x.Name == "comment").Value.ToString();
                                                }
                                                catch (Exception)
                                                { }

                                                string destFileComment = "";
                                                try
                                                {
                                                    destFileComment = ((JObject)destRelation.Properties().FirstOrDefault(x => x.Name == "attributes").Value).Properties().FirstOrDefault(x => x.Name == "comment").Value.ToString();
                                                }
                                                catch (Exception)
                                                { }

                                                if (fileName == destFileName && fileComment == destFileComment && fileSize == destFileSize)
                                                {
                                                    found = true;
                                                    break;
                                                }
                                            }
                                            else
                                            {
                                                if (relationURL ==
                                                    destRelation.Properties().FirstOrDefault(x => x.Name == "url").Value.ToString())
                                                {
                                                    found = true;
                                                    break;
                                                }
                                            }
                                        }
                                    }

                                    if (found)
                                    {
                                        jsonPatch.AddCollection("remove", "/relations/" + relationId.ToString(), "", "");
                                    }
                                }
                            }
                        }
                    }

                    if (jsonPatch.ToString() != "[]")
                    {
                        log.Info(jsonPatch.ToString());
                        try
                        {
                            await Utility.UpdateWorkItem(destVSTS, destPAT, destPrj, destWorkItemId, jsonPatch, log);

                            return(req.CreateResponse(HttpStatusCode.OK, ""));
                        }
                        catch (Exception ex)
                        {
                            log.Info(ex.Message);
                            return(Utility.CreateErrorResponse(req, ex.Message));
                        }
                    }
                    else
                    {
                        log.Info("+++ Empty JSON Patch +++");
                        return(req.CreateResponse(HttpStatusCode.OK, ""));
                    }
                }
                catch (Exception ex)
                {
                    log.Info(ex.Message);
                    return(Utility.CreateErrorResponse(req, ex.Message));
                }
            }
        }
        public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestMessage req, TraceWriter log)
        {
            // original workitem ID is saved on destination workitem as a Tag "OriginalID=xxx;"
            // http links and attachments are working
            // links to git/tfvc and other kind of links require work to run correctly

            // some fields are ignored by default, like created by, date, updated by, date, etc...

            // If the endpoint is "Tested" with the test button of Azure DevOps Service Hook wizard
            // it gives an error because it receives "Fabrikam" as the team project
            // Don't know if it's ok to trap it or leave the error.

            log.Info($"++++++++++WorkItemCreate was triggered!");
            HttpContent requestContent = req.Content;
            string      jsonContent    = requestContent.ReadAsStringAsync().Result;

            log.Info(jsonContent);

            JObject originalWI   = JObject.Parse(jsonContent);
            string  workItemType = Uri.UnescapeDataString((string)originalWI["resource"]["fields"]["System.WorkItemType"]);
            string  title        = Uri.UnescapeDataString((string)originalWI["resource"]["fields"]["System.Title"]);
            string  workItemID   = (string)originalWI["resource"]["id"];

            string areapath  = Utility.ProcessAreasAndIterations((string)originalWI["resource"]["fields"]["System.AreaPath"]);
            string iteration = Utility.ProcessAreasAndIterations((string)originalWI["resource"]["fields"]["System.IterationPath"]);

            string sourceVSTS = Uri.UnescapeDataString((string)originalWI["resourceContainers"]["collection"]["baseUrl"]);

            if (sourceVSTS.LastIndexOf('/') == sourceVSTS.Length - 1)
            {
                sourceVSTS = sourceVSTS.Substring(0, sourceVSTS.Length - 1);
            }
            string sourceTeamProject = Uri.UnescapeDataString((string)originalWI["resource"]["fields"]["System.TeamProject"]);

            log.Info(workItemType);
            log.Info(title);
            log.Info(workItemID);
            log.Info(sourceTeamProject);

            string destVSTS;
            string destPrj;
            string destPAT;

            Utility.SetVSTSAccountInfo("", sourceTeamProject, out destVSTS, out destPrj, out destPAT);

            log.Info("PAT . " + destPAT);

            if (workItemType == "Test Plan")
            {
                string testplan = await Utility.CreateTestPlan(destVSTS, destPAT, destPrj, workItemID, title, areapath, iteration, log);

                log.Info("Created testplan: " + testplan);
                return(req.CreateResponse(HttpStatusCode.OK, ""));
            }
            else if (workItemType == "Test Suite")
            {
                if (originalWI["resource"]["fields"]["Microsoft.VSTS.TCM.TestSuiteAudit"] == null)
                // first test suite in a test plan is automatically created when a test plan is created
                // we shouldn't create it, we should search for it and associate it
                {
                    string testPlan = await Utility.GetTestPlanFromRootTestSuite(sourceVSTS, sourceTeamProject, destPAT, workItemID, log);

                    string destTestPlan = await Utility.GetDestinationWorkItemId(destVSTS, destPAT, destPrj, testPlan, log);

                    string destSuites = await Utility.GetTestSuitesFromTestPlan(destVSTS, destPrj, destPAT, destTestPlan, log);

                    string id = GetRootTestSuite(destSuites);

                    if (id != null && await Utility.UpdateWorkItemOriginalIDTag(destVSTS, destPAT, destPrj, id, workItemID, log))
                    {
                        return(req.CreateResponse(HttpStatusCode.OK, ""));
                    }
                    else
                    {
                        return(Utility.CreateErrorResponse(req, "Cannot update destination Test Suite:" + id));
                    }
                }
                else if (originalWI["resource"]["fields"]["Microsoft.VSTS.TCM.TestSuiteAudit"].ToString().StartsWith("Added test suite to parent:"))
                // other test suites inside a test plan should be created and associated normally
                {
                    string sourceParentTestSuite = originalWI["resource"]["fields"]["Microsoft.VSTS.TCM.TestSuiteAudit"].ToString();
                    sourceParentTestSuite = sourceParentTestSuite.Substring(sourceParentTestSuite.LastIndexOf(' ') + 1);
                    string sourceTestPlan = await Utility.GetTestPlanFromGenericTestSuite(sourceVSTS, sourceTeamProject, destPAT, sourceParentTestSuite, log);

                    string destParentTestSuite = await Utility.GetDestinationWorkItemId(destVSTS, destPAT, destPrj, sourceParentTestSuite, log);

                    string destTestPlan = await Utility.GetDestinationWorkItemId(destVSTS, destPAT, destPrj, sourceTestPlan, log);

                    string json = "";

                    if (originalWI["resource"]["fields"]["Microsoft.VSTS.TCM.TestSuiteType"].ToString() == "Static")
                    {
                        json = "{ \"suiteType\": \"StaticTestSuite\", \"name\": \"" + title + "\" }";
                    }
                    else if (originalWI["resource"]["fields"]["Microsoft.VSTS.TCM.TestSuiteType"].ToString() == "Query Based")
                    {
                        string queryText = originalWI["resource"]["fields"]["Microsoft.VSTS.TCM.QueryText"].ToString();
                        if (queryText.Contains("System.AreaPath") || queryText.Contains("System.IterationPath"))
                        {
                            queryText = Utility.ProcessAreasAndIterations(queryText);
                        }
                        json = "{ \"suiteType\": \"DynamicTestSuite\", \"name\": \"" + title + "\", " +
                               "\"queryString\": \"" + queryText + "\" }";
                    }
                    else if (originalWI["resource"]["fields"]["Microsoft.VSTS.TCM.TestSuiteType"].ToString() == "Requirement Based")
                    {
                        string sourceRequirementId = await Utility.GetRequirementId(sourceVSTS, sourceTeamProject, destPAT, sourceTestPlan, workItemID, log);

                        string destRequirementId = await Utility.GetDestinationWorkItemId(destVSTS, destPAT, destPrj, sourceRequirementId, log);

                        json = "{ \"suiteType\": \"RequirementTestSuite\", \"name\": \"" + title + "\", " +
                               "\"requirementIds\": [" + destRequirementId + "] }";
                    }

                    string testsuiteid = await Utility.CreateTestSuite(destVSTS, destPAT, destPrj, destTestPlan, destParentTestSuite, workItemID, json, log);

                    log.Info("Created test suite: " + testsuiteid);
                    return(req.CreateResponse(HttpStatusCode.OK, ""));
                }
                else
                {
                    log.Info("Test Suite Not Created");
                    return(Utility.CreateErrorResponse(req, "Test Suite Not Created"));
                }
            }
            else
            {
                var  jsonPatch = new JsonPatch();
                bool hasTags   = false;
                foreach (JProperty property in ((JObject)originalWI["resource"]["fields"]).Properties())
                {
                    log.Info(property.Name + " - " + Utility.JSONEncode(property.Value.ToString()));

                    if (property.Name == "System.Tags")
                    {
                        jsonPatch.Add("add", "/fields/System.Tags", null, Utility.JSONEncode(property.Value.ToString() + "; OriginalID=" + workItemID));
                        hasTags = true;
                    }
                    else if (property.Name == "Microsoft.VSTS.TCM.TestSuiteAudit")
                    {
                        string   value   = property.Value.ToString();
                        string[] numbers = Regex.Split(value, @"\D+");
                        log.Info(value + " - " + numbers.Length.ToString());
                        foreach (string origWI in numbers)
                        {
                            int prova;
                            if (Int32.TryParse(origWI, out prova))
                            {
                                value = value.Replace(origWI, await Utility.GetDestinationWorkItemId(destVSTS, destPAT, destPrj, origWI, log));
                            }
                        }
                        jsonPatch.Add("add", "/fields/" + property.Name, null, value);
                    }
                    else if (property.Name.Contains("Kanban.Column") || property.Name.Contains("System.BoardColum"))
                    {
                        // skip Kanban.Column(s)
                        // should be moved to jsonPatch add
                    }
                    else
                    {
                        jsonPatch.ParseAndAddProperty(property);
                    }
                }
                if (!hasTags)
                {
                    jsonPatch.Add("add", "/fields/System.Tags", null, "OriginalID=" + workItemID);
                }

                if (originalWI["resource"]["relations"] == null)
                {
                    log.Info("+++No Relations+++");
                }
                else
                {
                    log.Info(originalWI["resource"]["relations"].GetType().ToString());

                    JArray relations = (JArray)originalWI["resource"]["relations"];
                    foreach (JObject relation in relations.Children())
                    {
                        foreach (JProperty property in relation.Properties())
                        {
                            log.Info(property.Name + " - " + Utility.JSONEncode(property.Value.ToString()));
                        }

                        string rel    = relation.Properties().FirstOrDefault(x => x.Name == "rel").Value.ToString();
                        string relUrl = "";

                        if (rel == "AttachedFile")
                        {
                            // attachments

                            relUrl = relation.Properties().FirstOrDefault(x => x.Name == "url").Value.ToString();
                            byte[] attachment = await Utility.GetSourceAttachment(relUrl + "?api-version=4.1", destPAT, log);

                            log.Info("Attachment length: " + attachment.Length.ToString());

                            string fileName = ((JObject)relation.Properties().FirstOrDefault(x => x.Name == "attributes").Value).Properties().FirstOrDefault(x => x.Name == "name").Value.ToString();
                            log.Info(fileName);
                            string jsonAttach = await Utility.CreateAttachment(destVSTS, destPAT, fileName, attachment, log);

                            JObject jsonObj = JObject.Parse(jsonAttach);

                            relUrl = Uri.UnescapeDataString((string)jsonObj["url"]);
                            log.Info(relUrl);
                        }
                        else
                        {
                            relUrl = await Utility.GetLinkedUrl(destVSTS, destPAT, destPrj, relation, log);
                        }

                        jsonPatch.AddCollection("add", "/relations/-", "",
                                                "{" +
                                                "  \"rel\": \"" + rel + "\"," +
                                                "  \"url\": \"" + relUrl + "\"," +
                                                "  \"attributes\": " + relation.Properties().FirstOrDefault(x => x.Name == "attributes").Value.ToString() +
                                                "}");
                    }
                }
                log.Info(jsonPatch.ToString());

                try
                {
                    await CreateWorkItem(destVSTS, destPAT, destPrj, workItemType, jsonPatch, log);

                    return(req.CreateResponse(HttpStatusCode.OK, ""));
                }
                catch (Exception ex)
                {
                    log.Info(ex.Message);
                    return(Utility.CreateErrorResponse(req, ex.Message));
                }
            }
        }