azdo-cli 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +49 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -42,6 +42,19 @@ async function fetchWithErrors(url, init) {
42
42
  if (response.status === 404) throw new Error("NOT_FOUND");
43
43
  return response;
44
44
  }
45
+ async function readResponseMessage(response) {
46
+ try {
47
+ const body = await response.json();
48
+ if (typeof body.message === "string" && body.message.trim() !== "") {
49
+ return body.message.trim();
50
+ }
51
+ } catch {
52
+ }
53
+ return null;
54
+ }
55
+ function normalizeFieldList(fields) {
56
+ return Array.from(new Set(fields.map((f) => f.trim()).filter((f) => f.length > 0)));
57
+ }
45
58
  function buildExtraFields(fields, requested) {
46
59
  const result = {};
47
60
  for (const name of requested) {
@@ -57,11 +70,18 @@ async function getWorkItem(context, id, pat, extraFields) {
57
70
  `https://dev.azure.com/${encodeURIComponent(context.org)}/${encodeURIComponent(context.project)}/_apis/wit/workitems/${id}`
58
71
  );
59
72
  url.searchParams.set("api-version", "7.1");
60
- if (extraFields && extraFields.length > 0) {
61
- const allFields = [...DEFAULT_FIELDS, ...extraFields];
73
+ const normalizedExtraFields = extraFields ? normalizeFieldList(extraFields) : [];
74
+ if (normalizedExtraFields.length > 0) {
75
+ const allFields = normalizeFieldList([...DEFAULT_FIELDS, ...normalizedExtraFields]);
62
76
  url.searchParams.set("fields", allFields.join(","));
63
77
  }
64
78
  const response = await fetchWithErrors(url.toString(), { headers: authHeaders(pat) });
79
+ if (response.status === 400) {
80
+ const serverMessage = await readResponseMessage(response);
81
+ if (serverMessage) {
82
+ throw new Error(`BAD_REQUEST: ${serverMessage}`);
83
+ }
84
+ }
65
85
  if (!response.ok) {
66
86
  throw new Error(`HTTP_${response.status}`);
67
87
  }
@@ -93,7 +113,7 @@ async function getWorkItem(context, id, pat, extraFields) {
93
113
  areaPath: data.fields["System.AreaPath"],
94
114
  iterationPath: data.fields["System.IterationPath"],
95
115
  url: data._links.html.href,
96
- extraFields: extraFields && extraFields.length > 0 ? buildExtraFields(data.fields, extraFields) : null
116
+ extraFields: normalizedExtraFields.length > 0 ? buildExtraFields(data.fields, normalizedExtraFields) : null
97
117
  };
98
118
  }
99
119
  async function getWorkItemFieldValue(context, id, pat, fieldName) {
@@ -103,6 +123,12 @@ async function getWorkItemFieldValue(context, id, pat, fieldName) {
103
123
  url.searchParams.set("api-version", "7.1");
104
124
  url.searchParams.set("fields", fieldName);
105
125
  const response = await fetchWithErrors(url.toString(), { headers: authHeaders(pat) });
126
+ if (response.status === 400) {
127
+ const serverMessage = await readResponseMessage(response);
128
+ if (serverMessage) {
129
+ throw new Error(`BAD_REQUEST: ${serverMessage}`);
130
+ }
131
+ }
106
132
  if (!response.ok) {
107
133
  throw new Error(`HTTP_${response.status}`);
108
134
  }
@@ -127,12 +153,7 @@ async function updateWorkItem(context, id, pat, fieldName, operations) {
127
153
  body: JSON.stringify(operations)
128
154
  });
129
155
  if (response.status === 400) {
130
- let serverMessage = "Unknown error";
131
- try {
132
- const body = await response.json();
133
- if (body.message) serverMessage = body.message;
134
- } catch {
135
- }
156
+ const serverMessage = await readResponseMessage(response) ?? "Unknown error";
136
157
  throw new Error(`UPDATE_REJECTED: ${serverMessage}`);
137
158
  }
138
159
  if (!response.ok) {
@@ -421,7 +442,7 @@ function validateOrgProjectPair(options) {
421
442
  process.exit(1);
422
443
  }
423
444
  }
424
- function handleCommandError(err, id, context, scope = "write") {
445
+ function handleCommandError(err, id, context, scope = "write", exit = true) {
425
446
  const error = err instanceof Error ? err : new Error(String(err));
426
447
  const msg = error.message;
427
448
  const scopeLabel = scope === "read" ? "Work Items (read)" : "Work Items (Read & Write)";
@@ -444,6 +465,10 @@ function handleCommandError(err, id, context, scope = "write") {
444
465
  process.stderr.write(
445
466
  "Error: Could not connect to Azure DevOps. Check your network connection.\n"
446
467
  );
468
+ } else if (msg.startsWith("BAD_REQUEST:")) {
469
+ const serverMsg = msg.replace("BAD_REQUEST: ", "");
470
+ process.stderr.write(`Error: Request rejected: ${serverMsg}
471
+ `);
447
472
  } else if (msg.startsWith("UPDATE_REJECTED:")) {
448
473
  const serverMsg = msg.replace("UPDATE_REJECTED: ", "");
449
474
  process.stderr.write(`Error: Update rejected: ${serverMsg}
@@ -452,10 +477,21 @@ function handleCommandError(err, id, context, scope = "write") {
452
477
  process.stderr.write(`Error: ${msg}
453
478
  `);
454
479
  }
455
- process.exit(1);
480
+ if (exit) {
481
+ process.exit(1);
482
+ } else {
483
+ process.exitCode = 1;
484
+ }
456
485
  }
457
486
 
458
487
  // src/commands/get-item.ts
488
+ function parseRequestedFields(raw) {
489
+ if (raw === void 0) return void 0;
490
+ const source = Array.isArray(raw) ? raw : [raw];
491
+ const tokens = source.flatMap((entry) => entry.split(/[,\s]+/)).map((field) => field.trim()).filter((field) => field.length > 0);
492
+ if (tokens.length === 0) return void 0;
493
+ return Array.from(new Set(tokens));
494
+ }
459
495
  function stripHtml(html) {
460
496
  let text = html;
461
497
  text = text.replace(/<h[1-6][^>]*>(.*?)<\/h[1-6]>/gi, "\n--- $1 ---\n");
@@ -515,12 +551,12 @@ function createGetItemCommand() {
515
551
  try {
516
552
  context = resolveContext(options);
517
553
  const credential = await resolvePat();
518
- const fieldsList = options.fields ? options.fields.split(",").map((f) => f.trim()) : loadConfig().fields;
554
+ const fieldsList = options.fields !== void 0 ? parseRequestedFields(options.fields) : parseRequestedFields(loadConfig().fields);
519
555
  const workItem = await getWorkItem(context, id, credential.pat, fieldsList);
520
556
  const output = formatWorkItem(workItem, options.short ?? false);
521
557
  process.stdout.write(output + "\n");
522
558
  } catch (err) {
523
- handleCommandError(err, id, context, "read");
559
+ handleCommandError(err, id, context, "read", false);
524
560
  }
525
561
  }
526
562
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azdo-cli",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Azure DevOps CLI tool",
5
5
  "type": "module",
6
6
  "bin": {