Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions DevOps/Actions/ActionHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,66 @@
using DevOps.Responses;
using DevOps.Services;
using DevOps.Utils;
using Newtonsoft.Json;
using Spectre.Console;

namespace DevOps.Actions;

internal static class ActionHelpers
{
/// <summary>
/// Writes work items as machine-readable JSON or CSV to stdout. Returns a non-zero
/// exit code (with an error) for an unknown format.
/// </summary>
internal static int WriteWorkItemsOutput(List<WorkItemResponse> items, string format)
{
switch (format?.ToLowerInvariant())
{
case "json":
var projected = items.Select(i => new
{
id = i.Id,
type = i.Fields.WorkItemType,
title = i.Fields.Title,
state = i.Fields.State,
assignedTo = i.Fields.AssignedTo?.DisplayName,
project = i.Fields.TeamProject,
parentId = i.Fields.ParentId,
priority = i.Fields.Priority,
createdDate = i.Fields.CreatedDate,
changedDate = i.Fields.ChangedDate
});
Console.WriteLine(JsonConvert.SerializeObject(projected, Formatting.Indented));
return 0;

case "csv":
Console.WriteLine("id,type,title,state,assigned_to,project,parent_id,priority,created,changed");
foreach (var i in items)
{
var f = i.Fields;
Console.WriteLine(string.Join(",",
i.Id,
Csv(f.WorkItemType), Csv(f.Title), Csv(f.State),
Csv(f.AssignedTo?.DisplayName), Csv(f.TeamProject),
f.ParentId?.ToString() ?? "", f.Priority?.ToString() ?? "",
f.CreatedDate.ToString("yyyy-MM-dd"), f.ChangedDate.ToString("yyyy-MM-dd")));
}
return 0;

default:
ConsoleHelper.WriteError($"Unknown output format '{format}'. Use 'json' or 'csv'.");
return 1;
}
}

private static string Csv(string value)
{
if (string.IsNullOrEmpty(value)) return "";
return value.Contains(',') || value.Contains('"') || value.Contains('\n')
? "\"" + value.Replace("\"", "\"\"") + "\""
: value;
}

/// <summary>Creates a table with bold headers, sized to the terminal, using the configured border.</summary>
internal static Table NewTable(params string[] columns)
{
Expand Down
3 changes: 3 additions & 0 deletions DevOps/Actions/GetAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ internal static async Task<int> Execute(GetOptions opts, CancellationToken ct)
var project = ConfigService.ResolveProject(opts.Project);
var item = await HttpService.GetWorkItem(opts.Id, project, ct);

if (!string.IsNullOrEmpty(opts.Output))
return ActionHelpers.WriteWorkItemsOutput([item], opts.Output);

Console.WriteLine($"ID : {item.Id}");
Console.WriteLine($"Type : {item.Fields.WorkItemType}");
Console.WriteLine($"Title : {item.Fields.Title}");
Expand Down
3 changes: 3 additions & 0 deletions DevOps/Actions/ListAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ internal static async Task<int> Execute(ListOptions opts, CancellationToken ct)
var project = ConfigService.ResolveProject(opts.Project);
var (items, totalMatched) = await HttpService.ListWorkItems(project, opts.State, opts.Type, opts.AssignedTo, opts.Query, opts.ParentId, opts.Top, ct);

if (!string.IsNullOrEmpty(opts.Output))
return ActionHelpers.WriteWorkItemsOutput(items, opts.Output);

if (items.Count == 0)
{
Console.WriteLine("No work items found.");
Expand Down
3 changes: 3 additions & 0 deletions DevOps/Actions/MineAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ internal static async Task<int> Execute(MineOptions opts, CancellationToken ct)
var project = ConfigService.ResolveProject(opts.Project);
var (items, totalMatched) = await HttpService.ListWorkItems(project, opts.State, opts.Type, "me", opts.Query, opts.ParentId, opts.Top, ct);

if (!string.IsNullOrEmpty(opts.Output))
return ActionHelpers.WriteWorkItemsOutput(items, opts.Output);

if (items.Count == 0)
{
Console.WriteLine("No work items assigned to you.");
Expand Down
3 changes: 3 additions & 0 deletions DevOps/Options/GetOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ public class GetOptions

[Option('p', "project", Required = false, HelpText = "Project name. Uses default if configured.")]
public string Project { get; set; }

[Option('o', "output", Required = false, HelpText = "Output format: 'json' or 'csv'. Defaults to a detailed view.")]
public string Output { get; set; }
}
3 changes: 3 additions & 0 deletions DevOps/Options/ListOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ public class ListOptions

[Option('n', "top", Required = false, Default = 50, HelpText = "Maximum number of work items to fetch (default: 50).")]
public int Top { get; set; }

[Option('o', "output", Required = false, HelpText = "Output format: 'json' or 'csv'. Defaults to a table.")]
public string Output { get; set; }
}
3 changes: 3 additions & 0 deletions DevOps/Options/MineOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ public class MineOptions

[Option('n', "top", Required = false, Default = 50, HelpText = "Maximum number of work items to fetch (default: 50).")]
public int Top { get; set; }

[Option('o', "output", Required = false, HelpText = "Output format: 'json' or 'csv'. Defaults to a table.")]
public string Output { get; set; }
}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ devops get -i 1234 -p AnotherProject
|---|---|---|
| `--id` | `-i` | Work item ID (required) |
| `--project` | `-p` | Project name (uses default if configured) |
| `--output` | `-o` | Output format: `json` or `csv`. Defaults to a detailed view |

---

Expand All @@ -112,6 +113,7 @@ devops mine -p 1234 # only children of work item 1234
| `--query` | `-q` | Additional WIQL WHERE clause |
| `--parent` | `-p` | Filter by parent work item ID |
| `--top` | `-n` | Maximum number of work items to fetch (default: 50) |
| `--output` | `-o` | Output format: `json` or `csv`. Defaults to a table |

---

Expand All @@ -124,6 +126,7 @@ devops list -t Bug -a me
devops list -P MyProject -s "In Progress" -t Task
devops list -p 1234 # only children of work item 1234
devops list -n 200 # fetch up to 200 items instead of the default 50
devops list -s Active -o json # machine-readable output for scripting
devops list -q "[System.IterationPath] UNDER 'MyProject\\Sprint 1'"
```

Expand All @@ -138,6 +141,7 @@ Queries fetch up to `--top` items (default 50). When more match than were fetche
| `--query` | `-q` | WIQL WHERE clause for advanced filtering |
| `--parent` | `-p` | Filter by parent work item ID |
| `--top` | `-n` | Maximum number of work items to fetch (default: 50) |
| `--output` | `-o` | Output format: `json` or `csv`. Defaults to a table |

---

Expand Down
Loading