specshield 2.0.1 → 3.1.1
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/README.md +84 -177
- package/package.json +34 -33
- package/src/api/bdctClient.js +28 -23
- package/src/cli.js +7 -3
- package/src/commands/bdct.js +141 -83
- package/src/commands/init.js +399 -0
- package/src/core/configWriter.js +221 -0
- package/src/core/projectConfig.js +189 -0
- package/src/core/projectDetect.js +180 -0
- package/src/api/contractsClient.js +0 -88
- package/src/commands/contracts.js +0 -561
package/src/commands/bdct.js
CHANGED
|
@@ -7,6 +7,7 @@ const path = require('path');
|
|
|
7
7
|
const fsExtra = require('fs-extra');
|
|
8
8
|
const logger = require('../utils/logger');
|
|
9
9
|
const { getStoredApiKey } = require('../config/localConfig');
|
|
10
|
+
const { applyBdctDefaults } = require('../core/projectConfig');
|
|
10
11
|
const {
|
|
11
12
|
publishProviderSpec,
|
|
12
13
|
publishConsumerContract,
|
|
@@ -31,6 +32,24 @@ function requireToken(token) {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Fill missing CLI options from `.specshield.yml` if one is present, then
|
|
37
|
+
* verify every required field for `command` is set. Exits 2 with a friendly
|
|
38
|
+
* message if anything is missing.
|
|
39
|
+
*/
|
|
40
|
+
function withProjectDefaults(opts, command) {
|
|
41
|
+
try {
|
|
42
|
+
applyBdctDefaults(opts, command);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (err.code === 'MISSING_REQUIRED_OPTIONS') {
|
|
45
|
+
logger.error(err.message);
|
|
46
|
+
process.exit(2);
|
|
47
|
+
}
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
return opts;
|
|
51
|
+
}
|
|
52
|
+
|
|
34
53
|
function fmtDate(iso) {
|
|
35
54
|
if (!iso) return chalk.gray('—');
|
|
36
55
|
try {
|
|
@@ -51,6 +70,26 @@ function hr() {
|
|
|
51
70
|
return chalk.gray(' ─────────────────────────────────────────────────────');
|
|
52
71
|
}
|
|
53
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Flatten a verification result's mismatches.
|
|
75
|
+
* Current backend returns `resultJson` (JSON string of [{endpoint,status,mismatches:[...]}]).
|
|
76
|
+
* Tolerate older shapes that put a flat array on `issues` or `mismatches`.
|
|
77
|
+
*/
|
|
78
|
+
function flattenMismatches(result) {
|
|
79
|
+
if (!result) return [];
|
|
80
|
+
if (Array.isArray(result.issues)) return result.issues;
|
|
81
|
+
if (Array.isArray(result.mismatches)) return result.mismatches;
|
|
82
|
+
if (typeof result.resultJson === 'string' && result.resultJson.length > 0) {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(result.resultJson);
|
|
85
|
+
if (Array.isArray(parsed)) {
|
|
86
|
+
return parsed.flatMap(ep => Array.isArray(ep.mismatches) ? ep.mismatches : []);
|
|
87
|
+
}
|
|
88
|
+
} catch { /* fall through */ }
|
|
89
|
+
}
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
|
|
54
93
|
/** Strip ANSI escape codes for length measurement */
|
|
55
94
|
function stripAnsi(str) {
|
|
56
95
|
return str.replace(/\[[0-9;]*m/g, '');
|
|
@@ -79,16 +118,17 @@ function printTable(headers, rows) {
|
|
|
79
118
|
|
|
80
119
|
const publishProviderCommand = new Command('publish-provider')
|
|
81
120
|
.description('Publish a provider OpenAPI spec to the BDCT registry')
|
|
82
|
-
.
|
|
83
|
-
.
|
|
84
|
-
.
|
|
121
|
+
.option('--spec <path>', 'Path to provider spec file (YAML or JSON)')
|
|
122
|
+
.option('--provider <name>', 'Provider service name')
|
|
123
|
+
.option('--version <ver>', 'Provider version tag')
|
|
124
|
+
.option('--org <key>', 'Organization key')
|
|
85
125
|
.option('--env <environment>', 'Environment label (e.g. staging, production)')
|
|
86
|
-
.option('--org <key>', 'Organization key')
|
|
87
126
|
.option('--branch <branch>', 'Git branch name')
|
|
88
|
-
.option('--json',
|
|
89
|
-
.option('--server <url>',
|
|
127
|
+
.option('--json', 'Output raw JSON')
|
|
128
|
+
.option('--server <url>', 'SpecShield server URL')
|
|
90
129
|
.option('--api-token <token>', 'API token (overrides env / stored config)')
|
|
91
130
|
.action(async (opts) => {
|
|
131
|
+
withProjectDefaults(opts, 'publish-provider');
|
|
92
132
|
const token = await resolveApiToken(opts);
|
|
93
133
|
requireToken(token);
|
|
94
134
|
|
|
@@ -110,12 +150,12 @@ const publishProviderCommand = new Command('publish-provider')
|
|
|
110
150
|
|
|
111
151
|
try {
|
|
112
152
|
const result = await publishProviderSpec(opts.server, token, {
|
|
113
|
-
|
|
114
|
-
|
|
153
|
+
orgKey: opts.org,
|
|
154
|
+
providerName: opts.provider,
|
|
155
|
+
version: opts.version,
|
|
115
156
|
specContent,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
branch: opts.branch || null,
|
|
157
|
+
environment: opts.env || null,
|
|
158
|
+
branch: opts.branch || null,
|
|
119
159
|
});
|
|
120
160
|
if (spinner) spinner.stop();
|
|
121
161
|
|
|
@@ -131,7 +171,11 @@ const publishProviderCommand = new Command('publish-provider')
|
|
|
131
171
|
process.stdout.write(` Provider : ${chalk.white(opts.provider)}\n`);
|
|
132
172
|
process.stdout.write(` Version : ${chalk.cyan(opts.version)}\n`);
|
|
133
173
|
if (opts.env) process.stdout.write(` Environment : ${opts.env}\n`);
|
|
134
|
-
|
|
174
|
+
const publishedAt = result.createdAt || result.publishedAt;
|
|
175
|
+
if (publishedAt) process.stdout.write(` Published At: ${fmtDate(publishedAt)}\n`);
|
|
176
|
+
if (typeof result.verificationsTriggered === 'number') {
|
|
177
|
+
process.stdout.write(` Verifications triggered: ${chalk.cyan(result.verificationsTriggered)}\n`);
|
|
178
|
+
}
|
|
135
179
|
process.stdout.write('\n');
|
|
136
180
|
} catch (err) {
|
|
137
181
|
if (spinner) spinner.fail('Publish failed');
|
|
@@ -144,16 +188,17 @@ const publishProviderCommand = new Command('publish-provider')
|
|
|
144
188
|
|
|
145
189
|
const publishConsumerCommand = new Command('publish-consumer')
|
|
146
190
|
.description('Publish a consumer contract to the BDCT registry')
|
|
147
|
-
.
|
|
148
|
-
.
|
|
149
|
-
.
|
|
150
|
-
.
|
|
151
|
-
.option('--org <key>',
|
|
152
|
-
.option('--format <fmt>',
|
|
153
|
-
.option('--json',
|
|
154
|
-
.option('--server <url>',
|
|
191
|
+
.option('--contract <path>', 'Path to consumer contract file (OpenAPI YAML/JSON or Pact JSON)')
|
|
192
|
+
.option('--consumer <name>', 'Consumer service name')
|
|
193
|
+
.option('--provider <name>', 'Provider service name')
|
|
194
|
+
.option('--version <ver>', 'Consumer version tag')
|
|
195
|
+
.option('--org <key>', 'Organization key')
|
|
196
|
+
.option('--format <fmt>', 'Contract format: OPENAPI | PACT', 'OPENAPI')
|
|
197
|
+
.option('--json', 'Output raw JSON')
|
|
198
|
+
.option('--server <url>', 'SpecShield server URL')
|
|
155
199
|
.option('--api-token <token>', 'API token (overrides env / stored config)')
|
|
156
200
|
.action(async (opts) => {
|
|
201
|
+
withProjectDefaults(opts, 'publish-consumer');
|
|
157
202
|
const token = await resolveApiToken(opts);
|
|
158
203
|
requireToken(token);
|
|
159
204
|
|
|
@@ -175,12 +220,12 @@ const publishConsumerCommand = new Command('publish-consumer')
|
|
|
175
220
|
|
|
176
221
|
try {
|
|
177
222
|
const result = await publishConsumerContract(opts.server, token, {
|
|
178
|
-
|
|
179
|
-
|
|
223
|
+
orgKey: opts.org,
|
|
224
|
+
consumerName: opts.consumer,
|
|
225
|
+
providerName: opts.provider,
|
|
180
226
|
version: opts.version,
|
|
181
227
|
contractContent,
|
|
182
|
-
|
|
183
|
-
format: opts.format || 'OPENAPI',
|
|
228
|
+
contractFormat: opts.format || 'OPENAPI',
|
|
184
229
|
});
|
|
185
230
|
if (spinner) spinner.stop();
|
|
186
231
|
|
|
@@ -197,7 +242,11 @@ const publishConsumerCommand = new Command('publish-consumer')
|
|
|
197
242
|
process.stdout.write(` Provider : ${chalk.white(opts.provider)}\n`);
|
|
198
243
|
process.stdout.write(` Version : ${chalk.cyan(opts.version)}\n`);
|
|
199
244
|
process.stdout.write(` Format : ${opts.format || 'OPENAPI'}\n`);
|
|
200
|
-
|
|
245
|
+
const consumerPublishedAt = result.createdAt || result.publishedAt;
|
|
246
|
+
if (consumerPublishedAt) process.stdout.write(` Published At: ${fmtDate(consumerPublishedAt)}\n`);
|
|
247
|
+
if (typeof result.verificationsTriggered === 'number') {
|
|
248
|
+
process.stdout.write(` Verifications triggered: ${chalk.cyan(result.verificationsTriggered)}\n`);
|
|
249
|
+
}
|
|
201
250
|
process.stdout.write('\n');
|
|
202
251
|
process.stdout.write(chalk.gray(` ➜ Run: specshield bdct verify --consumer ${opts.consumer} --provider ${opts.provider}\n`));
|
|
203
252
|
process.stdout.write('\n');
|
|
@@ -212,16 +261,17 @@ const publishConsumerCommand = new Command('publish-consumer')
|
|
|
212
261
|
|
|
213
262
|
const verifyCommand = new Command('verify')
|
|
214
263
|
.description('Verify consumer-provider contract compatibility')
|
|
215
|
-
.
|
|
216
|
-
.
|
|
217
|
-
.option('--consumer-version <ver>',
|
|
218
|
-
.option('--provider-version <ver>',
|
|
219
|
-
.option('--
|
|
220
|
-
.option('--
|
|
221
|
-
.option('--json',
|
|
222
|
-
.option('--server <url>',
|
|
223
|
-
.option('--api-token <token>',
|
|
264
|
+
.option('--consumer <name>', 'Consumer service name')
|
|
265
|
+
.option('--provider <name>', 'Provider service name')
|
|
266
|
+
.option('--consumer-version <ver>', 'Consumer version to verify')
|
|
267
|
+
.option('--provider-version <ver>', 'Provider version to verify against')
|
|
268
|
+
.option('--org <key>', 'Organization key')
|
|
269
|
+
.option('--env <environment>', 'Environment label')
|
|
270
|
+
.option('--json', 'Output raw JSON')
|
|
271
|
+
.option('--server <url>', 'SpecShield server URL')
|
|
272
|
+
.option('--api-token <token>', 'API token')
|
|
224
273
|
.action(async (opts) => {
|
|
274
|
+
withProjectDefaults(opts, 'verify');
|
|
225
275
|
const token = await resolveApiToken(opts);
|
|
226
276
|
requireToken(token);
|
|
227
277
|
|
|
@@ -229,23 +279,23 @@ const verifyCommand = new Command('verify')
|
|
|
229
279
|
|
|
230
280
|
try {
|
|
231
281
|
const result = await verify(opts.server, token, {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
consumerVersion: opts.consumerVersion
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
282
|
+
orgKey: opts.org,
|
|
283
|
+
consumerName: opts.consumer,
|
|
284
|
+
consumerVersion: opts.consumerVersion,
|
|
285
|
+
providerName: opts.provider,
|
|
286
|
+
providerVersion: opts.providerVersion,
|
|
287
|
+
environment: opts.env || null,
|
|
238
288
|
});
|
|
239
289
|
if (spinner) spinner.stop();
|
|
240
290
|
|
|
291
|
+
const status = String(result.status || result.result || '').toUpperCase();
|
|
292
|
+
const success = status === 'COMPATIBLE';
|
|
293
|
+
|
|
241
294
|
if (opts.json) {
|
|
242
295
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
243
|
-
|
|
296
|
+
process.exit(success ? 0 : 1);
|
|
244
297
|
}
|
|
245
298
|
|
|
246
|
-
const status = String(result.status || result.result || '').toUpperCase();
|
|
247
|
-
const success = status === 'COMPATIBLE';
|
|
248
|
-
|
|
249
299
|
process.stdout.write('\n');
|
|
250
300
|
if (success) {
|
|
251
301
|
process.stdout.write(chalk.green.bold(' ✔ COMPATIBLE') + '\n');
|
|
@@ -262,14 +312,26 @@ const verifyCommand = new Command('verify')
|
|
|
262
312
|
process.stdout.write(` Verified At : ${fmtDate(result.verifiedAt || result.completedAt)}\n`);
|
|
263
313
|
}
|
|
264
314
|
|
|
265
|
-
|
|
315
|
+
// Backend returns resultJson as a JSON string of [{endpoint, status, mismatches:[...]}].
|
|
316
|
+
// Older shapes also accepted: top-level issues / mismatches arrays.
|
|
317
|
+
const issues = flattenMismatches(result);
|
|
266
318
|
if (issues.length > 0) {
|
|
267
319
|
process.stdout.write('\n');
|
|
268
320
|
process.stdout.write(chalk.red.bold(' Issues') + '\n');
|
|
269
321
|
process.stdout.write(hr() + '\n');
|
|
270
322
|
for (const issue of issues) {
|
|
271
|
-
|
|
272
|
-
|
|
323
|
+
const sev = (issue.severity || 'ERROR').toUpperCase();
|
|
324
|
+
const marker = sev === 'WARNING' ? chalk.yellow('⚠') : chalk.red('●');
|
|
325
|
+
const type = issue.type || issue.mismatchType || 'MISMATCH';
|
|
326
|
+
const loc = issue.field || issue.path || issue.endpoint || '$';
|
|
327
|
+
process.stdout.write(` ${marker} ${chalk.bold(type)} at ${chalk.gray(loc)}\n`);
|
|
328
|
+
if (issue.consumerExpects && issue.providerProvides) {
|
|
329
|
+
process.stdout.write(` ${chalk.gray(`consumer: ${issue.consumerExpects}, provider: ${issue.providerProvides}`)}\n`);
|
|
330
|
+
} else if (issue.consumerExpects) {
|
|
331
|
+
process.stdout.write(` ${chalk.gray(`expected: ${issue.consumerExpects}`)}\n`);
|
|
332
|
+
} else if (issue.message) {
|
|
333
|
+
process.stdout.write(` ${chalk.gray(issue.message)}\n`);
|
|
334
|
+
}
|
|
273
335
|
}
|
|
274
336
|
process.stdout.write('\n');
|
|
275
337
|
}
|
|
@@ -287,14 +349,15 @@ const verifyCommand = new Command('verify')
|
|
|
287
349
|
|
|
288
350
|
const canIDeployCommand = new Command('can-i-deploy')
|
|
289
351
|
.description('Check if a service version is safe to deploy')
|
|
290
|
-
.
|
|
291
|
-
.
|
|
352
|
+
.option('--service <name>', 'Service name (consumer or provider)')
|
|
353
|
+
.option('--version <ver>', 'Service version to check')
|
|
354
|
+
.option('--org <key>', 'Organization key')
|
|
292
355
|
.option('--env <environment>', 'Target environment (e.g. qa, staging, production)')
|
|
293
|
-
.option('--
|
|
294
|
-
.option('--
|
|
295
|
-
.option('--server <url>', 'SpecShield server URL')
|
|
356
|
+
.option('--json', 'Output raw JSON')
|
|
357
|
+
.option('--server <url>', 'SpecShield server URL')
|
|
296
358
|
.option('--api-token <token>', 'API token')
|
|
297
359
|
.action(async (opts) => {
|
|
360
|
+
withProjectDefaults(opts, 'can-i-deploy');
|
|
298
361
|
const token = await resolveApiToken(opts);
|
|
299
362
|
requireToken(token);
|
|
300
363
|
|
|
@@ -302,21 +365,21 @@ const canIDeployCommand = new Command('can-i-deploy')
|
|
|
302
365
|
|
|
303
366
|
try {
|
|
304
367
|
const result = await canIDeploy(opts.server, token, {
|
|
368
|
+
org: opts.org,
|
|
305
369
|
service: opts.service,
|
|
306
370
|
version: opts.version,
|
|
307
371
|
env: opts.env || null,
|
|
308
|
-
org: opts.org || null,
|
|
309
372
|
});
|
|
310
373
|
if (spinner) spinner.stop();
|
|
311
374
|
|
|
375
|
+
const deployable = result.deployable ?? result.allowed ?? false;
|
|
376
|
+
const envLabel = opts.env ? ` in ${opts.env}` : '';
|
|
377
|
+
|
|
312
378
|
if (opts.json) {
|
|
313
379
|
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
314
|
-
|
|
380
|
+
process.exit(deployable ? 0 : 1);
|
|
315
381
|
}
|
|
316
382
|
|
|
317
|
-
const deployable = result.deployable ?? result.allowed ?? false;
|
|
318
|
-
const envLabel = opts.env ? ` in ${opts.env}` : '';
|
|
319
|
-
|
|
320
383
|
process.stdout.write('\n');
|
|
321
384
|
if (deployable) {
|
|
322
385
|
process.stdout.write(chalk.green.bold(' ✔ PASS') + chalk.white(`: ${opts.service} v${opts.version} is deployable${envLabel}\n`));
|
|
@@ -360,7 +423,7 @@ const canIDeployCommand = new Command('can-i-deploy')
|
|
|
360
423
|
|
|
361
424
|
const listCommand = new Command('list')
|
|
362
425
|
.description('List BDCT verification history')
|
|
363
|
-
.option('--org <key>', '
|
|
426
|
+
.option('--org <key>', 'Organization key')
|
|
364
427
|
.option('--consumer <name>', 'Filter by consumer service name')
|
|
365
428
|
.option('--provider <name>', 'Filter by provider service name')
|
|
366
429
|
.option('--env <environment>', 'Filter by environment')
|
|
@@ -370,6 +433,7 @@ const listCommand = new Command('list')
|
|
|
370
433
|
.option('--server <url>', 'SpecShield server URL')
|
|
371
434
|
.option('--api-token <token>', 'API token')
|
|
372
435
|
.action(async (opts) => {
|
|
436
|
+
withProjectDefaults(opts, 'list');
|
|
373
437
|
const token = await resolveApiToken(opts);
|
|
374
438
|
requireToken(token);
|
|
375
439
|
|
|
@@ -439,6 +503,7 @@ const matrixCommand = new Command('matrix')
|
|
|
439
503
|
.option('--server <url>', 'SpecShield server URL')
|
|
440
504
|
.option('--api-token <token>', 'API token')
|
|
441
505
|
.action(async (opts) => {
|
|
506
|
+
withProjectDefaults(opts, 'matrix');
|
|
442
507
|
const token = await resolveApiToken(opts);
|
|
443
508
|
requireToken(token);
|
|
444
509
|
|
|
@@ -456,7 +521,8 @@ const matrixCommand = new Command('matrix')
|
|
|
456
521
|
return;
|
|
457
522
|
}
|
|
458
523
|
|
|
459
|
-
//
|
|
524
|
+
// Backend matrix shape: { consumers: string[], providers: string[],
|
|
525
|
+
// cells: { "<consumer>__<provider>": "<STATUS>" } }
|
|
460
526
|
const consumers = matrix.consumers || [];
|
|
461
527
|
const providers = matrix.providers || [];
|
|
462
528
|
const cells = matrix.cells || {};
|
|
@@ -475,7 +541,7 @@ const matrixCommand = new Command('matrix')
|
|
|
475
541
|
const headers = ['Consumer \\ Provider', ...providers];
|
|
476
542
|
const rows = consumers.map(consumer => {
|
|
477
543
|
const providerCells = providers.map(provider => {
|
|
478
|
-
const status =
|
|
544
|
+
const status = cells[`${consumer}__${provider}`] || 'UNKNOWN';
|
|
479
545
|
return compatBadge(status);
|
|
480
546
|
});
|
|
481
547
|
return [chalk.white(consumer), ...providerCells];
|
|
@@ -497,15 +563,13 @@ const matrixCommand = new Command('matrix')
|
|
|
497
563
|
|
|
498
564
|
const listProvidersCommand = new Command('list-providers')
|
|
499
565
|
.description('List published provider specs')
|
|
500
|
-
.option('--org <key>',
|
|
566
|
+
.option('--org <key>', 'Organization key')
|
|
501
567
|
.option('--provider <name>', 'Filter by provider service name')
|
|
502
|
-
.option('--
|
|
503
|
-
.option('--
|
|
504
|
-
.option('--size <n>', 'Page size', '20')
|
|
505
|
-
.option('--json', 'Output raw JSON')
|
|
506
|
-
.option('--server <url>', 'SpecShield server URL')
|
|
568
|
+
.option('--json', 'Output raw JSON')
|
|
569
|
+
.option('--server <url>', 'SpecShield server URL')
|
|
507
570
|
.option('--api-token <token>', 'API token')
|
|
508
571
|
.action(async (opts) => {
|
|
572
|
+
withProjectDefaults(opts, 'list-providers');
|
|
509
573
|
const token = await resolveApiToken(opts);
|
|
510
574
|
requireToken(token);
|
|
511
575
|
|
|
@@ -515,9 +579,6 @@ const listProvidersCommand = new Command('list-providers')
|
|
|
515
579
|
const page = await listProviderSpecs(opts.server, token, {
|
|
516
580
|
org: opts.org,
|
|
517
581
|
provider: opts.provider,
|
|
518
|
-
env: opts.env,
|
|
519
|
-
page: parseInt(opts.page, 10) || 0,
|
|
520
|
-
size: parseInt(opts.size, 10) || 20,
|
|
521
582
|
});
|
|
522
583
|
if (spinner) spinner.stop();
|
|
523
584
|
|
|
@@ -543,11 +604,11 @@ const listProvidersCommand = new Command('list-providers')
|
|
|
543
604
|
['ID', 'Provider', 'Version', 'Environment', 'Branch', 'Published At'],
|
|
544
605
|
items.map(s => [
|
|
545
606
|
chalk.cyan(String(s.id ?? '—')),
|
|
546
|
-
s.
|
|
607
|
+
s.providerName || s.provider || '—',
|
|
547
608
|
chalk.cyan(s.version || '—'),
|
|
548
|
-
s.
|
|
609
|
+
s.environment || s.env || '—',
|
|
549
610
|
chalk.gray(s.branch || '—'),
|
|
550
|
-
fmtDate(s.publishedAt),
|
|
611
|
+
fmtDate(s.createdAt || s.publishedAt),
|
|
551
612
|
])
|
|
552
613
|
);
|
|
553
614
|
|
|
@@ -566,15 +627,14 @@ const listProvidersCommand = new Command('list-providers')
|
|
|
566
627
|
|
|
567
628
|
const listConsumersCommand = new Command('list-consumers')
|
|
568
629
|
.description('List published consumer contracts')
|
|
569
|
-
.option('--org <key>',
|
|
630
|
+
.option('--org <key>', 'Organization key')
|
|
570
631
|
.option('--consumer <name>', 'Filter by consumer service name')
|
|
571
632
|
.option('--provider <name>', 'Filter by provider service name')
|
|
572
|
-
.option('--
|
|
573
|
-
.option('--
|
|
574
|
-
.option('--json', 'Output raw JSON')
|
|
575
|
-
.option('--server <url>', 'SpecShield server URL')
|
|
633
|
+
.option('--json', 'Output raw JSON')
|
|
634
|
+
.option('--server <url>', 'SpecShield server URL')
|
|
576
635
|
.option('--api-token <token>', 'API token')
|
|
577
636
|
.action(async (opts) => {
|
|
637
|
+
withProjectDefaults(opts, 'list-consumers');
|
|
578
638
|
const token = await resolveApiToken(opts);
|
|
579
639
|
requireToken(token);
|
|
580
640
|
|
|
@@ -585,8 +645,6 @@ const listConsumersCommand = new Command('list-consumers')
|
|
|
585
645
|
org: opts.org,
|
|
586
646
|
consumer: opts.consumer,
|
|
587
647
|
provider: opts.provider,
|
|
588
|
-
page: parseInt(opts.page, 10) || 0,
|
|
589
|
-
size: parseInt(opts.size, 10) || 20,
|
|
590
648
|
});
|
|
591
649
|
if (spinner) spinner.stop();
|
|
592
650
|
|
|
@@ -612,11 +670,11 @@ const listConsumersCommand = new Command('list-consumers')
|
|
|
612
670
|
['ID', 'Consumer', 'Provider', 'Version', 'Format', 'Published At'],
|
|
613
671
|
items.map(c => [
|
|
614
672
|
chalk.cyan(String(c.id ?? '—')),
|
|
615
|
-
c.
|
|
616
|
-
c.
|
|
673
|
+
c.consumerName || c.consumer || '—',
|
|
674
|
+
c.providerName || c.provider || '—',
|
|
617
675
|
chalk.cyan(c.version || '—'),
|
|
618
|
-
c.format || '—',
|
|
619
|
-
fmtDate(c.publishedAt),
|
|
676
|
+
c.contractFormat || c.format || '—',
|
|
677
|
+
fmtDate(c.createdAt || c.publishedAt),
|
|
620
678
|
])
|
|
621
679
|
);
|
|
622
680
|
|