@devicecloud.dev/dcd 4.2.0 → 4.2.2
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/commands/list.d.ts +38 -0
- package/dist/commands/list.js +143 -0
- package/dist/commands/status.d.ts +9 -0
- package/dist/commands/status.js +79 -8
- package/dist/config/flags/output.flags.js +1 -1
- package/dist/gateways/api-gateway.d.ts +27 -0
- package/dist/gateways/api-gateway.js +290 -150
- package/dist/methods.js +144 -35
- package/dist/services/execution-plan.service.js +9 -0
- package/dist/services/execution-plan.utils.js +6 -10
- package/dist/services/test-submission.service.js +5 -0
- package/dist/types/domain/device.types.d.ts +1 -0
- package/dist/types/domain/device.types.js +1 -0
- package/dist/types/generated/schema.types.d.ts +1 -1
- package/dist/types/schema.types.d.ts +154 -101
- package/oclif.manifest.json +97 -2
- package/package.json +1 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
type UploadListItem = {
|
|
3
|
+
consoleUrl: string;
|
|
4
|
+
created_at: string;
|
|
5
|
+
id: string;
|
|
6
|
+
name: null | string;
|
|
7
|
+
};
|
|
8
|
+
type ListResponse = {
|
|
9
|
+
limit: number;
|
|
10
|
+
offset: number;
|
|
11
|
+
total: number;
|
|
12
|
+
uploads: UploadListItem[];
|
|
13
|
+
};
|
|
14
|
+
export default class List extends Command {
|
|
15
|
+
static description: string;
|
|
16
|
+
static enableJsonFlag: boolean;
|
|
17
|
+
static examples: string[];
|
|
18
|
+
static flags: {
|
|
19
|
+
apiKey: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
|
+
apiUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
21
|
+
from: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
23
|
+
limit: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
24
|
+
name: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
25
|
+
offset: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
26
|
+
to: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
27
|
+
};
|
|
28
|
+
run(): Promise<ListResponse | void>;
|
|
29
|
+
/**
|
|
30
|
+
* Detects if the provided name parameter likely underwent shell expansion
|
|
31
|
+
* Warns the user if shell expansion is detected
|
|
32
|
+
* @param name - The name parameter to check for shell expansion
|
|
33
|
+
* @returns void
|
|
34
|
+
*/
|
|
35
|
+
private detectShellExpansion;
|
|
36
|
+
private displayResults;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
const api_gateway_1 = require("../gateways/api-gateway");
|
|
6
|
+
const styling_1 = require("../utils/styling");
|
|
7
|
+
class List extends core_1.Command {
|
|
8
|
+
static description = 'List recent flow uploads for your organization';
|
|
9
|
+
static enableJsonFlag = true;
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> <%= command.id %>',
|
|
12
|
+
'<%= config.bin %> <%= command.id %> --limit 10',
|
|
13
|
+
'<%= config.bin %> <%= command.id %> --name "nightly-*" # Quote wildcards to prevent shell expansion!',
|
|
14
|
+
'<%= config.bin %> <%= command.id %> --from 2024-01-01 --to 2024-01-31',
|
|
15
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
16
|
+
];
|
|
17
|
+
static flags = {
|
|
18
|
+
apiKey: constants_1.flags.apiKey,
|
|
19
|
+
apiUrl: constants_1.flags.apiUrl,
|
|
20
|
+
from: core_1.Flags.string({
|
|
21
|
+
description: 'Filter uploads created on or after this date (ISO 8601 format, e.g., 2024-01-01)',
|
|
22
|
+
}),
|
|
23
|
+
json: core_1.Flags.boolean({
|
|
24
|
+
description: 'Output in JSON format',
|
|
25
|
+
}),
|
|
26
|
+
limit: core_1.Flags.integer({
|
|
27
|
+
default: 20,
|
|
28
|
+
description: 'Maximum number of uploads to return',
|
|
29
|
+
}),
|
|
30
|
+
name: core_1.Flags.string({
|
|
31
|
+
description: 'Filter by upload name (supports * wildcard, e.g., "nightly-*"). IMPORTANT: Always quote wildcards to prevent shell expansion!',
|
|
32
|
+
}),
|
|
33
|
+
offset: core_1.Flags.integer({
|
|
34
|
+
default: 0,
|
|
35
|
+
description: 'Number of uploads to skip (for pagination)',
|
|
36
|
+
}),
|
|
37
|
+
to: core_1.Flags.string({
|
|
38
|
+
description: 'Filter uploads created on or before this date (ISO 8601 format, e.g., 2024-01-31)',
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
async run() {
|
|
42
|
+
const { flags } = await this.parse(List);
|
|
43
|
+
const { apiKey: apiKeyFlag, apiUrl, from, json, limit, name, offset, to, } = flags;
|
|
44
|
+
const apiKey = apiKeyFlag || process.env.DEVICE_CLOUD_API_KEY;
|
|
45
|
+
if (!apiKey) {
|
|
46
|
+
this.error('API key is required. Please provide it via --api-key flag or DEVICE_CLOUD_API_KEY environment variable.');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// Validate date formats if provided
|
|
50
|
+
if (from && Number.isNaN(Date.parse(from))) {
|
|
51
|
+
this.error('Invalid --from date format. Please use ISO 8601 format (e.g., 2024-01-01).');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (to && Number.isNaN(Date.parse(to))) {
|
|
55
|
+
this.error('Invalid --to date format. Please use ISO 8601 format (e.g., 2024-01-31).');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// Detect potential shell expansion of wildcards
|
|
59
|
+
if (name) {
|
|
60
|
+
this.detectShellExpansion(name);
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const response = await api_gateway_1.ApiGateway.listUploads(apiUrl, apiKey, {
|
|
64
|
+
from,
|
|
65
|
+
limit,
|
|
66
|
+
name,
|
|
67
|
+
offset,
|
|
68
|
+
to,
|
|
69
|
+
});
|
|
70
|
+
if (json) {
|
|
71
|
+
return response;
|
|
72
|
+
}
|
|
73
|
+
this.displayResults(response);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
this.error(`Failed to list uploads: ${error.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Detects if the provided name parameter likely underwent shell expansion
|
|
81
|
+
* Warns the user if shell expansion is detected
|
|
82
|
+
* @param name - The name parameter to check for shell expansion
|
|
83
|
+
* @returns void
|
|
84
|
+
*/
|
|
85
|
+
detectShellExpansion(name) {
|
|
86
|
+
const shellExpansionIndicators = [
|
|
87
|
+
// Contains file path separators (likely expanded to file paths)
|
|
88
|
+
name.includes('/') || name.includes('\\'),
|
|
89
|
+
// Contains file extensions (likely expanded to filenames)
|
|
90
|
+
/\.(yaml|yml|json|txt|md|ts|js|py|sh)$/i.test(name),
|
|
91
|
+
// Looks like multiple space-separated filenames (shell expanded glob to multiple files)
|
|
92
|
+
name.includes(' ') && !name.includes('*') && !name.includes('?'),
|
|
93
|
+
];
|
|
94
|
+
if (shellExpansionIndicators.some(Boolean)) {
|
|
95
|
+
this.warn(`\nThe --name parameter appears to have been expanded by your shell: "${name}"\n` +
|
|
96
|
+
'Wildcards like * should be quoted to prevent shell expansion.\n' +
|
|
97
|
+
'Examples:\n' +
|
|
98
|
+
' ✓ Correct: dcd list --name "nightly-*"\n' +
|
|
99
|
+
' ✓ Correct: dcd list --name \'nightly-*\'\n' +
|
|
100
|
+
' ✗ Incorrect: dcd list --name nightly-*\n');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
displayResults(response) {
|
|
104
|
+
const { uploads, total, limit, offset } = response;
|
|
105
|
+
if (uploads.length === 0) {
|
|
106
|
+
this.log('\nNo uploads found matching your criteria.\n');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
this.log((0, styling_1.sectionHeader)('Recent Uploads'));
|
|
110
|
+
this.log(` ${styling_1.colors.dim('Showing')} ${uploads.length} ${styling_1.colors.dim('of')} ${total} ${styling_1.colors.dim('uploads')}`);
|
|
111
|
+
if (offset > 0) {
|
|
112
|
+
this.log(` ${styling_1.colors.dim('(offset:')} ${offset}${styling_1.colors.dim(')')}`);
|
|
113
|
+
}
|
|
114
|
+
this.log('');
|
|
115
|
+
for (const upload of uploads) {
|
|
116
|
+
const date = new Date(upload.created_at);
|
|
117
|
+
const formattedDate = date.toLocaleDateString('en-US', {
|
|
118
|
+
day: 'numeric',
|
|
119
|
+
hour: '2-digit',
|
|
120
|
+
minute: '2-digit',
|
|
121
|
+
month: 'short',
|
|
122
|
+
year: 'numeric',
|
|
123
|
+
});
|
|
124
|
+
// Upload name
|
|
125
|
+
const displayName = upload.name || styling_1.colors.dim('(unnamed)');
|
|
126
|
+
this.log(` ${styling_1.colors.bold(displayName)}`);
|
|
127
|
+
// Upload ID and date
|
|
128
|
+
this.log(` ${styling_1.colors.dim('ID:')} ${(0, styling_1.formatId)(upload.id)}`);
|
|
129
|
+
this.log(` ${styling_1.colors.dim('Created:')} ${formattedDate}`);
|
|
130
|
+
// Console URL
|
|
131
|
+
this.log(` ${styling_1.colors.dim('Console:')} ${(0, styling_1.formatUrl)(upload.consoleUrl)}`);
|
|
132
|
+
this.log('');
|
|
133
|
+
}
|
|
134
|
+
// Pagination hint
|
|
135
|
+
if (total > offset + uploads.length) {
|
|
136
|
+
const remaining = total - (offset + uploads.length);
|
|
137
|
+
this.log(` ${styling_1.colors.dim('Use')} --offset ${offset + limit} ${styling_1.colors.dim('to see the next')} ${Math.min(remaining, limit)} ${styling_1.colors.dim('uploads')}\n`);
|
|
138
|
+
}
|
|
139
|
+
// Hint about getting detailed status
|
|
140
|
+
this.log(` ${styling_1.colors.dim('Tip: Use')} dcd status --upload-id <id> ${styling_1.colors.dim('for detailed test results')}\n`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.default = List;
|
|
@@ -9,9 +9,12 @@ type StatusResponse = {
|
|
|
9
9
|
message: string;
|
|
10
10
|
};
|
|
11
11
|
consoleUrl?: string;
|
|
12
|
+
createdAt?: string;
|
|
12
13
|
error?: string;
|
|
14
|
+
name?: string;
|
|
13
15
|
status: 'CANCELLED' | 'FAILED' | 'PASSED' | 'PENDING' | 'RUNNING';
|
|
14
16
|
tests: {
|
|
17
|
+
createdAt?: string;
|
|
15
18
|
durationSeconds?: number;
|
|
16
19
|
failReason?: string;
|
|
17
20
|
name: string;
|
|
@@ -31,5 +34,11 @@ export default class Status extends Command {
|
|
|
31
34
|
'upload-id': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
32
35
|
};
|
|
33
36
|
run(): Promise<StatusResponse | void>;
|
|
37
|
+
/**
|
|
38
|
+
* Format an ISO date string to a human-readable local date/time
|
|
39
|
+
* @param isoString - ISO 8601 date string
|
|
40
|
+
* @returns Formatted local date/time string
|
|
41
|
+
*/
|
|
42
|
+
private formatDateTime;
|
|
34
43
|
}
|
|
35
44
|
export {};
|
package/dist/commands/status.js
CHANGED
|
@@ -47,8 +47,10 @@ class Status extends core_1.Command {
|
|
|
47
47
|
}
|
|
48
48
|
let lastError = null;
|
|
49
49
|
let status = null;
|
|
50
|
+
let attemptsMade = 0;
|
|
50
51
|
for (let attempt = 1; attempt <= 5; attempt++) {
|
|
51
52
|
try {
|
|
53
|
+
attemptsMade = attempt;
|
|
52
54
|
status = (await api_gateway_1.ApiGateway.getUploadStatus(apiUrl, apiKey, {
|
|
53
55
|
name,
|
|
54
56
|
uploadId,
|
|
@@ -57,57 +59,109 @@ class Status extends core_1.Command {
|
|
|
57
59
|
}
|
|
58
60
|
catch (error) {
|
|
59
61
|
lastError = error;
|
|
60
|
-
if (
|
|
62
|
+
// Check if this is a retryable error (network/timeout issues)
|
|
63
|
+
// Non-retryable errors: 4xx client errors (bad request, not found, unauthorized, forbidden)
|
|
64
|
+
const isNetworkError = lastError.name === 'NetworkError' ||
|
|
65
|
+
(error instanceof TypeError && lastError.message === 'fetch failed');
|
|
66
|
+
const isClientError = lastError.message.includes('Invalid request:') ||
|
|
67
|
+
lastError.message.includes('Resource not found') ||
|
|
68
|
+
lastError.message.includes('Authentication failed') ||
|
|
69
|
+
lastError.message.includes('Access denied') ||
|
|
70
|
+
lastError.message.includes('Invalid API key') ||
|
|
71
|
+
lastError.message.includes('Rate limit exceeded');
|
|
72
|
+
// Don't retry client errors - they won't succeed on retry
|
|
73
|
+
if (isClientError) {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
// Only retry network errors
|
|
77
|
+
if (attempt < 5 && isNetworkError) {
|
|
61
78
|
this.log(`Network error on attempt ${attempt}/5. Retrying...`);
|
|
62
79
|
await new Promise((resolve) => {
|
|
63
80
|
setTimeout(resolve, 1000 * attempt);
|
|
64
81
|
});
|
|
65
82
|
}
|
|
83
|
+
else if (attempt < 5) {
|
|
84
|
+
// For other errors (server errors), retry but with different message
|
|
85
|
+
this.log(`Request failed on attempt ${attempt}/5. Retrying...`);
|
|
86
|
+
await new Promise((resolve) => {
|
|
87
|
+
setTimeout(resolve, 1000 * attempt);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
66
90
|
}
|
|
67
91
|
}
|
|
68
92
|
if (!status) {
|
|
93
|
+
// Check if this was a client error (non-retryable)
|
|
94
|
+
const isClientError = lastError && (lastError.message.includes('Invalid request:') ||
|
|
95
|
+
lastError.message.includes('Resource not found') ||
|
|
96
|
+
lastError.message.includes('Authentication failed') ||
|
|
97
|
+
lastError.message.includes('Access denied') ||
|
|
98
|
+
lastError.message.includes('Invalid API key') ||
|
|
99
|
+
lastError.message.includes('Rate limit exceeded'));
|
|
100
|
+
if (isClientError) {
|
|
101
|
+
// For client errors, show the error immediately without connectivity check
|
|
102
|
+
const errorMessage = lastError?.message || 'Unknown error';
|
|
103
|
+
if (json) {
|
|
104
|
+
return {
|
|
105
|
+
status: 'FAILED',
|
|
106
|
+
error: errorMessage,
|
|
107
|
+
attempts: attemptsMade,
|
|
108
|
+
tests: [],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
this.error(errorMessage);
|
|
112
|
+
}
|
|
69
113
|
// Check if the failure is due to internet connectivity issues
|
|
70
114
|
const connectivityCheck = await (0, connectivity_1.checkInternetConnectivity)();
|
|
71
115
|
let errorMessage;
|
|
72
116
|
if (connectivityCheck.connected) {
|
|
73
|
-
errorMessage = `Failed to get status after
|
|
117
|
+
errorMessage = `Failed to get status after ${attemptsMade} attempt${attemptsMade > 1 ? 's' : ''}. Internet appears functional but unable to reach API. Last error: ${lastError?.message || 'Unknown error'}`;
|
|
74
118
|
}
|
|
75
119
|
else {
|
|
76
120
|
// Build detailed error message with endpoint diagnostics
|
|
77
121
|
const endpointDetails = connectivityCheck.endpointResults
|
|
78
122
|
.map((r) => ` - ${r.endpoint}: ${r.error} (${r.latencyMs}ms)`)
|
|
79
123
|
.join('\n');
|
|
80
|
-
errorMessage = `Failed to get status after
|
|
124
|
+
errorMessage = `Failed to get status after ${attemptsMade} attempt${attemptsMade > 1 ? 's' : ''}.\n\nInternet connectivity check failed - all test endpoints unreachable:\n${endpointDetails}\n\nPlease verify your network connection and DNS resolution.\nLast API error: ${lastError?.message || 'Unknown error'}`;
|
|
81
125
|
}
|
|
82
126
|
if (json) {
|
|
83
127
|
return {
|
|
84
|
-
|
|
128
|
+
status: 'FAILED',
|
|
129
|
+
error: errorMessage,
|
|
130
|
+
attempts: attemptsMade,
|
|
85
131
|
connectivityCheck: {
|
|
86
132
|
connected: connectivityCheck.connected,
|
|
87
133
|
endpointResults: connectivityCheck.endpointResults,
|
|
88
134
|
message: connectivityCheck.message,
|
|
89
135
|
},
|
|
90
|
-
error: errorMessage,
|
|
91
|
-
status: 'FAILED',
|
|
92
136
|
tests: [],
|
|
93
137
|
};
|
|
94
138
|
}
|
|
95
139
|
this.error(errorMessage);
|
|
96
|
-
return;
|
|
97
140
|
}
|
|
98
141
|
try {
|
|
99
142
|
if (json) {
|
|
100
|
-
|
|
143
|
+
// Reconstruct object to ensure tests appears at the bottom
|
|
144
|
+
const { tests, ...rest } = status;
|
|
145
|
+
return {
|
|
146
|
+
...rest,
|
|
147
|
+
tests,
|
|
148
|
+
};
|
|
101
149
|
}
|
|
102
150
|
this.log((0, styling_1.sectionHeader)('Upload Status'));
|
|
103
151
|
// Display overall status
|
|
104
152
|
this.log(` ${(0, styling_1.formatStatus)(status.status)}`);
|
|
153
|
+
if (status.name) {
|
|
154
|
+
this.log(` ${styling_1.colors.dim('Name:')} ${styling_1.colors.bold(status.name)}`);
|
|
155
|
+
}
|
|
105
156
|
if (status.uploadId) {
|
|
106
157
|
this.log(` ${styling_1.colors.dim('Upload ID:')} ${(0, styling_1.formatId)(status.uploadId)}`);
|
|
107
158
|
}
|
|
108
159
|
if (status.appBinaryId) {
|
|
109
160
|
this.log(` ${styling_1.colors.dim('Binary ID:')} ${(0, styling_1.formatId)(status.appBinaryId)}`);
|
|
110
161
|
}
|
|
162
|
+
if (status.createdAt) {
|
|
163
|
+
this.log(` ${styling_1.colors.dim('Created:')} ${this.formatDateTime(status.createdAt)}`);
|
|
164
|
+
}
|
|
111
165
|
if (status.consoleUrl) {
|
|
112
166
|
this.log(` ${styling_1.colors.dim('Console:')} ${(0, styling_1.formatUrl)(status.consoleUrl)}`);
|
|
113
167
|
}
|
|
@@ -121,6 +175,9 @@ class Status extends core_1.Command {
|
|
|
121
175
|
if (item.durationSeconds) {
|
|
122
176
|
this.log(` ${styling_1.colors.dim('Duration:')} ${(0, methods_1.formatDurationSeconds)(item.durationSeconds)}`);
|
|
123
177
|
}
|
|
178
|
+
if (item.createdAt) {
|
|
179
|
+
this.log(` ${styling_1.colors.dim('Created:')} ${this.formatDateTime(item.createdAt)}`);
|
|
180
|
+
}
|
|
124
181
|
this.log('');
|
|
125
182
|
}
|
|
126
183
|
}
|
|
@@ -129,5 +186,19 @@ class Status extends core_1.Command {
|
|
|
129
186
|
this.error(`Failed to get status: ${error.message}`);
|
|
130
187
|
}
|
|
131
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Format an ISO date string to a human-readable local date/time
|
|
191
|
+
* @param isoString - ISO 8601 date string
|
|
192
|
+
* @returns Formatted local date/time string
|
|
193
|
+
*/
|
|
194
|
+
formatDateTime(isoString) {
|
|
195
|
+
try {
|
|
196
|
+
const date = new Date(isoString);
|
|
197
|
+
return date.toLocaleString();
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return isoString;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
132
203
|
}
|
|
133
204
|
exports.default = Status;
|
|
@@ -30,7 +30,7 @@ exports.outputFlags = {
|
|
|
30
30
|
description: 'Enable detailed debug logging for troubleshooting issues',
|
|
31
31
|
}),
|
|
32
32
|
'download-artifacts': core_1.Flags.string({
|
|
33
|
-
description: 'Download a zip containing the logs, screenshots and videos for each result in this run.
|
|
33
|
+
description: 'Download a zip containing the logs, screenshots and videos for each result in this run. Options: ALL (everything), FAILED (failures only).',
|
|
34
34
|
options: ['ALL', 'FAILED'],
|
|
35
35
|
}),
|
|
36
36
|
'dry-run': core_1.Flags.boolean({
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { TAppMetadata } from '../types';
|
|
2
2
|
export declare const ApiGateway: {
|
|
3
|
+
/**
|
|
4
|
+
* Enhances generic "fetch failed" errors with more specific diagnostic information
|
|
5
|
+
* @param error - The original TypeError from fetch
|
|
6
|
+
* @param url - The URL that was being fetched
|
|
7
|
+
* @returns Enhanced error with diagnostic information
|
|
8
|
+
*/
|
|
9
|
+
enhanceFetchError(error: TypeError, url: string): Error;
|
|
3
10
|
/**
|
|
4
11
|
* Standardized error handling for API responses
|
|
5
12
|
* @param res - The fetch response object
|
|
@@ -38,14 +45,34 @@ export declare const ApiGateway: {
|
|
|
38
45
|
name?: string;
|
|
39
46
|
uploadId?: string;
|
|
40
47
|
}): Promise<{
|
|
48
|
+
createdAt?: string;
|
|
49
|
+
name?: string;
|
|
41
50
|
status: "CANCELLED" | "FAILED" | "PASSED" | "PENDING";
|
|
42
51
|
tests: Array<{
|
|
52
|
+
createdAt?: string;
|
|
43
53
|
durationSeconds?: number;
|
|
44
54
|
failReason?: string;
|
|
45
55
|
name: string;
|
|
46
56
|
status: "CANCELLED" | "FAILED" | "PASSED" | "PENDING";
|
|
47
57
|
}>;
|
|
48
58
|
}>;
|
|
59
|
+
listUploads(baseUrl: string, apiKey: string, options?: {
|
|
60
|
+
from?: string;
|
|
61
|
+
limit?: number;
|
|
62
|
+
name?: string;
|
|
63
|
+
offset?: number;
|
|
64
|
+
to?: string;
|
|
65
|
+
}): Promise<{
|
|
66
|
+
limit: number;
|
|
67
|
+
offset: number;
|
|
68
|
+
total: number;
|
|
69
|
+
uploads: Array<{
|
|
70
|
+
consoleUrl: string;
|
|
71
|
+
created_at: string;
|
|
72
|
+
id: string;
|
|
73
|
+
name: null | string;
|
|
74
|
+
}>;
|
|
75
|
+
}>;
|
|
49
76
|
uploadFlow(baseUrl: string, apiKey: string, testFormData: FormData): Promise<{
|
|
50
77
|
message?: string;
|
|
51
78
|
results?: import("../types/generated/schema.types").components["schemas"]["IDBResult"][];
|