Skip to content

Commit ab3b43c

Browse files
committed
Show API validation errors inline in Consistem: Create Item input
1 parent 36895d7 commit ab3b43c

File tree

2 files changed

+115
-64
lines changed

2 files changed

+115
-64
lines changed

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363
"workspaceContains:**/*.csp",
6464
"workspaceContains:**/*.csr",
6565
"onCommand:vscode-objectscript.explorer.open",
66-
"onCommand:vscode-objectscript.ccs.createItem",
6766
"onLanguage:objectscript",
6867
"onLanguage:objectscript-int",
6968
"onLanguage:objectscript-class",
@@ -890,7 +889,7 @@
890889
"title": "Follow Source Analysis Link"
891890
},
892891
{
893-
"category": "CCS",
892+
"category": "Consistem",
894893
"command": "vscode-objectscript.ccs.createItem",
895894
"title": "Create Item"
896895
},

src/ccs/commands/createItem.ts

Lines changed: 114 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,29 @@ import { SourceControlApi } from "../sourcecontrol/client";
88
import { CreateItemClient } from "../sourcecontrol/clients/createItemClient";
99
import { getCcsSettings } from "../config/settings";
1010

11-
async function promptForItemName(): Promise<string | undefined> {
11+
interface PromptForItemNameOptions {
12+
initialValue?: string;
13+
validationMessage?: string;
14+
}
15+
16+
async function promptForItemName(options: PromptForItemNameOptions = {}): Promise<string | undefined> {
1217
const hasValidExt = (s: string) => /\.cls$/i.test(s) || /\.mac$/i.test(s);
1318
const hasBadChars = (s: string) => /[\\/]/.test(s) || /\s/.test(s);
1419

1520
const ib = vscode.window.createInputBox();
16-
ib.title = "Create InterSystems Item";
17-
ib.prompt = "Enter the name of the class or routine to create (.cls or .mac)";
18-
ib.placeholder = "MyPackage.MyClass.cls or CCTRIB001.mac";
21+
ib.title = "Criar Item Consistem";
22+
ib.prompt = "Informe o nome da classe ou rotina a ser criada (.cls ou .mac)";
23+
ib.placeholder = "MeuPacote.MinhaClasse.cls ou MINHAROTINA.mac";
1924
ib.ignoreFocusOut = true;
25+
if (options.initialValue) {
26+
ib.value = options.initialValue;
27+
}
28+
if (options.validationMessage) {
29+
ib.validationMessage = {
30+
message: options.validationMessage,
31+
severity: vscode.InputBoxValidationSeverity.Error,
32+
};
33+
}
2034

2135
return await new Promise<string | undefined>((resolve) => {
2236
const disposeAll = () => {
@@ -36,19 +50,22 @@ async function promptForItemName(): Promise<string | undefined> {
3650
const name = ib.value.trim();
3751

3852
if (!name) {
39-
ib.validationMessage = { message: "Item name is required", severity: vscode.InputBoxValidationSeverity.Error };
53+
ib.validationMessage = {
54+
message: "Informe o nome do item",
55+
severity: vscode.InputBoxValidationSeverity.Error,
56+
};
4057
return;
4158
}
4259
if (hasBadChars(name)) {
4360
ib.validationMessage = {
44-
message: "Invalid name: avoid spaces and path separators",
61+
message: "Nome inválido: não use espaços nem separadores de caminho (\\ ou /)",
4562
severity: vscode.InputBoxValidationSeverity.Error,
4663
};
4764
return;
4865
}
4966
if (!hasValidExt(name)) {
5067
ib.validationMessage = {
51-
message: "Please include a valid extension: .cls or .mac",
68+
message: "Inclua uma extensão válida: .cls ou .mac",
5269
severity: vscode.InputBoxValidationSeverity.Error,
5370
};
5471
return;
@@ -142,6 +159,24 @@ function getErrorMessage(err: unknown): string | undefined {
142159
return undefined;
143160
}
144161

162+
function getApiValidationMessage(err: unknown): string | undefined {
163+
const anyErr = err as any;
164+
const response = anyErr?.response;
165+
if (!response?.data) return undefined;
166+
167+
const status = typeof response.status === "number" ? response.status : undefined;
168+
if (typeof status === "number" && status >= 500) {
169+
return undefined;
170+
}
171+
172+
const data = response.data;
173+
if (typeof data === "string" && data.trim()) return data.trim();
174+
if (typeof data?.error === "string" && data.error.trim()) return data.error.trim();
175+
if (typeof data?.message === "string" && data.message.trim()) return data.message.trim();
176+
if (typeof data?.Message === "string" && data.Message.trim()) return data.Message.trim();
177+
return undefined;
178+
}
179+
145180
export async function createItem(): Promise<void> {
146181
const workspaceFolder = await getWsFolder(
147182
"Pick the workspace folder where you want to create the item",
@@ -171,13 +206,6 @@ export async function createItem(): Promise<void> {
171206
}
172207
const namespace = ns.toUpperCase();
173208

174-
const itemName = await promptForItemName();
175-
if (!itemName) {
176-
return;
177-
}
178-
179-
logDebug("CCS createItem invoked", { namespace, itemName });
180-
181209
let sourceControlApi: SourceControlApi;
182210
try {
183211
sourceControlApi = SourceControlApi.fromAtelierApi(api);
@@ -192,61 +220,85 @@ export async function createItem(): Promise<void> {
192220
const { requestTimeout } = getCcsSettings();
193221
const backoff = Math.min(500, Math.max(150, Math.floor(requestTimeout * 0.1)));
194222

195-
try {
196-
const { data, status } = await vscode.window.withProgress(
197-
{
198-
location: vscode.ProgressLocation.Notification,
199-
title: "Creating item...",
200-
cancellable: false,
201-
},
202-
async () => withTimeoutRetry(() => createItemClient.create(namespace, itemName), 2, backoff)
203-
);
223+
let lastValue: string | undefined;
224+
let lastValidationMessage: string | undefined;
204225

205-
if (data.error) {
206-
logError("CCS createItem failed", { namespace, itemName, status, error: data.error });
207-
void vscode.window.showErrorMessage(data.error);
226+
while (true) {
227+
const itemName = await promptForItemName({ initialValue: lastValue, validationMessage: lastValidationMessage });
228+
if (!itemName) {
208229
return;
209230
}
210231

211-
if (status < 200 || status >= 300) {
212-
const message = `Item creation failed with status ${status}.`;
213-
logError("CCS createItem failed", { namespace, itemName, status });
214-
void vscode.window.showErrorMessage(message);
215-
return;
216-
}
232+
lastValue = itemName;
233+
lastValidationMessage = undefined;
217234

218-
if (!data.file) {
219-
const message = "Item created on server but no file path was returned.";
220-
logError("CCS createItem missing file path", { namespace, itemName, response: data });
221-
void vscode.window.showErrorMessage(message);
222-
return;
223-
}
235+
logDebug("Consistem createItem invoked", { namespace, itemName });
224236

225237
try {
226-
await openCreatedFile(data.file);
227-
} catch (openErr) {
228-
logError("Failed to open created file", { file: data.file, error: openErr });
229-
void vscode.window.showWarningMessage("Item created, but the returned file could not be opened.");
230-
}
238+
const { data, status } = await vscode.window.withProgress(
239+
{
240+
location: vscode.ProgressLocation.Notification,
241+
title: "Creating item...",
242+
cancellable: false,
243+
},
244+
async () => withTimeoutRetry(() => createItemClient.create(namespace, itemName), 2, backoff)
245+
);
246+
247+
if (data.error) {
248+
logError("Consistem createItem failed", { namespace, itemName, status, error: data.error });
249+
lastValidationMessage = data.error;
250+
continue;
251+
}
231252

232-
const createdNamespace = data.namespace ?? namespace;
233-
const createdItem = (data as any).itemIdCriado ?? itemName;
234-
const moduleName = extractModuleName(data.file, workspaceFolder);
235-
const location = moduleName ? `${createdNamespace}/${moduleName}` : createdNamespace;
236-
const successMessage = `Item created successfully in ${location}: ${createdItem}`;
237-
logInfo("CCS createItem succeeded", {
238-
namespace: createdNamespace,
239-
module: moduleName,
240-
itemName: createdItem,
241-
file: data.file,
242-
});
243-
void vscode.window.showInformationMessage(successMessage);
244-
} catch (error) {
245-
const errorMessage =
246-
(CreateItemClient as any).getErrorMessage?.(error) ??
247-
getErrorMessage(error) ??
248-
(isTimeoutError(error) ? "Item creation timed out." : "Item creation failed.");
249-
logError("CCS createItem encountered an unexpected error", error);
250-
void vscode.window.showErrorMessage(errorMessage);
253+
if (status < 200 || status >= 300) {
254+
const message = `Item creation failed with status ${status}.`;
255+
logError("Consistem createItem failed", { namespace, itemName, status });
256+
void vscode.window.showErrorMessage(message);
257+
return;
258+
}
259+
260+
if (!data.file) {
261+
const message = "Item created on server but no file path was returned.";
262+
logError("Consistem createItem missing file path", { namespace, itemName, response: data });
263+
void vscode.window.showErrorMessage(message);
264+
return;
265+
}
266+
267+
try {
268+
await openCreatedFile(data.file);
269+
} catch (openErr) {
270+
logError("Failed to open created file", { file: data.file, error: openErr });
271+
void vscode.window.showWarningMessage("Item created, but the returned file could not be opened.");
272+
}
273+
274+
const createdNamespace = data.namespace ?? namespace;
275+
const createdItem = (data as any).itemIdCriado ?? itemName;
276+
const moduleName = extractModuleName(data.file, workspaceFolder);
277+
const location = moduleName ? `${createdNamespace}/${moduleName}` : createdNamespace;
278+
const successMessage = `Item created successfully in ${location}: ${createdItem}`;
279+
logInfo("Consistem createItem succeeded", {
280+
namespace: createdNamespace,
281+
module: moduleName,
282+
itemName: createdItem,
283+
file: data.file,
284+
});
285+
void vscode.window.showInformationMessage(successMessage);
286+
return;
287+
} catch (error) {
288+
const apiValidationMessage = getApiValidationMessage(error);
289+
if (apiValidationMessage) {
290+
logError("Consistem createItem API validation failed", { namespace, itemName, error: apiValidationMessage });
291+
lastValidationMessage = apiValidationMessage;
292+
continue;
293+
}
294+
295+
const errorMessage =
296+
(CreateItemClient as any).getErrorMessage?.(error) ??
297+
getErrorMessage(error) ??
298+
(isTimeoutError(error) ? "Item creation timed out." : "Item creation failed.");
299+
logError("Consistem createItem encountered an unexpected error", error);
300+
void vscode.window.showErrorMessage(errorMessage);
301+
return;
302+
}
251303
}
252304
}

0 commit comments

Comments
 (0)