@devicecloud.dev/dcd
Version:
Better cloud maestro testing
144 lines (143 loc) • 6.58 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@oclif/core");
const constants_1 = require("../constants");
const api_gateway_1 = require("../gateways/api-gateway");
const styling_1 = require("../utils/styling");
class List extends core_1.Command {
static description = 'List recent flow uploads for your organization';
static enableJsonFlag = true;
static examples = [
'<%= config.bin %> <%= command.id %>',
'<%= config.bin %> <%= command.id %> --limit 10',
'<%= config.bin %> <%= command.id %> --name "nightly-*" # Quote wildcards to prevent shell expansion!',
'<%= config.bin %> <%= command.id %> --from 2024-01-01 --to 2024-01-31',
'<%= config.bin %> <%= command.id %> --json',
];
static flags = {
apiKey: constants_1.flags.apiKey,
apiUrl: constants_1.flags.apiUrl,
from: core_1.Flags.string({
description: 'Filter uploads created on or after this date (ISO 8601 format, e.g., 2024-01-01)',
}),
json: core_1.Flags.boolean({
description: 'Output in JSON format',
}),
limit: core_1.Flags.integer({
default: 20,
description: 'Maximum number of uploads to return',
}),
name: core_1.Flags.string({
description: 'Filter by upload name (supports * wildcard, e.g., "nightly-*"). IMPORTANT: Always quote wildcards to prevent shell expansion!',
}),
offset: core_1.Flags.integer({
default: 0,
description: 'Number of uploads to skip (for pagination)',
}),
to: core_1.Flags.string({
description: 'Filter uploads created on or before this date (ISO 8601 format, e.g., 2024-01-31)',
}),
};
async run() {
const { flags } = await this.parse(List);
const { apiKey: apiKeyFlag, apiUrl, from, json, limit, name, offset, to, } = flags;
const apiKey = apiKeyFlag || process.env.DEVICE_CLOUD_API_KEY;
if (!apiKey) {
this.error('API key is required. Please provide it via --api-key flag or DEVICE_CLOUD_API_KEY environment variable.');
return;
}
// Validate date formats if provided
if (from && Number.isNaN(Date.parse(from))) {
this.error('Invalid --from date format. Please use ISO 8601 format (e.g., 2024-01-01).');
return;
}
if (to && Number.isNaN(Date.parse(to))) {
this.error('Invalid --to date format. Please use ISO 8601 format (e.g., 2024-01-31).');
return;
}
// Detect potential shell expansion of wildcards
if (name) {
this.detectShellExpansion(name);
}
try {
const response = await api_gateway_1.ApiGateway.listUploads(apiUrl, apiKey, {
from,
limit,
name,
offset,
to,
});
if (json) {
return response;
}
this.displayResults(response);
}
catch (error) {
this.error(`Failed to list uploads: ${error.message}`);
}
}
/**
* Detects if the provided name parameter likely underwent shell expansion
* Warns the user if shell expansion is detected
* @param name - The name parameter to check for shell expansion
* @returns void
*/
detectShellExpansion(name) {
const shellExpansionIndicators = [
// Contains file path separators (likely expanded to file paths)
name.includes('/') || name.includes('\\'),
// Contains file extensions (likely expanded to filenames)
/\.(yaml|yml|json|txt|md|ts|js|py|sh)$/i.test(name),
// Looks like multiple space-separated filenames (shell expanded glob to multiple files)
name.includes(' ') && !name.includes('*') && !name.includes('?'),
];
if (shellExpansionIndicators.some(Boolean)) {
this.warn(`\nThe --name parameter appears to have been expanded by your shell: "${name}"\n` +
'Wildcards like * should be quoted to prevent shell expansion.\n' +
'Examples:\n' +
' ✓ Correct: dcd list --name "nightly-*"\n' +
' ✓ Correct: dcd list --name \'nightly-*\'\n' +
' ✗ Incorrect: dcd list --name nightly-*\n');
}
}
displayResults(response) {
const { uploads, total, limit, offset } = response;
if (uploads.length === 0) {
this.log('\nNo uploads found matching your criteria.\n');
return;
}
this.log((0, styling_1.sectionHeader)('Recent Uploads'));
this.log(` ${styling_1.colors.dim('Showing')} ${uploads.length} ${styling_1.colors.dim('of')} ${total} ${styling_1.colors.dim('uploads')}`);
if (offset > 0) {
this.log(` ${styling_1.colors.dim('(offset:')} ${offset}${styling_1.colors.dim(')')}`);
}
this.log('');
for (const upload of uploads) {
const date = new Date(upload.created_at);
const formattedDate = date.toLocaleDateString('en-US', {
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
month: 'short',
year: 'numeric',
});
// Upload name
const displayName = upload.name || styling_1.colors.dim('(unnamed)');
this.log(` ${styling_1.colors.bold(displayName)}`);
// Upload ID and date
this.log(` ${styling_1.colors.dim('ID:')} ${(0, styling_1.formatId)(upload.id)}`);
this.log(` ${styling_1.colors.dim('Created:')} ${formattedDate}`);
// Console URL
this.log(` ${styling_1.colors.dim('Console:')} ${(0, styling_1.formatUrl)(upload.consoleUrl)}`);
this.log('');
}
// Pagination hint
if (total > offset + uploads.length) {
const remaining = total - (offset + uploads.length);
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`);
}
// Hint about getting detailed status
this.log(` ${styling_1.colors.dim('Tip: Use')} dcd status --upload-id <id> ${styling_1.colors.dim('for detailed test results')}\n`);
}
}
exports.default = List;