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/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
> **OpenAPI Diff · API Breaking Change Detection · Swagger Diff ·
|
|
10
|
+
> **OpenAPI Diff · API Breaking Change Detection · Swagger Diff · Bi-Directional Contract Testing · Pact Alternative · can-i-deploy · GitHub PR Checks · CI/CD**
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -57,7 +57,7 @@ No runtime surprise. No production incident. No 3am page.
|
|
|
57
57
|
|
|
58
58
|
[](https://www.youtube.com/watch?v=mugDyQQGqZw)
|
|
59
59
|
|
|
60
|
-
> Watch: Catching a breaking API change before it hits production — compare specs, publish
|
|
60
|
+
> Watch: Catching a breaking API change before it hits production — compare specs, publish provider/consumer specs via BDCT, and gate deployments with `can-i-deploy`.
|
|
61
61
|
|
|
62
62
|
**CLI preview:**
|
|
63
63
|
|
|
@@ -117,6 +117,26 @@ specshield compare base.yaml target.yaml --fail-on-breaking
|
|
|
117
117
|
|
|
118
118
|
That's it. Works with any OpenAPI 3.x YAML or JSON spec.
|
|
119
119
|
|
|
120
|
+
**For BDCT**, run the wizard once and never re-type the same flags again:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
specshield init
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
It autodetects your OpenAPI spec, your service name (from `package.json` /
|
|
127
|
+
`pyproject.toml` / `pom.xml` / `go.mod`), your git branch, and your
|
|
128
|
+
default environment, asks you a few questions, and writes a
|
|
129
|
+
**`.specshield.yml`** at the project root. Every subsequent
|
|
130
|
+
`specshield bdct ...` invocation reads this file, so your CI commands
|
|
131
|
+
collapse to:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
specshield bdct publish-provider --version $GITHUB_SHA
|
|
135
|
+
specshield bdct can-i-deploy --version $GITHUB_SHA
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
See [§ specshield init](#specshield-init--first-run-setup-wizard) below.
|
|
139
|
+
|
|
120
140
|
---
|
|
121
141
|
|
|
122
142
|
## 🚀 Create Your Free Account
|
|
@@ -141,8 +161,6 @@ That's it. Works with any OpenAPI 3.x YAML or JSON spec.
|
|
|
141
161
|
| JSON / human output | ✅ | ✅ | ✅ |
|
|
142
162
|
| Fail CI on breaking change | ✅ | ✅ | ✅ |
|
|
143
163
|
| **Compare history & dashboard** | ❌ | ✅ | ✅ |
|
|
144
|
-
| **CDCT contract testing registry** | ❌ | ✅ | ✅ |
|
|
145
|
-
| **CDCT can-i-deploy gating** | ❌ | ✅ | ✅ |
|
|
146
164
|
| **GitHub App PR checks** | ❌ | ✅ | ✅ |
|
|
147
165
|
| **BDCT bi-directional contracts** | ❌ | ❌ | ✅ |
|
|
148
166
|
| **BDCT can-i-deploy gating** | ❌ | ❌ | ✅ |
|
|
@@ -160,8 +178,9 @@ That's it. Works with any OpenAPI 3.x YAML or JSON spec.
|
|
|
160
178
|
> Without SpecShield, this would have reached staging, broken checkout for every user,
|
|
161
179
|
> and triggered an incident at 2am.
|
|
162
180
|
>
|
|
163
|
-
> With SpecShield, the provider's CI
|
|
164
|
-
>
|
|
181
|
+
> With SpecShield, the provider's CI published the new spec via
|
|
182
|
+
> `specshield bdct publish-provider`. The compatibility engine ran against
|
|
183
|
+
> `checkout-ui`'s published contract and the mismatch was caught immediately:
|
|
165
184
|
>
|
|
166
185
|
> ```
|
|
167
186
|
> ● MISSING_FIELD at $.status
|
|
@@ -195,7 +214,6 @@ Track API drift over time across your entire platform. Know what changed, when,
|
|
|
195
214
|
|---|---|---|---|
|
|
196
215
|
| OpenAPI / Swagger native | ✅ | ❌ (code-level) | ✅ |
|
|
197
216
|
| No broker required | ✅ | ❌ (needs Pact Broker) | ✅ |
|
|
198
|
-
| Consumer-driven contract testing | ✅ | ✅ | ❌ |
|
|
199
217
|
| **Bi-directional contract testing** | ✅ | ✅ (Pactflow paid) | ❌ |
|
|
200
218
|
| Breaking change detection | ✅ | ❌ | ✅ |
|
|
201
219
|
| can-i-deploy gating | ✅ | ✅ (via broker) | ❌ |
|
|
@@ -212,7 +230,7 @@ Track API drift over time across your entire platform. Know what changed, when,
|
|
|
212
230
|
|
|
213
231
|
| Plan | Price | What's included |
|
|
214
232
|
|---|---|---|
|
|
215
|
-
| **Free** | $0 forever | Local compare (unlimited) · Compare history & dashboard ·
|
|
233
|
+
| **Free** | $0 forever | Local compare (unlimited) · Compare history & dashboard · GitHub App PR checks |
|
|
216
234
|
| **Pro** | Coming soon | Everything in Free + BDCT bi-directional contracts · BDCT can-i-deploy & matrix · Team collaboration · Advanced reporting · Priority support |
|
|
217
235
|
|
|
218
236
|
No credit card ever required for the free plan.
|
|
@@ -351,116 +369,80 @@ github:
|
|
|
351
369
|
|
|
352
370
|
---
|
|
353
371
|
|
|
354
|
-
##
|
|
355
|
-
|
|
356
|
-
Consumer-driven contract testing for microservices — without a broker.
|
|
372
|
+
## `specshield init` — first-run setup wizard
|
|
357
373
|
|
|
358
|
-
|
|
374
|
+
Run once at the root of your project. The wizard:
|
|
359
375
|
|
|
360
|
-
1.
|
|
361
|
-
2.
|
|
362
|
-
3.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
{
|
|
368
|
-
"consumer": { "name": "checkout-ui", "version": "2.0.0" },
|
|
369
|
-
"provider": { "name": "payment-service" },
|
|
370
|
-
"orgKey": "acme-store",
|
|
371
|
-
"contractName": "create-payment",
|
|
372
|
-
"contractType": "HTTP",
|
|
373
|
-
"interactions": [
|
|
374
|
-
{
|
|
375
|
-
"description": "checkout-ui creates a payment",
|
|
376
|
-
"request": {
|
|
377
|
-
"method": "POST",
|
|
378
|
-
"path": "/payments",
|
|
379
|
-
"headers": { "Content-Type": "application/json" },
|
|
380
|
-
"body": { "orderId": "ORD-123", "amount": 1299, "currency": "INR" }
|
|
381
|
-
},
|
|
382
|
-
"expectedResponse": {
|
|
383
|
-
"status": 201,
|
|
384
|
-
"headers": { "Content-Type": "application/json" },
|
|
385
|
-
"body": { "paymentId": "PAY-123", "status": "CREATED" }
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
]
|
|
389
|
-
}
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
### Publish a Contract
|
|
376
|
+
1. Detects your OpenAPI spec (looks under `api/`, `spec/`, `docs/`, repo root).
|
|
377
|
+
2. Detects your service name (`package.json`, `pyproject.toml`, `pom.xml`, `go.mod`, `Cargo.toml`, or directory name).
|
|
378
|
+
3. Detects your git branch and suggests `production` for `main`/`master`, `staging` otherwise.
|
|
379
|
+
4. Asks whether this project is a provider, a consumer, or both.
|
|
380
|
+
5. Asks for your org key (autocompletes from your account if you're already signed in).
|
|
381
|
+
6. Validates / stores your API key.
|
|
382
|
+
7. Writes **`.specshield.yml`** and, optionally, a starter **`.github/workflows/specshield-bdct.yml`** that uses [`specshield26/bdct-action@v1`](https://github.com/marketplace/actions/specshield-bdct).
|
|
393
383
|
|
|
394
384
|
```bash
|
|
395
|
-
specshield
|
|
396
|
-
--file ./contracts/create-payment.json \
|
|
397
|
-
--org acme-store \
|
|
398
|
-
--consumer-version 2.0.0 \
|
|
399
|
-
--tag main
|
|
385
|
+
specshield init
|
|
400
386
|
```
|
|
401
387
|
|
|
402
|
-
###
|
|
388
|
+
### Example `.specshield.yml`
|
|
403
389
|
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
--contract-id 42 \
|
|
407
|
-
--base-url http://localhost:8080 \
|
|
408
|
-
--provider-version v2.1.0 \
|
|
409
|
-
--env staging
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
Pass output:
|
|
413
|
-
```
|
|
414
|
-
✔ Verification PASSED (1/1 interactions)
|
|
415
|
-
```
|
|
390
|
+
```yaml
|
|
391
|
+
schemaVersion: 1
|
|
416
392
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
✖ Verification FAILED (0/1 interactions passed, 1 failed)
|
|
393
|
+
failOnBreaking: true
|
|
394
|
+
severity: error
|
|
420
395
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
```
|
|
396
|
+
bdct:
|
|
397
|
+
org: acme-pay
|
|
398
|
+
environment: staging
|
|
425
399
|
|
|
426
|
-
|
|
400
|
+
provider:
|
|
401
|
+
name: payment-service
|
|
402
|
+
spec: api/openapi.yaml
|
|
427
403
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
404
|
+
# consumer (optional — present when --kind=consumer or --kind=both):
|
|
405
|
+
# consumer:
|
|
406
|
+
# name: checkout-ui
|
|
407
|
+
# provider: payment-service
|
|
408
|
+
# contract: contracts/payment-service.yaml
|
|
409
|
+
# format: OPENAPI
|
|
434
410
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
✖ FAIL: payment-service v2.1.0 is NOT deployable in staging
|
|
411
|
+
github:
|
|
412
|
+
specPath: api/openapi.yaml
|
|
413
|
+
failOnBreaking: true
|
|
414
|
+
commentOnPr: true
|
|
440
415
|
```
|
|
441
416
|
|
|
442
|
-
|
|
417
|
+
CLI flags **always** override this file. Paths in the file are resolved
|
|
418
|
+
relative to the file's own directory, so you can run `specshield bdct ...`
|
|
419
|
+
from any subdirectory of your project.
|
|
443
420
|
|
|
444
|
-
###
|
|
421
|
+
### Non-interactive (scriptable) mode
|
|
445
422
|
|
|
446
423
|
```bash
|
|
447
|
-
specshield
|
|
448
|
-
|
|
449
|
-
|
|
424
|
+
specshield init --no-interactive \
|
|
425
|
+
--kind provider \
|
|
426
|
+
--org acme-pay \
|
|
427
|
+
--provider payment-service \
|
|
428
|
+
--spec api/openapi.yaml \
|
|
429
|
+
--env staging \
|
|
430
|
+
--write-workflow
|
|
450
431
|
```
|
|
451
432
|
|
|
452
|
-
###
|
|
433
|
+
### Other useful flags
|
|
453
434
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
435
|
+
| Flag | Purpose |
|
|
436
|
+
| --- | --- |
|
|
437
|
+
| `--print` | Detect everything, print the proposed YAML, write nothing. Good for `--dry-run` review in CI. |
|
|
438
|
+
| `--force` | Skip the overwrite-confirmation if `.specshield.yml` already exists. |
|
|
439
|
+
| `--server <url>` | Use a non-default SpecShield endpoint (self-hosted / staging). |
|
|
440
|
+
| `--write-workflow` | Also write a starter GitHub Actions workflow under `.github/workflows/`. |
|
|
460
441
|
|
|
461
|
-
|
|
462
|
-
specshield
|
|
463
|
-
|
|
442
|
+
> **What is _not_ written into `.specshield.yml`:** your API key. It is
|
|
443
|
+
> stored in `~/.specshield/config.json` (set by `specshield login`) or
|
|
444
|
+
> read from `SPECSHIELD_API_KEY` in CI. The project file is meant to be
|
|
445
|
+
> committed; never commit a secret into it.
|
|
464
446
|
|
|
465
447
|
---
|
|
466
448
|
|
|
@@ -468,20 +450,12 @@ specshield contracts can-i-deploy --provider payment-service --version v2.1.0
|
|
|
468
450
|
|
|
469
451
|
**Spec-to-spec contract testing — no running services required.**
|
|
470
452
|
|
|
471
|
-
|
|
453
|
+
Both sides publish their OpenAPI specs. SpecShield compares them and flags
|
|
454
|
+
mismatches immediately — ideal for teams that don't run services locally or in
|
|
455
|
+
CI. Pact JSON consumer contracts are also accepted and auto-converted.
|
|
472
456
|
|
|
473
457
|
> BDCT requires a **Pro plan**. [Upgrade at specshield.io/upgrade](https://specshield.io/upgrade)
|
|
474
458
|
|
|
475
|
-
**CDCT vs BDCT:**
|
|
476
|
-
|
|
477
|
-
| | CDCT | BDCT |
|
|
478
|
-
|---|---|---|
|
|
479
|
-
| How verification works | Replay requests against a live server | Compare OpenAPI specs statically |
|
|
480
|
-
| Provider needs to run | Yes | No |
|
|
481
|
-
| Feedback speed | After deploy to test env | Immediately on spec publish |
|
|
482
|
-
| Pact JSON contracts | Supported | Supported (auto-converted) |
|
|
483
|
-
| Best for | Runtime correctness | Early spec-level safety |
|
|
484
|
-
|
|
485
459
|
### How BDCT Works
|
|
486
460
|
|
|
487
461
|
1. Consumer team publishes an OpenAPI spec subset (the endpoints they use)
|
|
@@ -687,7 +661,7 @@ specshield bdct list-consumers --org acme-store --provider payment-service
|
|
|
687
661
|
### List Verifications
|
|
688
662
|
|
|
689
663
|
```bash
|
|
690
|
-
specshield bdct list
|
|
664
|
+
specshield bdct list \
|
|
691
665
|
--org acme-store \
|
|
692
666
|
--provider payment-service \
|
|
693
667
|
--env production \
|
|
@@ -780,73 +754,6 @@ jobs:
|
|
|
780
754
|
run: specshield compare /tmp/base.yaml api/openapi.yaml --fail-on-breaking
|
|
781
755
|
```
|
|
782
756
|
|
|
783
|
-
### On Push — Publish consumer contract (CDCT)
|
|
784
|
-
|
|
785
|
-
```yaml
|
|
786
|
-
name: Publish Contract
|
|
787
|
-
|
|
788
|
-
on:
|
|
789
|
-
push:
|
|
790
|
-
branches: [main]
|
|
791
|
-
paths:
|
|
792
|
-
- 'contracts/**'
|
|
793
|
-
|
|
794
|
-
jobs:
|
|
795
|
-
publish:
|
|
796
|
-
runs-on: ubuntu-latest
|
|
797
|
-
steps:
|
|
798
|
-
- uses: actions/checkout@v4
|
|
799
|
-
- uses: actions/setup-node@v4
|
|
800
|
-
with:
|
|
801
|
-
node-version: '20'
|
|
802
|
-
- run: npm install -g specshield
|
|
803
|
-
- name: Publish contract
|
|
804
|
-
env:
|
|
805
|
-
SPECSHIELD_API_KEY: ${{ secrets.SPECSHIELD_API_KEY }}
|
|
806
|
-
run: |
|
|
807
|
-
specshield contracts publish \
|
|
808
|
-
--file ./contracts/create-payment.json \
|
|
809
|
-
--org acme-store \
|
|
810
|
-
--tag ${{ github.ref_name }}
|
|
811
|
-
```
|
|
812
|
-
|
|
813
|
-
### On Push — Verify provider + gate deployment (CDCT)
|
|
814
|
-
|
|
815
|
-
```yaml
|
|
816
|
-
name: Contract Verification
|
|
817
|
-
|
|
818
|
-
on:
|
|
819
|
-
push:
|
|
820
|
-
branches: [main]
|
|
821
|
-
|
|
822
|
-
jobs:
|
|
823
|
-
verify:
|
|
824
|
-
runs-on: ubuntu-latest
|
|
825
|
-
steps:
|
|
826
|
-
- uses: actions/checkout@v4
|
|
827
|
-
- uses: actions/setup-node@v4
|
|
828
|
-
with:
|
|
829
|
-
node-version: '20'
|
|
830
|
-
- run: npm install -g specshield
|
|
831
|
-
- name: Verify contract
|
|
832
|
-
env:
|
|
833
|
-
SPECSHIELD_API_KEY: ${{ secrets.SPECSHIELD_API_KEY }}
|
|
834
|
-
run: |
|
|
835
|
-
specshield contracts verify \
|
|
836
|
-
--contract-id ${{ vars.CONTRACT_ID }} \
|
|
837
|
-
--base-url http://localhost:8080 \
|
|
838
|
-
--provider-version ${{ github.sha }} \
|
|
839
|
-
--env staging
|
|
840
|
-
- name: Can I deploy?
|
|
841
|
-
env:
|
|
842
|
-
SPECSHIELD_API_KEY: ${{ secrets.SPECSHIELD_API_KEY }}
|
|
843
|
-
run: |
|
|
844
|
-
specshield contracts can-i-deploy \
|
|
845
|
-
--provider payment-service \
|
|
846
|
-
--version ${{ github.sha }} \
|
|
847
|
-
--env staging
|
|
848
|
-
```
|
|
849
|
-
|
|
850
757
|
### On Push — Publish provider spec (BDCT)
|
|
851
758
|
|
|
852
759
|
```yaml
|
|
@@ -981,7 +888,7 @@ specshield bdct <subcommand> [options]
|
|
|
981
888
|
| `matrix` | View compatibility matrix across all pairs |
|
|
982
889
|
| `list-providers` | List published provider specs |
|
|
983
890
|
| `list-consumers` | List published consumer contracts |
|
|
984
|
-
| `list
|
|
891
|
+
| `list` | List verification history |
|
|
985
892
|
|
|
986
893
|
All `bdct` subcommands support `--json` for machine-readable output.
|
|
987
894
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specshield",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "CLI for OpenAPI breaking change detection
|
|
3
|
+
"version": "3.1.1",
|
|
4
|
+
"description": "CLI for OpenAPI breaking change detection and bi-directional contract verification — with can-i-deploy gating, GitHub PR checks, and a first-run setup wizard.",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"specshield": "./bin/specshield.js"
|
|
@@ -13,36 +13,36 @@
|
|
|
13
13
|
"lint": "eslint src tests --ext .js"
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
],
|
|
16
|
+
"openapi",
|
|
17
|
+
"swagger",
|
|
18
|
+
"api-diff",
|
|
19
|
+
"openapi-diff",
|
|
20
|
+
"swagger-diff",
|
|
21
|
+
"breaking-change-detection",
|
|
22
|
+
"api-breaking-changes",
|
|
23
|
+
"api-compatibility",
|
|
24
|
+
"contract-testing",
|
|
25
|
+
"consumer-driven-contract",
|
|
26
|
+
"api-contract",
|
|
27
|
+
"contract-verification",
|
|
28
|
+
"can-i-deploy",
|
|
29
|
+
"ci-cd",
|
|
30
|
+
"github-actions",
|
|
31
|
+
"cli",
|
|
32
|
+
"developer-tools",
|
|
33
|
+
"openapi-compare",
|
|
34
|
+
"swagger-compare",
|
|
35
|
+
"pact-alternative",
|
|
36
|
+
"api-contract-testing",
|
|
37
|
+
"microservices",
|
|
38
|
+
"api-quality",
|
|
39
|
+
"api-governance",
|
|
40
|
+
"bi-directional-contract-testing",
|
|
41
|
+
"bdct",
|
|
42
|
+
"pact-json",
|
|
43
|
+
"github-app",
|
|
44
|
+
"api-drift"
|
|
45
|
+
],
|
|
46
46
|
"license": "MIT",
|
|
47
47
|
"files": [
|
|
48
48
|
"bin",
|
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
"commander": "^12.0.0",
|
|
57
57
|
"fs-extra": "^11.2.0",
|
|
58
58
|
"js-yaml": "^4.1.0",
|
|
59
|
-
"ora": "^5.4.1"
|
|
59
|
+
"ora": "^5.4.1",
|
|
60
|
+
"prompts": "^2.4.2"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"jest": "^29.7.0"
|
package/src/api/bdctClient.js
CHANGED
|
@@ -22,7 +22,10 @@ function apiError(err) {
|
|
|
22
22
|
const data = err.response.data;
|
|
23
23
|
const msg = (data && (data.message || data.error || data.title))
|
|
24
24
|
|| `HTTP ${err.response.status}`;
|
|
25
|
-
|
|
25
|
+
const e = new Error(`API error (${err.response.status}): ${msg}`);
|
|
26
|
+
e.status = err.response.status;
|
|
27
|
+
e.responseData = data;
|
|
28
|
+
return e;
|
|
26
29
|
}
|
|
27
30
|
if (err.request) return new Error(`No response from server: ${err.message}`);
|
|
28
31
|
return new Error(`Request failed: ${err.message}`);
|
|
@@ -51,11 +54,10 @@ async function verify(server, apiToken, payload) {
|
|
|
51
54
|
|
|
52
55
|
async function listVerifications(server, apiToken, { org, consumer, provider, env, page = 0, size = 20 } = {}) {
|
|
53
56
|
try {
|
|
54
|
-
const params = { page, size };
|
|
55
|
-
if (
|
|
56
|
-
if (
|
|
57
|
-
if (
|
|
58
|
-
if (env) params.env = env;
|
|
57
|
+
const params = { orgKey: org, page, size };
|
|
58
|
+
if (consumer) params.consumerName = consumer;
|
|
59
|
+
if (provider) params.providerName = provider;
|
|
60
|
+
if (env) params.environment = env;
|
|
59
61
|
const res = await buildClient(server, apiToken).get('/api/bdct/verifications', { params });
|
|
60
62
|
return res.data;
|
|
61
63
|
} catch (err) { throw apiError(err); }
|
|
@@ -63,41 +65,44 @@ async function listVerifications(server, apiToken, { org, consumer, provider, en
|
|
|
63
65
|
|
|
64
66
|
async function canIDeploy(server, apiToken, { org, service, version: ver, env } = {}) {
|
|
65
67
|
try {
|
|
66
|
-
const params = { service, version: ver };
|
|
67
|
-
if (
|
|
68
|
-
if (env) params.env = env;
|
|
68
|
+
const params = { orgKey: org, service, version: ver };
|
|
69
|
+
if (env) params.environment = env;
|
|
69
70
|
const res = await buildClient(server, apiToken).get('/api/bdct/can-i-deploy', { params });
|
|
70
71
|
return res.data;
|
|
71
|
-
} catch (err) {
|
|
72
|
+
} catch (err) {
|
|
73
|
+
// 409 = NOT DEPLOYABLE — valid result, not an error (backend signals via status)
|
|
74
|
+
if (err.response && err.response.status === 409 && err.response.data
|
|
75
|
+
&& typeof err.response.data === 'object'
|
|
76
|
+
&& Object.prototype.hasOwnProperty.call(err.response.data, 'deployable')) {
|
|
77
|
+
return err.response.data;
|
|
78
|
+
}
|
|
79
|
+
throw apiError(err);
|
|
80
|
+
}
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
async function getMatrix(server, apiToken, { org, env } = {}) {
|
|
75
84
|
try {
|
|
76
|
-
const params = {};
|
|
77
|
-
if (
|
|
78
|
-
if (env) params.env = env;
|
|
85
|
+
const params = { orgKey: org };
|
|
86
|
+
if (env) params.environment = env;
|
|
79
87
|
const res = await buildClient(server, apiToken).get('/api/bdct/matrix', { params });
|
|
80
88
|
return res.data;
|
|
81
89
|
} catch (err) { throw apiError(err); }
|
|
82
90
|
}
|
|
83
91
|
|
|
84
|
-
async function listProviderSpecs(server, apiToken, { org, provider
|
|
92
|
+
async function listProviderSpecs(server, apiToken, { org, provider } = {}) {
|
|
85
93
|
try {
|
|
86
|
-
const params = {
|
|
87
|
-
if (
|
|
88
|
-
if (provider) params.provider = provider;
|
|
89
|
-
if (env) params.env = env;
|
|
94
|
+
const params = { orgKey: org };
|
|
95
|
+
if (provider) params.providerName = provider;
|
|
90
96
|
const res = await buildClient(server, apiToken).get('/api/bdct/provider-specs', { params });
|
|
91
97
|
return res.data;
|
|
92
98
|
} catch (err) { throw apiError(err); }
|
|
93
99
|
}
|
|
94
100
|
|
|
95
|
-
async function listConsumerContracts(server, apiToken, { org, consumer, provider
|
|
101
|
+
async function listConsumerContracts(server, apiToken, { org, consumer, provider } = {}) {
|
|
96
102
|
try {
|
|
97
|
-
const params = {
|
|
98
|
-
if (
|
|
99
|
-
if (
|
|
100
|
-
if (provider) params.provider = provider;
|
|
103
|
+
const params = { orgKey: org };
|
|
104
|
+
if (consumer) params.consumerName = consumer;
|
|
105
|
+
if (provider) params.providerName = provider;
|
|
101
106
|
const res = await buildClient(server, apiToken).get('/api/bdct/consumer-contracts', { params });
|
|
102
107
|
return res.data;
|
|
103
108
|
} catch (err) { throw apiError(err); }
|
package/src/cli.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
const { Command } = require('commander');
|
|
4
4
|
const { version } = require('../package.json');
|
|
5
5
|
const compareCommand = require('./commands/compare');
|
|
6
|
+
const initCommand = require('./commands/init');
|
|
6
7
|
const loginCommand = require('./commands/login');
|
|
7
8
|
const logoutCommand = require('./commands/logout');
|
|
8
|
-
const contractsCommand = require('./commands/contracts');
|
|
9
9
|
const bdctCommand = require('./commands/bdct');
|
|
10
10
|
|
|
11
11
|
const program = new Command();
|
|
@@ -13,12 +13,16 @@ const program = new Command();
|
|
|
13
13
|
program
|
|
14
14
|
.name('specshield')
|
|
15
15
|
.description('Compare OpenAPI specs and detect breaking changes')
|
|
16
|
-
.version(version)
|
|
16
|
+
.version(version)
|
|
17
|
+
// Without this, a subcommand option named `--version` (e.g. on `bdct can-i-deploy`)
|
|
18
|
+
// is greedily consumed by the root `--version` flag and the program prints the
|
|
19
|
+
// CLI version and exits before the subcommand runs.
|
|
20
|
+
.enablePositionalOptions();
|
|
17
21
|
|
|
18
22
|
program.addCommand(compareCommand);
|
|
23
|
+
program.addCommand(initCommand);
|
|
19
24
|
program.addCommand(loginCommand);
|
|
20
25
|
program.addCommand(logoutCommand);
|
|
21
|
-
program.addCommand(contractsCommand);
|
|
22
26
|
program.addCommand(bdctCommand);
|
|
23
27
|
|
|
24
28
|
program.parseAsync(process.argv).catch((err) => {
|