public string GetDeploymentErrors(int PastInstances)
        {
            string result = "";
            List <TfsReleaseError>      errorlist = new List <TfsReleaseError>();
            Dictionary <string, string> inputs    = new Dictionary <string, string>();

            var def = relclient.GetReleaseDefinitionsAsync(TfsEnvInfo.ProjectName, TfsEnvInfo.ReleaseDefinitionName, isExactNameMatch: true).Result;

            if (def.Count() > 0)
            {
                var id = def.First().Id;

                var runs = relclient.GetDeploymentsAsync(project: TfsEnvInfo.ProjectName, definitionId: id, deploymentStatus: DeploymentStatus.Failed, operationStatus: DeploymentOperationStatus.PhaseFailed, queryOrder: ReleaseQueryOrder.Descending, top: PastInstances).Result;

                foreach (var run in runs)
                {
                    var rel     = relclient.GetReleaseAsync(TfsEnvInfo.ProjectName, run.Release.Id).Result;
                    var env     = rel.Environments.First(e => e.Id == run.ReleaseEnvironmentReference.Id);
                    var attempt = env.DeploySteps.First(s => s.Attempt == run.Attempt);
                    var phase   = attempt.ReleaseDeployPhases.First(p => p.Status == DeployPhaseStatus.Failed);

                    //assumption here is each phase has only one job that failed
                    var job        = phase.DeploymentJobs.First(j => j.Job.Status == Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.TaskStatus.Failed);
                    var failedtask = job.Tasks.Where(t => t.Status == Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.TaskStatus.Failed).First();

                    var error = new TfsReleaseError()
                    {
                        ReleaseName     = rel.Name,
                        EnvironmentName = env.Name,
                        Attempt         = attempt.Attempt,
                        PhaseType       = phase.PhaseType.ToString(),
                        AgentName       = failedtask.AgentName,
                        TaskName        = failedtask.Name,
                        StartTime       = failedtask.StartTime,
                        FinishTime      = failedtask.FinishTime,
                        ErrorMessages   = failedtask.Issues.Where(i => i.Message != "").Select(i => i.Message).ToArray()
                    };

                    var snapshot = env.DeployPhasesSnapshot.First(dps => dps.Rank == phase.Rank);
                    if (failedtask.Task != null)    //failing at download artifacts
                    {
                        inputs = snapshot.WorkflowTasks.First(w => w.TaskId == failedtask.Task.Id).Inputs;
                    }

                    error.TaskInputs = inputs;

                    List <TfsArtifact> artifacts = new List <TfsArtifact>();
                    foreach (var a in rel.Artifacts)
                    {
                        var build    = a.DefinitionReference.Where(d => d.Key == "version").Select(v => v.Value.Name).First();
                        var buildurl = a.DefinitionReference.Where(d => d.Key == "artifactSourceVersionUrl").Select(v => v.Value.Id).First();
                        artifacts.Add(new TfsArtifact()
                        {
                            Build    = build,
                            BuildUrl = buildurl
                        });
                    }
                    error.Artifacts = artifacts.ToArray();

                    errorlist.Add(error);
                }

                //To download all logs for a given release
                //docs: https://docs.microsoft.com/en-us/rest/api/vsts/release/releases/get%20logs?view=vsts-rest-4.1
                //GET https://{accountName}.vsrm.visualstudio.com/{project}/_apis/release/releases/{releaseId}/logs?api-version=4.1-preview.2
                //client.DownloadFile(rel.LogsContainerUrl, rel.Name + ".zip");
                //The return data seems to have bad encoding; looks like a bug
                //var logs = tfsclient.GetLogsAsync(tfsinfo.ProjectName, id).Result;

                //Handle case when no errors are found
                if (errorlist.Count > 0)
                {
                    result = JsonConvert.SerializeObject(errorlist, Formatting.Indented);
                }
                else
                {
                    result = $"**Warning** No errors found";
                }
            }
            else
            {
                result = $"**Warning** Failed to find Release Definition with name \"{TfsEnvInfo.ReleaseDefinitionName}\"";
            }

            return(result);
        }
        public string GetDeploymentErrors(DateTime mindate, DateTime maxdate)
        {
            string result = "";
            List <TfsReleaseError>      errorlist = new List <TfsReleaseError>();
            Dictionary <string, string> inputs    = new Dictionary <string, string>();

            var runs = relclient.GetDeploymentsAsync(project: TfsEnvInfo.ProjectName, minStartedTime: mindate, maxStartedTime: maxdate, deploymentStatus: DeploymentStatus.Failed, operationStatus: DeploymentOperationStatus.PhaseFailed, queryOrder: ReleaseQueryOrder.Descending).Result;

            foreach (var run in runs)
            {
                var rel     = relclient.GetReleaseAsync(TfsEnvInfo.ProjectName, run.Release.Id).Result;
                var env     = rel.Environments.First(e => e.Id == run.ReleaseEnvironmentReference.Id);
                var attempt = env.DeploySteps.First(s => s.Attempt == run.Attempt);
                var phase   = attempt.ReleaseDeployPhases.First(p => p.Status == DeployPhaseStatus.Failed);

                //assumption here is each phase has only one job that failed
                var job        = phase.DeploymentJobs.First(j => j.Job.Status == TaskStatus.Failed);
                var failedtask = job.Tasks.Where(t => t.Status == TaskStatus.Failed).First();

                //GetDeploymentsAsync return top 50 regardless of minStartedTime or maxStartedTime
                //looks like a bug
                if (failedtask.StartTime >= mindate && failedtask.StartTime <= maxdate)
                {
                    var error = new TfsReleaseError()
                    {
                        ReleaseName     = rel.Name,
                        EnvironmentName = env.Name,
                        Attempt         = attempt.Attempt,
                        PhaseType       = phase.PhaseType.ToString(),
                        AgentName       = failedtask.AgentName,
                        TaskName        = failedtask.Name,
                        StartTime       = failedtask.StartTime,
                        FinishTime      = failedtask.FinishTime,
                        ErrorMessages   = failedtask.Issues.Where(i => i.Message != "").Select(i => i.Message).ToArray()
                    };

                    var snapshot = env.DeployPhasesSnapshot.First(dps => dps.Rank == phase.Rank);
                    if (failedtask.Task != null)    //failing at download artifacts
                    {
                        inputs = snapshot.WorkflowTasks.First(w => w.TaskId == failedtask.Task.Id).Inputs;
                    }

                    error.TaskInputs = inputs;

                    List <TfsArtifact> artifacts = new List <TfsArtifact>();
                    foreach (var a in rel.Artifacts)
                    {
                        var build    = a.DefinitionReference.Where(d => d.Key == "version").Select(v => v.Value.Name).First();
                        var q        = a.DefinitionReference.Where(d => d.Key == "artifactSourceVersionUrl").Select(v => v.Value.Id);
                        var buildurl = (q.Count() > 0) ? q.First() : "";
                        artifacts.Add(new TfsArtifact()
                        {
                            Build    = build,
                            BuildUrl = buildurl
                        });
                    }
                    error.Artifacts = artifacts.ToArray();

                    errorlist.Add(error);
                }
            }

            //Handle case when no errors are found
            if (errorlist.Count > 0)
            {
                result = JsonConvert.SerializeObject(errorlist, Formatting.Indented);
            }
            else
            {
                result = $"**Warning** No errors found";
            }

            return(result);
        }
        public string GetLatestError()
        {
            string result = "";
            List <TfsReleaseError> errorlist = new List <TfsReleaseError>();

            var deflist = relclient.GetReleaseDefinitionsAsync(TfsEnvInfo.ProjectName, TfsEnvInfo.ReleaseDefinitionName, isExactNameMatch: true, expand: ReleaseDefinitionExpands.Environments).Result;

            if (deflist.Count() > 0)
            {
                var def        = deflist.First();
                var defenvlist = def.Environments.Where(e => e.Name == TfsEnvInfo.EnvironmentName);

                if (defenvlist.Count() > 0)
                {
                    var defenv  = defenvlist.First();
                    var rellist = relclient.GetReleasesAsync(def.Id, defenv.Id).Result;

                    if (rellist.Count() > 0)
                    {
                        var rel = rellist.Where(r => r.Name == TfsEnvInfo.ReleaseName);

                        if (rel.Count() > 0)
                        {
                            var relid      = rel.First().Id;
                            var reldetails = relclient.GetReleaseAsync(TfsEnvInfo.ProjectName, relid).Result;
                            var env        = reldetails.Environments.Where(e => e.Name == TfsEnvInfo.EnvironmentName).First();
                            var steplist   = env.DeploySteps.Where(s => s.Status == DeploymentStatus.Failed);

                            foreach (var step in steplist)
                            {
                                //assuming only one phase that fails in any one deployment
                                var phase = step.ReleaseDeployPhases.Where(p => p.Status == DeployPhaseStatus.Failed).First();
                                //assumption here is each phase has only one job that failed
                                var job        = phase.DeploymentJobs.First(j => j.Job.Status == TaskStatus.Failed);
                                var failedtask = job.Tasks.Where(t => t.Status == TaskStatus.Failed).First();
                                var error      = new TfsReleaseError()
                                {
                                    ReleaseName     = reldetails.Name,
                                    EnvironmentName = env.Name,
                                    Attempt         = step.Attempt,
                                    PhaseType       = phase.PhaseType.ToString(),
                                    AgentName       = failedtask.AgentName,
                                    TaskName        = failedtask.Name,
                                    StartTime       = failedtask.StartTime,
                                    FinishTime      = failedtask.FinishTime,
                                    ErrorMessages   = failedtask.Issues.Where(i => i.Message != "").Select(i => i.Message).ToArray()
                                };

                                var logstream = relclient.GetTaskLogAsync(TfsEnvInfo.ProjectName, reldetails.Id, env.Id, phase.Id, failedtask.Id).Result;
                                var reader    = new StreamReader(logstream);
                                var logstring = reader.ReadToEnd();

                                var lines = logstring.Replace("\n", "").Split('\r');

                                var    errorindex   = Array.FindIndex(lines, l => l.Contains(error.ErrorMessages.First()));
                                string logsample    = "";
                                var    samplelength = 10;
                                int    begin        = (errorindex - samplelength) > 0 ? (errorindex - samplelength) : 0;
                                int    end          = (errorindex + samplelength) < lines.Length ? (errorindex + samplelength) : lines.Length;
                                for (int i = begin; i < end; i++)
                                {
                                    logsample += lines[i];
                                }

                                error.ErrorLog = logstring;
                                errorlist.Add(error);
                            }

                            //Handle case when no errors are found
                            if (errorlist.Count > 0)
                            {
                                result = JsonConvert.SerializeObject(errorlist, Formatting.Indented);
                            }
                            else
                            {
                                result = $"**Warning** No errors found";
                            }
                        }
                        else
                        {
                            result = $"**Warning** Failed to find Release Details with name \"{TfsEnvInfo.ReleaseName}\"";
                        }
                    }
                    else
                    {
                        result = $"**Warning** Failed to find Release with name \"{TfsEnvInfo.ReleaseName}\"";
                    }
                }
                else
                {
                    result = $"**Warning** Failed to find Env with name \"{TfsEnvInfo.EnvironmentName}\"";
                }
            }
            else
            {
                result = $"**Warning** Failed to find Release Definition with name \"{TfsEnvInfo.ReleaseDefinitionName}\"";
            }

            return(result);
        }