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.
- package/dist/index.js +49 -13
- 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
|
-
|
|
61
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
);
|