diff --git a/CHANGELOG.md b/CHANGELOG.md index a9c323ebf..e0d20e6ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ - 7580cee: Removal of postman -> asyncapi conversion functionality +### Patch Changes + +- cb8c08a: fix(generate): preserve underlying error when validating registry URL + - Preserve the original error message and attach it as `cause` when registry URL validation fails to improve diagnosability for DNS/network/SSL/proxy failures. Adds unit tests covering the behavior. + ## ⚠ BREAKING CHANGES Remove postman conversion utilities due to unmaintained dependencies and compatibility issues. diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md new file mode 100644 index 000000000..0be4dfaa6 --- /dev/null +++ b/PR_DESCRIPTION.md @@ -0,0 +1,28 @@ +Title: fix: preserve underlying error when validating registry URL + +Summary + +The `registryValidation` function in `src/utils/generate/registry.ts` previously caught all errors during registry URL validation and rethrew a generic Error that discarded the original error details. This made troubleshooting impossible for users because the underlying cause (DNS resolution, network timeout, TLS/SSL, proxy issues, etc.) was lost. + +What I changed + +- Capture the original error in the catch block and include its message in the thrown error text (`Caused by: ...`). +- Attach the original error to the thrown Error's `cause` property when possible so callers can inspect it programmatically. + +Why + +Preserving the original error helps users and maintainers diagnose registry connection issues more effectively. + +Testing + +- Ran `npm ci` and executed the CLI test suite (`npm run cli:test`). The repository has many test suites; I ran the CLI tests locally to validate behavior. The change is small and focused; adding a dedicated unit test for `registryValidation` is straightforward and I can add it if you'd like. + +Notes + +- I avoided changing the public API; callers still receive an Error but with helpful details and `error.cause` populated when available. + +Suggested PR body for GitHub + +This PR fixes #2013 — preserve the underlying error when validating `--registry-url` so users can see the root cause of failures (DNS, network, SSL, proxy, etc.). + +Would you like me to create the GitHub PR automatically using the GitHub CLI (`gh`) or open a draft PR URL for you? \ No newline at end of file diff --git a/src/utils/generate/registry.ts b/src/utils/generate/registry.ts index 16fdda2e5..367b21ebc 100644 --- a/src/utils/generate/registry.ts +++ b/src/utils/generate/registry.ts @@ -13,7 +13,11 @@ export async function registryValidation(registryUrl?: string, registryAuth?: st if (response.status === 401 && !registryAuth && !registryToken) { throw new Error('You Need to pass either registryAuth in username:password encoded in Base64 or need to pass registryToken'); } - } catch { - throw new Error(`Can't fetch registryURL: ${registryUrl}`); + } catch (err) { + const causeMsg = err instanceof Error ? err.message : String(err); + // Use a typed options object to avoid `any`/casts and remain Sonar-friendly. + type LocalErrorOptions = { cause?: unknown }; + const opts: LocalErrorOptions = { cause: err instanceof Error ? err : undefined }; + throw new Error(`Can't fetch registryURL: ${registryUrl}\nCaused by: ${causeMsg}`, opts as ErrorOptions); } } diff --git a/test/unit/registry.test.ts b/test/unit/registry.test.ts new file mode 100644 index 000000000..4580afbe1 --- /dev/null +++ b/test/unit/registry.test.ts @@ -0,0 +1,43 @@ +import { expect } from 'chai'; +import { registryValidation } from '../../src/utils/generate/registry'; + +describe('registryValidation', () => { + const originalFetch = (globalThis as any).fetch; + + afterEach(() => { + (globalThis as any).fetch = originalFetch; + }); + + it('returns undefined when no url provided', async () => { + const result = await registryValidation(undefined); + expect(result).to.equal(undefined); + }).timeout(5000); + + it('wraps fetch errors and preserves cause', async () => { + const networkError = new Error('getaddrinfo ENOTFOUND my-registry.example.com'); + (globalThis as any).fetch = () => { throw networkError; }; + + try { + await registryValidation('https://my-registry.example.com'); + throw new Error('Expected registryValidation to throw'); + } catch (err: any) { + expect(String(err.message)).to.contain("Can't fetch registryURL: https://my-registry.example.com"); + expect(String(err.message)).to.contain('Caused by: getaddrinfo ENOTFOUND my-registry.example.com'); + expect((err as any).cause).to.equal(networkError); + } + }).timeout(5000); + + it('wraps 401 auth response and preserves cause message', async () => { + (globalThis as any).fetch = async () => ({ status: 401 }); + + try { + await registryValidation('https://my-registry.example.com'); + throw new Error('Expected registryValidation to throw'); + } catch (err: any) { + expect(String(err.message)).to.contain("Can't fetch registryURL: https://my-registry.example.com"); + expect(String(err.message)).to.contain('Caused by: You Need to pass either registryAuth'); + expect((err as any).cause).to.be.instanceOf(Error); + expect((err as any).cause.message).to.contain('You Need to pass either registryAuth'); + } + }).timeout(5000); +});