specshield 3.4.0 → 3.4.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/package.json +1 -1
- package/src/commands/bdct.js +9 -1
- package/src/core/projectConfig.js +50 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specshield",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.2",
|
|
4
4
|
"description": "Contract compatibility testing for APIs — catch breaking OpenAPI changes before they reach your consumers, with can-i-deploy deploy gating and GitHub PR checks. (a.k.a. bidirectional contract testing.)",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
package/src/commands/bdct.js
CHANGED
|
@@ -330,7 +330,15 @@ const verifyCommand = new Command('verify')
|
|
|
330
330
|
const sev = (issue.severity || 'ERROR').toUpperCase();
|
|
331
331
|
const marker = sev === 'WARNING' ? chalk.yellow('⚠') : chalk.red('●');
|
|
332
332
|
const type = issue.type || issue.mismatchType || 'MISMATCH';
|
|
333
|
-
|
|
333
|
+
// Show the endpoint AND the field. One field can be read from several
|
|
334
|
+
// endpoints, so field alone renders genuinely distinct findings as
|
|
335
|
+
// identical lines that look like a duplicate-output bug — e.g. a
|
|
336
|
+
// `receiptUrl` removal reported once for `POST /payments` and once for
|
|
337
|
+
// `GET /payments/{paymentId}` both printed as "at $.receiptUrl".
|
|
338
|
+
const field = issue.field || issue.path || null;
|
|
339
|
+
const endpoint = issue.endpoint || null;
|
|
340
|
+
const loc = field && endpoint ? `${endpoint} ${field}`
|
|
341
|
+
: (field || endpoint || '$');
|
|
334
342
|
process.stdout.write(` ${marker} ${chalk.bold(type)} at ${chalk.gray(loc)}\n`);
|
|
335
343
|
if (issue.consumerExpects && issue.providerProvides) {
|
|
336
344
|
process.stdout.write(` ${chalk.gray(`consumer: ${issue.consumerExpects}, provider: ${issue.providerProvides}`)}\n`);
|
|
@@ -92,9 +92,30 @@ const REQUIRED_FIELDS = {
|
|
|
92
92
|
'list-consumers': ['org'],
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Option names that CAN be defaulted from `.specshield.yml`.
|
|
97
|
+
*
|
|
98
|
+
* Deliberately NOT every required field: `version`, `consumerVersion` and
|
|
99
|
+
* `providerVersion` are per-invocation values (a git SHA, a release tag) with no
|
|
100
|
+
* sensible project-wide default, so `bdctDefaultFor` has no case for them.
|
|
101
|
+
*
|
|
102
|
+
* This set exists so the "missing required options" error can tell the truth about
|
|
103
|
+
* where each field can be set. Pointing a user at `.specshield.yml` for a field the
|
|
104
|
+
* config can never supply sends them to edit a file, re-run, hit the identical error,
|
|
105
|
+
* and conclude the config file is broken.
|
|
106
|
+
*
|
|
107
|
+
* MUST stay in sync with the cases handled in {@link bdctDefaultFor} — the
|
|
108
|
+
* "config-backed fields" test asserts both directions.
|
|
109
|
+
*/
|
|
110
|
+
const CONFIG_BACKED_FIELDS = new Set([
|
|
111
|
+
'org', 'server', 'env',
|
|
112
|
+
'provider', 'consumer', 'service',
|
|
113
|
+
'spec', 'contract', 'format', 'branch',
|
|
114
|
+
]);
|
|
115
|
+
|
|
95
116
|
/**
|
|
96
117
|
* Map a CLI option name to a path inside `.specshield.yml > bdct`.
|
|
97
|
-
* Returns
|
|
118
|
+
* Returns undefined when there is no defaulting rule for that option.
|
|
98
119
|
*/
|
|
99
120
|
function bdctDefaultFor(bdct, name, command) {
|
|
100
121
|
if (!bdct) return undefined;
|
|
@@ -181,14 +202,35 @@ function applyBdctDefaults(opts, command, { cwd = process.cwd() } = {}) {
|
|
|
181
202
|
const missing = required.filter(k => !opts[k]);
|
|
182
203
|
if (missing.length > 0) {
|
|
183
204
|
const flagFor = (k) => '--' + k.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
|
|
205
|
+
const list = (ks) => ks.map(flagFor).join(', ');
|
|
206
|
+
|
|
207
|
+
// Split the advice: only fields the config can actually supply get pointed at
|
|
208
|
+
// `.specshield.yml`. The rest are flag-only and must say so, or the user edits
|
|
209
|
+
// the config, re-runs, and hits the same error with no idea why.
|
|
210
|
+
const configurable = missing.filter(k => CONFIG_BACKED_FIELDS.has(k));
|
|
211
|
+
const flagOnly = missing.filter(k => !CONFIG_BACKED_FIELDS.has(k));
|
|
212
|
+
|
|
184
213
|
const msg = [
|
|
185
214
|
`Missing required ${missing.length === 1 ? 'option' : 'options'} for \`bdct ${command}\`: `
|
|
186
|
-
+ missing
|
|
215
|
+
+ list(missing),
|
|
187
216
|
];
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
217
|
+
|
|
218
|
+
if (flagOnly.length > 0) {
|
|
219
|
+
msg.push(
|
|
220
|
+
` ${list(flagOnly)} must be passed as CLI ${flagOnly.length === 1 ? 'flag' : 'flags'} — ` +
|
|
221
|
+
`${flagOnly.length === 1 ? 'it has' : 'they have'} no \`.specshield.yml\` equivalent ` +
|
|
222
|
+
'(version values change per run).');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (configurable.length > 0) {
|
|
226
|
+
msg.push(cfg._file
|
|
227
|
+
? ` ${list(configurable)} can be passed as CLI flags or set under \`bdct\` in ${cfg._file}.`
|
|
228
|
+
: ` ${list(configurable)} can be passed as CLI flags, or run \`specshield init\` to write a \`.specshield.yml\`.`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (command === 'verify' && flagOnly.length > 0) {
|
|
232
|
+
msg.push(' e.g. specshield bdct verify --consumer <NAME> --provider <NAME> '
|
|
233
|
+
+ '--consumer-version <VER> --provider-version <VER>');
|
|
192
234
|
}
|
|
193
235
|
const err = new Error(msg.join('\n'));
|
|
194
236
|
err.code = 'MISSING_REQUIRED_OPTIONS';
|
|
@@ -205,4 +247,6 @@ module.exports = {
|
|
|
205
247
|
clearCache,
|
|
206
248
|
applyBdctDefaults,
|
|
207
249
|
REQUIRED_FIELDS,
|
|
250
|
+
CONFIG_BACKED_FIELDS,
|
|
251
|
+
bdctDefaultFor,
|
|
208
252
|
};
|