Skip to content

feat(#4039): add boost-connector-utils shared package - #4144

Open
fullsend-ai-coder[bot] wants to merge 7 commits into
mainfrom
agent/4039-connector-utils
Open

feat(#4039): add boost-connector-utils shared package#4144
fullsend-ai-coder[bot] wants to merge 7 commits into
mainfrom
agent/4039-connector-utils

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Create @red-hat-developer-hub/backstage-plugin-boost-connector-utils providing shared utilities for AI catalog connector entity providers:

  • loadCaBundle(config, logger): resolve CA bundles from tls.caFile
    (file path) or tls.caSecret (resolved $env), with graceful failure
    (WARN for missing file, ERROR for invalid PEM, no expire check)
  • createHttpsAgent(caBundle?): factory for https.Agent with custom CA
  • createProviderWrapper(provider, logger, ctx?): wrap EntityProvider
    connect() in try/catch to prevent unhandled rejections crashing the
    catalog backend process
  • createSafeRefresh(fn, connectorId, logger, ctx?): wrap scheduled
    refresh callbacks with the same fault isolation pattern
  • classifyConnectorError(error): classify errors as retryable
    (ECONNREFUSED, ETIMEDOUT, HTTP 5xx/429) or non-retryable (HTTP
    4xx, TypeError, TLS cert errors)
  • isConnectorEnabled(config): check enabled boolean with default true
  • validateConnectorStartupConfig(config, opts): reject empty
    credentials and invalid/non-HTTPS endpoint URLs at startup

Includes 47 unit tests covering CA loading (file, env, missing, invalid PEM, chains, isolation), fault isolation (connect crash, refresh crash, no rethrow, multi-provider independence, structured error logging), config guards (enabled/disabled/default), and startup validation (HTTPS URLs, credential fields, descriptive errors).

Reference app-config YAML at workspaces/boost/examples/ demonstrates MCP Registry, RHOAI, and OCI Skill connector configuration with an air-gapped deployment variant.

Config root: ai-catalog.providers. per #4069 clarification. No Helm charts or Operator CRs (deferred per issue clarification).


Closes #4039

Post-script verification

  • Branch is not main/master (agent/4039-connector-utils)
  • Secret scan passed (gitleaks — fe4298b658c7868ca806e4edaf082e0417ba7cb2..HEAD)
  • Pre-commit hooks passed (authoritative run on runner)
  • Tests ran inside sandbox

@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Agent PR ready for human review label Aug 3, 2026
@rhdh-gh-app

rhdh-gh-app Bot commented Aug 3, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Missing Changesets

The following package(s) are changed by this PR but do not have a changeset:

  • @red-hat-developer-hub/backstage-plugin-boost-backend
  • @red-hat-developer-hub/backstage-plugin-boost-common
  • @red-hat-developer-hub/backstage-plugin-boost-node
  • @red-hat-developer-hub/backstage-plugin-boost-responses-api-toolkit
  • @red-hat-developer-hub/backstage-plugin-boost-toolscope
  • @red-hat-developer-hub/backstage-plugin-boost

See CONTRIBUTING.md for more information about how to add changesets.

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-boost-backend workspaces/boost/plugins/boost-backend none v0.1.3
@red-hat-developer-hub/backstage-plugin-boost-common workspaces/boost/plugins/boost-common none v0.1.3
@red-hat-developer-hub/backstage-plugin-boost-connector-utils workspaces/boost/plugins/boost-connector-utils minor v0.1.0
@red-hat-developer-hub/backstage-plugin-boost-node workspaces/boost/plugins/boost-node none v0.1.3
@red-hat-developer-hub/backstage-plugin-boost-responses-api-toolkit workspaces/boost/plugins/boost-responses-api-toolkit none v0.1.2
@red-hat-developer-hub/backstage-plugin-boost-toolscope workspaces/boost/plugins/boost-toolscope none v0.1.2
@red-hat-developer-hub/backstage-plugin-boost workspaces/boost/plugins/boost none v0.3.0

@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.61017% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.16%. Comparing base (3af0fb2) to head (583d71c).
⚠️ Report is 5 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4144      +/-   ##
==========================================
+ Coverage   58.11%   58.16%   +0.04%     
==========================================
  Files        2422     2425       +3     
  Lines       96490    96608     +118     
  Branches    26892    26928      +36     
==========================================
+ Hits        56079    56193     +114     
- Misses      40168    40172       +4     
  Partials      243      243              
Flag Coverage Δ *Carryforward flag
adoption-insights 84.55% <ø> (ø) Carriedforward from 6862842
ai-integrations 69.76% <ø> (ø) Carriedforward from 6862842
app-defaults 69.79% <ø> (ø) Carriedforward from 6862842
augment 46.67% <ø> (ø) Carriedforward from 6862842
boost 77.49% <96.61%> (+0.72%) ⬆️
bulk-import 72.56% <ø> (ø) Carriedforward from 6862842
cost-management 13.55% <ø> (ø) Carriedforward from 6862842
dcm 60.72% <ø> (ø) Carriedforward from 6862842
extensions 56.59% <ø> (ø) Carriedforward from 6862842
global-floating-action-button 71.18% <ø> (ø) Carriedforward from 6862842
global-header 66.50% <ø> (ø) Carriedforward from 6862842
homepage 47.50% <ø> (ø) Carriedforward from 6862842
install-dynamic-plugins 59.95% <ø> (ø) Carriedforward from 6862842
intelligent-assistant 74.61% <ø> (ø) Carriedforward from 6862842
konflux 91.98% <ø> (ø) Carriedforward from 6862842
lightspeed 69.02% <ø> (ø) Carriedforward from 6862842
mcp-integrations 83.40% <ø> (ø) Carriedforward from 6862842
orchestrator 66.87% <ø> (ø) Carriedforward from 6862842
quickstart 63.74% <ø> (ø) Carriedforward from 6862842
sandbox 79.56% <ø> (ø) Carriedforward from 6862842
scorecard 85.98% <ø> (ø) Carriedforward from 6862842
theme 88.52% <ø> (ø) Carriedforward from 6862842
translations 5.12% <ø> (ø) Carriedforward from 6862842
x2a 79.20% <ø> (ø) Carriedforward from 6862842

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3af0fb2...583d71c. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mareklibra

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 3, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:04 PM UTC · Completed 2:25 PM UTC
Commit: e96d319 · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [credential-validation] workspaces/boost/plugins/boost-connector-utils/src/config.ts:61validateCredentialField() cannot distinguish plaintext credentials from resolved $env references. Backstage resolves $env at config load time, so both appear as identical plain strings at runtime. The function only validates non-emptiness, yet the JSDoc (lines 42–60), inline comments, README, and example config YAML comment ("plaintext is rejected at startup") claim it detects plaintext vs $env.
    Remediation: Update JSDoc on validateCredentialField and validateConnectorStartupConfig to accurately state validation checks non-emptiness, not plaintext detection. Update the example config YAML comment to "empty credentials are rejected at startup".

  • [missing-doc] workspaces/boost/README.md:42 — The workspace README Plugins table does not include the new boost-connector-utils package. This table is the primary discovery point for developers.
    Remediation: Add a row to the Plugins table for boost-connector-utils.

Low

  • [edge-case] workspaces/boost/plugins/boost-connector-utils/src/ca-bundle.ts:78loadCaBundle uses readFileSync to read CA files during connector initialization within async init() handlers, blocking the event loop. Typically harmless for small PEM files at startup, but worth noting.

  • [scope-creep] workspaces/boost/plugins/boost/report.api.md — Unrelated reordering of ExtensionDataRef union members and translation keys in an API Extractor-generated report file, likely auto-generated side effects of updating pluginPackages arrays.

  • [import-style] workspaces/boost/plugins/boost-connector-utils/src/ca-bundle.ts:17 — Production code uses node:fs/node:https (with node: prefix) while the test file uses bare fs/os/path. Sibling boost-backend package also omits the prefix.
    Remediation: Align to bare imports without node: prefix to match workspace conventions.

  • [naming-convention] workspaces/boost/plugins/boost-connector-utils/src/fault-isolation.ts:115ConnectorEntityProvider interface is declared in fault-isolation.ts while other shared types (ConnectorErrorContext, ValidateConnectorStartupConfigOptions) live in types.ts.
    Remediation: Consider moving ConnectorEntityProvider to types.ts and re-exporting.

  • [API-surface] workspaces/boost/plugins/boost-connector-utils/report.api.md:26classifyConnectorError returns a bare boolean. If classification needs to grow beyond retryable/non-retryable, changing from boolean to a string union is a breaking change. Since this is v0.1.0, now is the time to decide.

  • [API-surface] workspaces/boost/plugins/boost-connector-utils/src/fault-isolation.ts:33ConnectorEntityProvider.connect() accepts connection: unknown, decoupled from Backstage's EntityProviderConnection. Structurally compatible at runtime, but consumers may need a type assertion when passing to catalog.addEntityProvider().
    Remediation: Document the structural compatibility and type assertion pattern in JSDoc.

  • [missing-test] workspaces/boost/plugins/boost-connector-utils/src/fault-isolation.test.ts:86classifyConnectorError it.each array covers ECONNREFUSED, ECONNRESET, ETIMEDOUT but omits EPIPE and EAI_AGAIN (both in RETRYABLE_CODES).
    Remediation: Add EPIPE and EAI_AGAIN to the it.each array.

  • [error-handling-idiom] workspaces/boost/plugins/boost-connector-utils/src/ca-bundle.ts:48 — Uses try { getConfig('tls') } catch { } for optional config detection. The Backstage idiom is getOptionalConfig('tls') which returns undefined without throwing.
    Remediation: Replace with const tlsConfig = connectorConfig.getOptionalConfig('tls'); if (!tlsConfig) return undefined;

  • [stale-doc] workspaces/boost/openspec/changes/connector-shared-infrastructure/tasks.md:24 — Task 2.2 references old type EntityProvider but implementation uses ConnectorEntityProvider.

  • [stale-doc] workspaces/boost/openspec/changes/connector-shared-infrastructure/tasks.md:50 — Task 4.1's export list is incomplete — missing classifyConnectorError, validateConnectorStartupConfig, ConnectorEntityProvider, FaultIsolationContext, ValidateConnectorStartupConfigOptions.

  • [API-surface] workspaces/boost/plugins/boost-connector-utils/src/types.ts:43ValidateConnectorStartupConfigOptions.endpointField is a single string. OCI connectors with per-registry config nodes would call the function separately per registry, so the current shape is correct for the intended usage pattern.

Previous run

Review

Findings

Medium

  • [stale function signature] workspaces/boost/openspec/changes/connector-shared-infrastructure/specs/ca-bundle-resolution/spec.md:16 — Multiple references to loadCaBundle() use the old single-argument signature but the implementation now requires two arguments: loadCaBundle(connectorConfig, logger). Lines 16, 31, 32, and 68 all show the old call pattern.
    Remediation: Update all loadCaBundle references to include the logger parameter.

  • [stale function signature] workspaces/boost/openspec/changes/mcp-registry-connector/design.md:162 — Code example shows const caBundle = loadCaBundle(connectorConfig); using the old single-argument signature.
    Remediation: Update to loadCaBundle(connectorConfig, logger).

  • [stale function signature] workspaces/boost/openspec/changes/mcp-registry-connector/tasks.md:38 — Task 2.3 references loadCaBundle(config.getConfig('ai-catalog.providers.mcpRegistry')) using the old single-argument call.
    Remediation: Update to include the logger parameter.

  • [stale function signature] workspaces/boost/openspec/changes/oci-skill-connector/tasks.md:57 — Task 4.6 references loadCaBundle(registryConfig) using the old single-argument call.
    Remediation: Update to loadCaBundle(registryConfig, logger).

  • [stale function signature] workspaces/boost/openspec/changes/rhoai-connector/tasks.md:33 — Task 2.5 references loadCaBundle(config.getConfig('ai-catalog.providers.rhoai.mcpCatalog')) using the old single-argument call.
    Remediation: Update to include the logger parameter.

  • [missing package in docs] workspaces/boost/AGENTS.md:73 — The package structure table in AGENTS.md does not include the new boost-connector-utils package added by this PR.
    Remediation: Add a row to the package structure table for boost-connector-utils.

Low

  • [dead code / unreachable branch] workspaces/boost/plugins/boost-connector-utils/src/config.ts:141 — The value?.trim() === '' check is unreachable in practice. Backstage's ConfigReader.getOptionalString() throws on empty string values first, so the catch block handles that case. This branch could only be reached for whitespace-only strings, but the error message says "is empty" rather than noting whitespace.

  • [misleading comment] workspaces/boost/plugins/boost-connector-utils/src/config.ts:91 — The JSDoc for validateCredentialField describes an algorithm for detecting plaintext vs $env credentials that the implementation does not perform. The function validates non-empty values only; Backstage resolves $env at config-load time, making runtime distinction impossible. See also: [credential validation limitation] at this location.

  • [path traversal] workspaces/boost/plugins/boost-connector-utils/src/ca-bundle.ts:62 — The caFile config value is passed directly to readFileSync without path sanitization. While admin-controlled and consistent with Backstage CA file handling patterns, the file is read into memory before PEM validation.

  • [design-divergence] workspaces/boost/openspec/changes/connector-shared-infrastructure/design.md — Decision 3 (fault isolation) does not reflect the implementation additions: classifyConnectorError(), the ctx parameter on both wrapper functions, createSafeRefresh signature with ctx, and the ConnectorEntityProvider local interface.

  • [import convention] workspaces/boost/plugins/boost-connector-utils/src/ca-bundle.test.ts:17 — Test file uses bare specifier imports (from 'fs', from 'os', from 'path') while production code in the same package uses the node: prefix convention (from 'node:fs', from 'node:https').

  • [naming convention] workspaces/boost/plugins/boost-connector-utils/src/fault-isolation.ts:26ConnectorEntityProvider interface is defined in fault-isolation.ts while all other public interfaces live in types.ts, breaking the single-location-for-types pattern within this package.

  • [dependency version pinning] workspaces/boost/plugins/boost-connector-utils/package.json:31@backstage/config dependency specifies ^1.3.3 while all other boost workspace packages use ^1.3.6 or ^1.3.8. Yarn resolves all to 1.3.8 but the lower bound is inconsistent.
    Remediation: Align to ^1.3.8.

  • [stale function signature] workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1510-feasibility-report.md:136 — Documents the old signature without the logger parameter.

  • [stale function signature] workspaces/boost/specifications/JIRA-analysis/rhdhplan1505-1-staged-issues.md:64 — Task 1.2 references the old signature without the logger parameter.


Labels: PR adds a new shared utility package for connector infrastructure in the boost workspace

Previous run (2)

Review

Findings

Medium

  • [stale-config-root] workspaces/boost/openspec/changes/connector-shared-infrastructure/design.md — Design doc references catalog.providers as the config root throughout, but the implementation correctly uses ai-catalog.providers per boost(docs): 2.1 github staging plan, frontend requirements, RHDHPLAN-1513 deferment details #4069 clarification. The code is correct; the design doc is stale.
    Remediation: Replace all catalog.providers references with ai-catalog.providers.

  • [stale-config-root] workspaces/boost/openspec/changes/connector-shared-infrastructure/tasks.md:9 — Tasks reference catalog.providers instead of the correct ai-catalog.providers.
    Remediation: Update config path references.

  • [stale-function-signature] workspaces/boost/openspec/changes/connector-shared-infrastructure/design.md:74 — Design doc specifies loadCaBundle(connectorConfig: Config): Buffer | undefined (one parameter), but implementation adds a required logger: LoggerService second parameter.
    Remediation: Update function signature and calling examples in the design doc.

Low

  • [dead-code] workspaces/boost/plugins/boost-connector-utils/src/fault-isolation.ts:68 — The FetchError-specific branch is unreachable; the generic retryable-code check at line 63 already returns true for any error with a retryable code, including FetchError.

  • [TLS-Configuration] workspaces/boost/plugins/boost-connector-utils/src/ca-bundle.ts:87createHttpsAgent does not explicitly set rejectUnauthorized: true. Adding it would defend against NODE_TLS_REJECT_UNAUTHORIZED=0 leaking from dev environments into production.

  • [scope-consistency] workspaces/boost/plugins/boost-connector-utils/package.json:18 — The pluginPackages array lists sibling packages, but they don't list boost-connector-utils back. One-directional inconsistency.

  • [stale-config-root] Multiple additional openspec files reference catalog.providers instead of ai-catalog.providers: proposal.md, ca-bundle-resolution/spec.md, fault-isolation/spec.md, mcp-registry-connector/design.md, mcp-registry-connector/tasks.md, rhoai-connector/tasks.md.

  • [stale-function-signature] design.md:123createProviderWrapper signature uses EntityProvider type instead of ConnectorEntityProvider and is missing the optional ctx parameter.

  • [missing-exports-in-design] design.md:36 — Design doc exports list is missing classifyConnectorError, validateConnectorStartupConfig, ValidateConnectorStartupConfigOptions, and ConnectorEntityProvider.

  • [file-naming] ca-bundle.ts, fault-isolation.ts — Uses kebab-case file naming while existing boost workspace files use single-word lowercase or PascalCase.

  • [import-style] ca-bundle.ts — Uses namespace imports (import * as fs from 'fs') while existing boost code uses named destructured imports.


Labels: PR adds a new package under workspaces/boost/plugins/

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment workspace/boost Boost workspace (Backstage AI plugin) labels Aug 3, 2026
@mareklibra

Copy link
Copy Markdown
Member

/fs-fix

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 7:39 AM UTC · Completed 7:52 AM UTC
Commit: e96d319 · View workflow run →

fullsend-ai-coder Bot added a commit that referenced this pull request Aug 4, 2026
- Remove unreachable FetchError branch in classifyConnectorError
  (already covered by generic retryable-code check)
- Add rejectUnauthorized: true to createHttpsAgent to guard against
  NODE_TLS_REJECT_UNAUTHORIZED=0 leaking from dev environments
- Switch ca-bundle.ts from namespace imports to named destructured
  imports to match existing boost workspace conventions
- Update connector-shared-infrastructure design doc, tasks, proposal,
  and spec files to use ai-catalog.providers config root per #4069
- Fix loadCaBundle signature in docs to include logger parameter
- Fix createProviderWrapper signature in docs to use
  ConnectorEntityProvider type and optional ctx parameter
- Update exports list in design doc to include classifyConnectorError,
  validateConnectorStartupConfig, ValidateConnectorStartupConfigOptions,
  and ConnectorEntityProvider

Addresses review feedback on #4144
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Addressed 11 of 14 review findings: removed dead FetchError branch, added rejectUnauthorized:true TLS hardening, switched to named destructured imports, and updated all connector-shared-infrastructure design docs/specs to use ai-catalog.providers config root with correct function signatures and exports list. Disagreed with 3 findings (pluginPackages cross-ref, file naming convention, out-of-scope openspec updates).

Fixed (11):

  1. dead-code: unreachable FetchError branch in classifyConnectorError (workspaces/boost/plugins/boost-connector-utils/src/fault-isolation.ts): Removed unreachable FetchError-specific branch at line 122-128. The generic retryable-code check at line 112 already returns true for any error with a retryable code, including FetchError.
  2. TLS-Configuration: createHttpsAgent missing rejectUnauthorized: true (workspaces/boost/plugins/boost-connector-utils/src/ca-bundle.ts): Added rejectUnauthorized: true to the https.Agent constructor in createHttpsAgent to defend against NODE_TLS_REJECT_UNAUTHORIZED=0 leaking from dev environments into production.
  3. import-style: ca-bundle.ts uses namespace imports instead of named destructured imports (workspaces/boost/plugins/boost-connector-utils/src/ca-bundle.ts): Changed import * as fs from 'fs' to import { readFileSync } from 'fs' and import * as https from 'https' to import { Agent as HttpsAgent } from 'https' to match existing boost workspace conventions.
  4. stale-config-root: design.md references catalog.providers instead of ai-catalog.providers (workspaces/boost/openspec/changes/connector-shared-infrastructure/design.md): Replaced all catalog.providers references with ai-catalog.providers throughout design.md per boost(docs): 2.1 github staging plan, frontend requirements, RHDHPLAN-1513 deferment details #4069 clarification.
  5. stale-config-root: tasks.md references catalog.providers instead of ai-catalog.providers (workspaces/boost/openspec/changes/connector-shared-infrastructure/tasks.md): Replaced all catalog.providers references with ai-catalog.providers in tasks.md.
  6. stale-function-signature: design.md loadCaBundle signature missing logger parameter (workspaces/boost/openspec/changes/connector-shared-infrastructure/design.md): Updated loadCaBundle signature from (connectorConfig: Config) to (connectorConfig: Config, logger: LoggerService) and all calling examples to include logger parameter.
  7. stale-function-signature: design.md createProviderWrapper uses EntityProvider instead of ConnectorEntityProvider (workspaces/boost/openspec/changes/connector-shared-infrastructure/design.md): Updated createProviderWrapper signature to use ConnectorEntityProvider type, added optional ctx parameter, and changed connection parameter type from EntityProviderConnection to unknown.
  8. missing-exports-in-design: design.md exports list missing classifyConnectorError, validateConnectorStartupConfig, ValidateConnectorStartupConfigOptions, ConnectorEntityProvider (workspaces/boost/openspec/changes/connector-shared-infrastructure/design.md): Updated exports list in design doc to include all public exports: classifyConnectorError, validateConnectorStartupConfig, ConnectorEntityProvider, ValidateConnectorStartupConfigOptions.
  9. stale-config-root: proposal.md references catalog.providers (workspaces/boost/openspec/changes/connector-shared-infrastructure/proposal.md): Replaced all catalog.providers references with ai-catalog.providers and updated loadCaBundle signature to include logger parameter.
  10. stale-config-root: ca-bundle-resolution/spec.md references catalog.providers (workspaces/boost/openspec/changes/connector-shared-infrastructure/specs/ca-bundle-resolution/spec.md): Replaced all catalog.providers references with ai-catalog.providers in CA bundle spec.
  11. stale-config-root: fault-isolation/spec.md references catalog.providers (workspaces/boost/openspec/changes/connector-shared-infrastructure/specs/fault-isolation/spec.md): Replaced all catalog.providers references with ai-catalog.providers in fault isolation spec.

Disagreed (3):

  1. scope-consistency: pluginPackages lists sibling packages that don't list boost-connector-utils back: Adding boost-connector-utils to sibling packages' pluginPackages arrays is a cross-cutting change that modifies files outside this PR's scope and risks merge conflicts with other active PRs. The one-directional listing is standard for a newly added package; sibling packages should add the reverse reference when they actually depend on boost-connector-utils.
  2. file-naming: ca-bundle.ts and fault-isolation.ts use kebab-case instead of single-word or PascalCase: Kebab-case is the standard Node.js convention for multi-word filenames and is widely used in the Backstage ecosystem. The existing boost workspace files cited (single-word like types.ts, or PascalCase like KeycloakAuthClient.ts) follow their own conventions for different reasons: single-word files don't need a separator, PascalCase files export a single class matching the filename. Renaming would break all imports across source and test files for minimal style benefit.
  3. stale-config-root: mcp-registry-connector, rhoai-connector, and oci-skill-connector openspec files reference catalog.providers: Those openspec files belong to separate features (MCP Registry connector, RHOAI connector, OCI Skill connector) that are not part of this PR's scope. Updating them here would be a cross-cutting change that may conflict with other PRs working on those features. They should be updated when their respective connectors are implemented.

Tests: passed

Updated by fullsend fix agent

@mareklibra

Copy link
Copy Markdown
Member

/fs-fix following CI error:


Run yarn tsc:full
plugins/boost-connector-utils/src/fault-isolation.ts:174:52 - error TS2345: Argument of type 'ConnectorErrorContext' is not assignable to parameter of type 'Error | JsonObject | undefined'.
  Type 'ConnectorErrorContext' is not assignable to type 'JsonObject'.
    Index signature for type 'string' is missing in type 'ConnectorErrorContext'.

174         logger.error('Connector connect() failed', errorCtx);
                                                       ~~~~~~~~

plugins/boost-connector-utils/src/fault-isolation.ts:204:48 - error TS2345: Argument of type 'ConnectorErrorContext' is not assignable to parameter of type 'Error | JsonObject | undefined'.
  Type 'ConnectorErrorContext' is not assignable to type 'JsonObject'.
    Index signature for type 'string' is missing in type 'ConnectorErrorContext'.

204       logger.error('Connector refresh failed', errorCtx);
                                                   ~~~~~~~~


Found 2 errors in the same file, starting at: plugins/boost-connector-utils/src/fault-isolation.ts:174

Error: Process completed with exit code 2.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 8:07 AM UTC · Completed 8:21 AM UTC
Commit: f165ffd · View workflow run →

fullsend-ai-coder Bot added a commit that referenced this pull request Aug 4, 2026
…ompat

ConnectorErrorContext lacked a string index signature, making it
incompatible with Backstage LoggerService's JsonObject metadata parameter.
Add `[key: string]: string | boolean | undefined` so the interface
satisfies the `error(message, meta?)` signature without type assertions.

Regenerate API reports for boost-connector-utils (new) and boost
(re-ordered by api-extractor).

Addresses review feedback on #4144
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (human-triggered)

Fixed TS2345 compilation error by adding a string index signature to ConnectorErrorContext, making it compatible with Backstage LoggerService's JsonObject metadata type. All 47 tests pass, lint and prettier checks pass, API reports regenerated.

Fixed (1):

  1. TS2345: ConnectorErrorContext not assignable to JsonObject in logger.error() calls (workspaces/boost/plugins/boost-connector-utils/src/types.ts): Added index signature [key: string]: string | boolean | undefined to the ConnectorErrorContext interface. This makes it structurally compatible with Backstage LoggerService's JsonObject metadata parameter, resolving both TS2345 errors at fault-isolation.ts:174 and fault-isolation.ts:204 without type assertions at call sites.

Tests: passed

Updated by fullsend fix agent

@mareklibra

Copy link
Copy Markdown
Member

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 10:31 AM UTC · Completed 10:47 AM UTC
Commit: c460dad · View workflow run →

Create @red-hat-developer-hub/backstage-plugin-boost-connector-utils
providing shared utilities for AI catalog connector entity providers:

- loadCaBundle(config, logger): resolve CA bundles from tls.caFile
  (file path) or tls.caSecret (resolved $env), with graceful failure
  (WARN for missing file, ERROR for invalid PEM, no expire check)
- createHttpsAgent(caBundle?): factory for https.Agent with custom CA
- createProviderWrapper(provider, logger, ctx?): wrap EntityProvider
  connect() in try/catch to prevent unhandled rejections crashing the
  catalog backend process
- createSafeRefresh(fn, connectorId, logger, ctx?): wrap scheduled
  refresh callbacks with the same fault isolation pattern
- classifyConnectorError(error): classify errors as retryable
  (ECONNREFUSED, ETIMEDOUT, HTTP 5xx/429) or non-retryable (HTTP
  4xx, TypeError, TLS cert errors)
- isConnectorEnabled(config): check enabled boolean with default true
- validateConnectorStartupConfig(config, opts): reject empty
  credentials and invalid/non-HTTPS endpoint URLs at startup

Includes 47 unit tests covering CA loading (file, env, missing,
invalid PEM, chains, isolation), fault isolation (connect crash,
refresh crash, no rethrow, multi-provider independence, structured
error logging), config guards (enabled/disabled/default), and
startup validation (HTTPS URLs, credential fields, descriptive
errors).

Reference app-config YAML at workspaces/boost/examples/ demonstrates
MCP Registry, RHOAI, and OCI Skill connector configuration with an
air-gapped deployment variant.

Config root: ai-catalog.providers.<id> per #4069 clarification.
No Helm charts or Operator CRs (deferred per issue clarification).

Closes #4039
- Remove unreachable FetchError branch in classifyConnectorError
  (already covered by generic retryable-code check)
- Add rejectUnauthorized: true to createHttpsAgent to guard against
  NODE_TLS_REJECT_UNAUTHORIZED=0 leaking from dev environments
- Switch ca-bundle.ts from namespace imports to named destructured
  imports to match existing boost workspace conventions
- Update connector-shared-infrastructure design doc, tasks, proposal,
  and spec files to use ai-catalog.providers config root per #4069
- Fix loadCaBundle signature in docs to include logger parameter
- Fix createProviderWrapper signature in docs to use
  ConnectorEntityProvider type and optional ctx parameter
- Update exports list in design doc to include classifyConnectorError,
  validateConnectorStartupConfig, ValidateConnectorStartupConfigOptions,
  and ConnectorEntityProvider

Addresses review feedback on #4144
…ompat

ConnectorErrorContext lacked a string index signature, making it
incompatible with Backstage LoggerService's JsonObject metadata parameter.
Add `[key: string]: string | boolean | undefined` so the interface
satisfies the `error(message, meta?)` signature without type assertions.

Regenerate API reports for boost-connector-utils (new) and boost
(re-ordered by api-extractor).

Addresses review feedback on #4144
- Use node: protocol for built-in module imports (node:fs, node:https)
- Use optional chaining for credential field empty check
- Use String.replaceAll() instead of String.replace() with regex
- Parameterize retryable error code tests with it.each
- Regenerate API reports after rebase

Addresses review feedback on #4144
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 3 (human-triggered)

Addressed all 5 SonarQube findings: node: protocol imports for built-in modules (fs, https), optional chaining for credential validation, replaceAll() for string replacement, and parameterized test for retryable error codes. Also rebased onto main (resolved 2 merge conflicts in openspec docs) and regenerated API reports.

Fixed (5):

  1. Prefer node:fs over fs import (workspaces/boost/plugins/boost-connector-utils/src/ca-bundle.ts): Changed import { readFileSync } from 'fs' to import { readFileSync } from 'node:fs' per SonarQube rule preferring Node.js built-in module node: protocol prefix
  2. Prefer node:https over https import (workspaces/boost/plugins/boost-connector-utils/src/ca-bundle.ts): Changed import { Agent as HttpsAgent } from 'https' to import { Agent as HttpsAgent } from 'node:https' per SonarQube rule preferring Node.js built-in module node: protocol prefix
  3. Prefer optional chain expression for conciseness (workspaces/boost/plugins/boost-connector-utils/src/config.ts): Changed 'value !== undefined && value.trim() === ""' to 'value?.trim() === ""' using optional chaining as recommended by SonarQube
  4. Prefer String.replaceAll() over String.replace() with regex (workspaces/boost/plugins/boost-connector-utils/src/config.ts): Changed .replace(/./g, '') to .replaceAll('.', '') and .replace(/([a-z])([A-Z])/g, ...) to .replaceAll(/([a-z])([A-Z])/g, ...) per SonarQube rule preferring replaceAll over replace with global regex
  5. Replace 3 tests with a single parameterized one (workspaces/boost/plugins/boost-connector-utils/src/fault-isolation.test.ts): Consolidated three identical-structure retryable error code tests (ECONNREFUSED, ECONNRESET, ETIMEDOUT) into a single it.each parameterized test per SonarQube recommendation

Tests: passed

Decision points
  • Rebase onto main before applying SonarQube fixes as requested by human instruction (alternatives: Apply fixes first then rebase, Rebase only; rationale: Rebasing first ensures fixes are applied on top of the latest main, reducing merge conflicts and ensuring CI runs against current code)

Updated by fullsend fix agent

@fullsend-ai-coder
fullsend-ai-coder Bot force-pushed the agent/4039-connector-utils branch from c460dad to 6937075 Compare August 4, 2026 10:47
@mareklibra

Copy link
Copy Markdown
Member

/fs-fix address CI issues (recently for yarn fix)

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 11:04 AM UTC · Completed 11:18 AM UTC
Commit: 6937075 · View workflow run →

Run `yarn backstage-cli repo fix --publish` to add the new
boost-connector-utils package to the pluginPackages array in all
sibling package.json files sharing pluginId "boost", and fix
alphabetical ordering in the connector-utils package itself.

Addresses CI failure in `yarn fix --check --publish` on PR #4144
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 4 (human-triggered)

Fixed CI failure caused by missing pluginPackages sync. The new boost-connector-utils package needed to be added to all sibling package.json pluginPackages arrays via yarn backstage-cli repo fix --publish.

Fixed (1):

  1. CI failure: yarn backstage-cli repo fix --check --publish: Ran yarn backstage-cli repo fix --publish to sync the pluginPackages arrays across all sibling packages sharing pluginId "boost". The new boost-connector-utils package was missing from the pluginPackages arrays in 6 sibling package.json files (boost, boost-backend, boost-common, boost-node, boost-responses-api-toolkit, boost-toolscope), and the connector-utils package itself had entries in non-alphabetical order.

Tests: passed

Updated by fullsend fix agent

@mareklibra

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 4, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 12:23 PM UTC · Completed 12:42 PM UTC
Commit: 6862842 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@mareklibra

Copy link
Copy Markdown
Member

/fs-fix Address all fullsend-ai-review comments plus following:

  1. classifyConnectorError (fault-isolation.ts) — undici/fetch network failures

    • Native fetch throws TypeError("fetch failed") with cause.code in
      {ECONNREFUSED, ECONNRESET, ETIMEDOUT, EPIPE, EAI_AGAIN}.
    • Today NON_RETRYABLE_TYPES short-circuits all TypeErrors before cause
      is inspected, and RETRYABLE_CODES is never read from err.cause?.code
      (TLS non-retryable already checks cause).
    • Spec treats network-cause failures as retryable and TypeError only for
      malformed URL. Fix ordering: honor retryable/non-retryable codes on
      err and err.cause first; only treat bare TypeError as fatal when there
      is no retryable network cause.
    • Add unit test: TypeError + cause.code=ECONNREFUSED => retryable true.
  2. nextRetryAt never populated by wrappers

    • fault-isolation/spec requires nextRetryAt on structured failure logs
      when retryable=true; buildErrorContext never sets it; ctx only has
      endpoint.
    • Extend ctx with optional nextRetryAt (or schedule hint), or narrow the
      spec/API so nextRetryAt is explicitly connector-owned and not implied
      by createProviderWrapper/createSafeRefresh.
  3. HTTP status classification misses axios-shaped errors

    • README documents axios.create({ httpsAgent }). Axios exposes status as
      error.response.status, not err.status / err.statusCode.
    • Also read response?.status so 429/5xx/4xx classification matches the
      spec for the documented HTTP client. Add a test with a nested
      response.status.
  4. Add a changeset for the new public package

    • @red-hat-developer-hub/backstage-plugin-boost-connector-utils (new
      0.1.0 publishable package). Sibling package.json edits that only sync
      pluginPackages can stay out of the changeset if that matches workspace
      practice; the new package itself needs one per CONTRIBUTING.md.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 1:01 PM UTC · Completed 1:17 PM UTC
Commit: 6862842 · View workflow run →

- Reorder classifyConnectorError to check retryable/non-retryable codes
  on err.code and err.cause?.code before NON_RETRYABLE_TYPES so native
  fetch TypeErrors with network causes (ECONNREFUSED, ETIMEDOUT, etc.)
  are correctly classified as retryable
- Add axios-shaped err.response?.status to HTTP status extraction so
  429/5xx/4xx classification works with the documented HTTP client
- Introduce FaultIsolationContext interface with optional nextRetryAt;
  wrappers pass it through to error context when retryable=true,
  documenting nextRetryAt as connector-owned (not computed by wrappers)
- Add changeset for the new public package
- Add 7 new unit tests covering native-fetch TypeError+cause, axios
  response.status, and nextRetryAt passthrough/omission

Addresses review feedback on #4144
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 5 (human-triggered)

Addressed all 4 human review findings. (1) Reordered classifyConnectorError to check err.code/err.cause?.code before NON_RETRYABLE_TYPES so native-fetch TypeErrors with network causes are retryable. (2) Introduced FaultIsolationContext with connector-owned nextRetryAt passthrough. (3) Added axios err.response?.status to HTTP status extraction. (4) Added changeset for new package. 7 new unit tests, all 54 tests passing.

Fixed (4):

  1. classifyConnectorError: undici/fetch network failures misclassified (workspaces/boost/plugins/boost-connector-utils/src/fault-isolation.ts): Reordered classification logic: retryable/non-retryable codes on err.code and err.cause?.code are now checked before NON_RETRYABLE_TYPES, so native-fetch TypeErrors with network cause codes (ECONNREFUSED, ECONNRESET, ETIMEDOUT, EPIPE, EAI_AGAIN) are correctly classified as retryable. Bare TypeErrors (malformed URL) still return non-retryable. Added 4 unit tests: TypeError+cause.code=ECONNREFUSED (retryable), TypeError+cause.code=ETIMEDOUT (retryable), TypeError+cause.code=CERT_HAS_EXPIRED (non-retryable TLS), bare TypeError (non-retryable).
  2. nextRetryAt never populated by wrappers (workspaces/boost/plugins/boost-connector-utils/src/fault-isolation.ts): Introduced FaultIsolationContext interface with optional nextRetryAt field, documented as connector-owned. buildErrorContext now accepts FaultIsolationContext and passes nextRetryAt through to ConnectorErrorContext only when retryable=true. createProviderWrapper and createSafeRefresh ctx parameters updated to FaultIsolationContext. FaultIsolationContext exported from index.ts. Added 2 unit tests: nextRetryAt included when retryable, omitted when non-retryable.
  3. HTTP status classification misses axios-shaped errors (workspaces/boost/plugins/boost-connector-utils/src/fault-isolation.ts): Extended httpStatus extraction to also read err.response?.status (axios error shape). Added response property to the type cast. Added 2 unit tests: axios-shaped response.status 503 (retryable), axios-shaped response.status 401 (non-retryable).
  4. Add a changeset for the new public package (workspaces/boost/.changeset/connector-utils-initial.md): Created minor changeset for @red-hat-developer-hub/backstage-plugin-boost-connector-utils initial release, following the workspace changeset convention.

Tests: passed

Updated by fullsend fix agent

@mareklibra

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 4, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:21 PM UTC · Completed 1:38 PM UTC
Commit: aa14adc · View workflow run →

@mareklibra

Copy link
Copy Markdown
Member

/fs-fix both the latest review comments and the failing CI

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 1:50 PM UTC · Completed 2:06 PM UTC
Commit: aa14adc · View workflow run →

- Remove dead code: unreachable value?.trim() === '' check in
  validateCredentialField (ConfigReader throws on empty strings first)
- Fix misleading JSDoc: rewrite validateCredentialField comment to
  match actual non-empty validation behavior
- Fix import convention: use node: prefix for fs/os/path in test file
- Move ConnectorEntityProvider interface from fault-isolation.ts to
  types.ts, consolidating all public interfaces in one location
- Align @backstage/config dependency to ^1.3.8 matching monorepo
- Regenerate boost/report.api.md to fix CI api-report check

Addresses review feedback on #4144
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 6 (human-triggered)

Addressed 6 of 7 review findings and fixed the CI api-report failure. Fixed dead code removal, misleading JSDoc, import convention, interface location, dependency version alignment, and regenerated API reports. Disagreed with 1 finding (path traversal) as admin-controlled config consistent with Backstage patterns.

Fixed (6):

  1. dead code / unreachable branch in validateCredentialField (workspaces/boost/plugins/boost-connector-utils/src/config.ts): Removed the unreachable value?.trim() === '' check. Backstage ConfigReader.getOptionalString() throws on empty strings before this branch could execute; the catch block already handles that case with a descriptive error message.
  2. misleading JSDoc comment on validateCredentialField (workspaces/boost/plugins/boost-connector-utils/src/config.ts): Rewrote the JSDoc to accurately describe what the function does: validates that credential fields are non-empty when present. Removed the incorrect description of a plaintext vs $env detection algorithm that the implementation never performed.
  3. import convention: bare specifiers in test file (workspaces/boost/plugins/boost-connector-utils/src/ca-bundle.test.ts): Changed bare specifier imports (fs, os, path) to use the node: prefix convention (node:fs, node:os, node:path), matching the production code style.
  4. naming convention: ConnectorEntityProvider in wrong file (workspaces/boost/plugins/boost-connector-utils/src/types.ts): Moved ConnectorEntityProvider interface from fault-isolation.ts to types.ts, consolidating all public interfaces in one location. Updated fault-isolation.ts to import from types.ts and updated index.ts to re-export from types.ts.
  5. dependency version pinning: @backstage/config ^1.3.3 (workspaces/boost/plugins/boost-connector-utils/package.json): Updated @backstage/config from ^1.3.3 to ^1.3.8 to match the version range used by other packages in the monorepo. Updated yarn.lock accordingly.
  6. CI failure: check api reports and generate API reference (workspaces/boost/plugins/boost/report.api.md): Regenerated report.api.md for the boost plugin to pass the CI api-report check. The report had stale ordering of union members and translation keys.

Disagreed (1):

  1. path traversal in caFile config value: The caFile value is admin-controlled Backstage configuration (not user input), consistent with Backstage's own CA file handling patterns. Adding path sanitization could break legitimate absolute paths used in K8s volume mounts. The config surface is restricted to cluster admins.

Tests: passed

Updated by fullsend fix agent

@sonarqubecloud

sonarqubecloud Bot commented Aug 4, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature ready-for-review Agent PR ready for human review requires-manual-review Review requires human judgment workspace/boost Boost workspace (Backstage AI plugin)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cross-Connector Shared Infrastructure Package (issue 1 of 29)

1 participant