Skip to content

Conversation

@yasun1
Copy link
Contributor

@yasun1 yasun1 commented Dec 10, 2025

Summary by CodeRabbit

  • New Features

    • Search docs now describe TSL query syntax with examples (name, status., labels.).
    • Resource IDs now use a stable lowercase Base32 format (consistent 32-character IDs).
  • Bug Fixes & Improvements

    • Stricter resource name validation (must start/end alphanumeric; hyphens allowed internally).
    • Added DB indexes to improve query/search performance.
    • Field-name mapping for status.* and labels.* improved for reliable searches.
  • Tests

    • New integration and unit tests covering search field mapping, ID format, and rejected disallowed fields.
  • Documentation

    • README updated with TSL examples and search usage.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Walkthrough

This PR updates OpenAPI docs (SearchParams) and the name validation pattern to an RFC‑1123/DNS‑1123 style; changes KSUID output to a lowercase Base32 32-character ID and updates factories/tests accordingly; introduces field-name mapping (status.* → DB columns, labels.* → JSONB extracts) earlier in the search pipeline; adds DB indexes on status_last_updated_time for clusters and node_pools with rollbacks; and expands test factories and integration tests to cover status/labels search behavior.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant API as API Handler
    participant Val as Validation
    participant Parser as TSL Parser
    participant Mapper as Field Mapper
    participant Builder as Search Builder
    participant SQLizer as SQL Generator
    participant DB

    User->>API: HTTP search request (e.g. status.phase='NotReady' and labels.region='us-east')
    API->>Val: Validate request & params (OpenAPI + name pattern)
    Val-->>API: OK / errors

    API->>Parser: Parse TSL → AST
    Parser-->>API: AST

    API->>Mapper: FieldNameWalk(AST) — map status.* → columns, labels.* → JSONB extracts
    Mapper-->>API: Mapped AST

    API->>Builder: buildSearchValues(mapped AST)
    Builder->>SQLizer: treeWalkForSqlizer(mapped AST)
    SQLizer->>DB: Execute generated SQL (e.g. WHERE status_phase = ?, labels->>'region' = ?)
    DB-->>API: Results
    API-->>User: HTTP response with filtered items
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring extra attention:

  • pkg/sql_helpers.go: label key validation, labels.* handling and status.* → column mappings
  • pkg/services/generic.go: reordering of FieldNameWalk, SearchDisallowedFields initialization, and related-table resolution changes
  • pkg/api/resource_id.go and test/factories: Base32 KSUID encoding and tests ensuring length/charset/uniqueness/K8s-compatibility
  • pkg/db/migrations/*: new indexes (clusters/node_pools) and rollback ordering
  • test/integration/search_field_mapping_test.go and test/factories/*: DB-backed factory helpers and integration assertions around time/labels/status

Possibly related PRs

Suggested labels

lgtm

Suggested reviewers

  • aredenba-rh
  • yingzhanredhat

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the two main features added: user-friendly search syntax (field mapping for status, labels) and lowercase Base32 ID encoding (resource ID format change).
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (1)
test/factories/factory.go (1)

12-22: Duplicate code - see comment on pkg/api/resource_id.go.

This code duplicates the Base32 encoding logic from pkg/api/resource_id.go. Consider using api.NewID() directly instead of reimplementing the encoding logic.

🧹 Nitpick comments (2)
pkg/api/resource_id.go (1)

9-22: Suggest extracting shared Base32 encoding constants.

The uidAlphabet and uidEncoding definitions are duplicated in test/factories/factory.go (lines 18-22). Consider extracting these to a shared location to maintain a single source of truth.

Apply this approach:

Option 1 (Recommended): Keep in pkg/api/resource_id.go and import in test/factories/factory.go

In test/factories/factory.go:

 package factories
 
 import (
-	"encoding/base32"
-
+	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/api"
 	"github.com/segmentio/ksuid"
 )
 
-// NewID generates a new unique identifier using KSUID with lowercase Base32 encoding.
-// The resulting identifier is compatible with Kubernetes DNS-1123 subdomain naming requirements.
 func (f *Factories) NewID() string {
-	return uidEncoding.EncodeToString(ksuid.New().Bytes())
+	return api.NewID()
 }
-
-// uidAlphabet is the lowercase alphabet used to encode unique identifiers.
-const uidAlphabet = "0123456789abcdefghijklmnopqrstuv"
-
-// uidEncoding is the lowercase variant of Base32 used to encode unique identifiers.
-var uidEncoding = base32.NewEncoding(uidAlphabet).WithPadding(base32.NoPadding)
test/integration/search_field_mapping_test.go (1)

229-285: Consider splitting NodePool test into separate test functions.

This test covers two distinct scenarios (status.phase and labels mappings) in a single function. While functionally correct, splitting into TestSearchNodePoolStatusPhaseMapping and TestSearchNodePoolLabelsMapping would improve test isolation and make failures easier to diagnose.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ffd6d0 and 9aded6e.

📒 Files selected for processing (14)
  • openapi/openapi.yaml (2 hunks)
  • pkg/api/resource_id.go (1 hunks)
  • pkg/api/resource_id_test.go (1 hunks)
  • pkg/db/migrations/202511111044_add_clusters.go (1 hunks)
  • pkg/db/migrations/202511111055_add_node_pools.go (2 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/handlers/validation.go (2 hunks)
  • pkg/handlers/validation_test.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (3 hunks)
  • test/factories/clusters.go (2 hunks)
  • test/factories/factory.go (1 hunks)
  • test/factories/node_pools.go (2 hunks)
  • test/integration/search_field_mapping_test.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Files:

  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/services/generic_test.go
  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/api/resource_id_test.go
  • pkg/db/sql_helpers.go
  • test/integration/search_field_mapping_test.go
  • pkg/services/generic.go
  • pkg/handlers/validation_test.go
  • test/factories/factory.go
  • test/factories/clusters.go
  • pkg/handlers/validation.go
  • test/factories/node_pools.go
  • pkg/api/resource_id.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use PostgreSQL 14.2 with GORM ORM for all database operations

Files:

  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/sql_helpers.go
pkg/db/migrations/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/db/migrations/**/*.go: Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns
Use polymorphic adapter_statuses table with owner_type and owner_id columns instead of separate status tables per resource type

Files:

  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/db/migrations/202511111055_add_node_pools.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Files:

  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/services/generic_test.go
  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/api/resource_id_test.go
  • pkg/db/sql_helpers.go
  • pkg/services/generic.go
  • pkg/handlers/validation_test.go
  • pkg/handlers/validation.go
  • pkg/api/resource_id.go
pkg/{handlers,services}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Always retrieve database session from context via db.NewContext() instead of creating new connections

Files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
  • pkg/handlers/validation_test.go
  • pkg/handlers/validation.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/{handlers,services,dao}/**/*.go: Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
  • pkg/handlers/validation_test.go
  • pkg/handlers/validation.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Files:

  • pkg/api/resource_id_test.go
  • pkg/api/resource_id.go
test/integration/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

test/integration/**/*.go: Create integration tests using Testcontainers for isolated PostgreSQL instances per test
All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Files:

  • test/integration/search_field_mapping_test.go
openapi/openapi.yaml

📄 CodeRabbit inference engine (CLAUDE.md)

TypeSpec definitions are maintained separately in hyperfleet-api-spec repository; only edit openapi/openapi.yaml in this repository for spec changes

Files:

  • openapi/openapi.yaml
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Files:

  • pkg/handlers/validation_test.go
  • pkg/handlers/validation.go
test/factories/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use test data factories in test/factories/ to provide consistent test data instead of hardcoding values

Files:

  • test/factories/factory.go
  • test/factories/clusters.go
  • test/factories/node_pools.go
🧠 Learnings (8)
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns

Applied to files:

  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/sql_helpers.go
  • pkg/services/generic.go
  • test/factories/node_pools.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns instead of separate status tables per resource type

Applied to files:

  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/sql_helpers.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/dao/**/*.go : Increment generation field on each spec update to enable optimistic concurrency control

Applied to files:

  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/services/generic_test.go
  • pkg/db/sql_helpers.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : Create integration tests using Testcontainers for isolated PostgreSQL instances per test

Applied to files:

  • pkg/services/generic_test.go
  • test/integration/search_field_mapping_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Applied to files:

  • pkg/services/generic_test.go
  • pkg/api/resource_id_test.go
  • test/integration/search_field_mapping_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/factories/**/*.go : Use test data factories in test/factories/ to provide consistent test data instead of hardcoding values

Applied to files:

  • test/factories/factory.go
  • test/factories/clusters.go
  • test/factories/node_pools.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use OCM_ENV environment variable to select configuration environment (development, unit_testing, integration_testing, production)

Applied to files:

  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/handlers/**/*.go : Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Applied to files:

  • pkg/handlers/validation.go
🧬 Code graph analysis (6)
pkg/services/generic_test.go (1)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (129-220)
pkg/api/resource_id_test.go (1)
pkg/api/resource_id.go (1)
  • NewID (14-16)
test/integration/search_field_mapping_test.go (5)
test/registration.go (1)
  • RegisterIntegration (13-24)
test/factories/clusters.go (3)
  • NewClusterWithStatus (55-77)
  • NewClusterWithLabels (80-101)
  • NewClusterWithStatusAndLabels (104-126)
test/factories/factory.go (1)
  • Factories (9-10)
pkg/api/resource_id.go (1)
  • NewID (14-16)
test/factories/node_pools.go (2)
  • NewNodePoolWithStatus (75-97)
  • NewNodePoolWithLabels (100-121)
pkg/services/generic.go (1)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (129-220)
pkg/handlers/validation.go (1)
pkg/errors/errors.go (1)
  • Validation (214-216)
test/factories/node_pools.go (2)
pkg/db/session.go (1)
  • SessionFactory (12-20)
pkg/api/node_pool_types.go (1)
  • NodePool (12-40)
🔇 Additional comments (22)
pkg/db/migrations/202511111055_add_node_pools.go (1)

73-77: Index addition and rollback for status_last_updated_time look correct

The new idx_node_pools_status_last_updated_time index is created and dropped in a fully symmetric, idempotent way (IF NOT EXISTS / IF EXISTS), matches the existing index naming scheme, and fits the stated search use case. The migration still keeps spec and status_conditions as JSONB, consistent with the JSONB storage guideline. Based on learnings, this is aligned with the expected migration shape.

Also applies to: 99-101

pkg/db/migrations/202511111044_add_clusters.go (1)

70-75: Consistent status_last_updated_time indexing and rollback for clusters

The idx_clusters_status_last_updated_time index is added in line with existing index patterns and is cleanly removed in rollback using IF EXISTS. This keeps the clusters migration symmetrical and idempotent while supporting the new search patterns that filter on status_last_updated_time. The JSONB-based spec/status storage remains intact, consistent with prior migration guidelines.

Also applies to: 80-82

pkg/handlers/validation_test.go (1)

81-83: LGTM! Good test coverage for RFC 1123 edge cases.

The added test cases properly validate that names cannot start or end with hyphens, ensuring compliance with the RFC 1123 pattern.

pkg/handlers/validation.go (2)

10-12: LGTM! RFC 1123 DNS subdomain pattern correctly implemented.

The regex pattern ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ properly enforces Kubernetes-compatible naming: starts and ends with alphanumeric, allows internal hyphens.


76-76: LGTM! Clear validation error message.

The updated error message accurately describes the RFC 1123 validation requirements.

openapi/openapi.yaml (2)

410-413: LGTM! Clear documentation of search syntax.

The SearchParams description effectively documents the TSL query syntax with practical examples demonstrating status.*, labels.*, and in operator usage.


718-718: LGTM! Pattern matches validation logic.

The ClusterBase.name pattern is consistent with the RFC 1123 validation implemented in pkg/handlers/validation.go line 12.

pkg/services/generic_test.go (2)

35-36: Verify the error message change.

The error message changed from "clusters.id invalid field name" to "spec invalid field name" for the search query spec = '{}'. While both indicate invalid fields, can you confirm this change is intentional and reflects the actual error behavior?


59-95: LGTM! Comprehensive field mapping test coverage.

The new test cases thoroughly validate the field mapping functionality:

  • status.phasestatus_phase
  • status.last_updated_timestatus_last_updated_time
  • labels.environmentlabels->>'environment'
  • id query (should be allowed)

The explicit FieldNameWalk call before SQL conversion correctly reflects the new pre-processing step.

pkg/api/resource_id_test.go (3)

8-30: LGTM! Thorough ID format and uniqueness validation.

The test properly validates:

  • 32-character length (correct for Base32 encoding of 20-byte KSUID)
  • Character set restricted to 0-9a-v (matching uidAlphabet)
  • Uniqueness across 100 generated IDs

32-51: LGTM! Pragmatic approach to time-ordering validation.

Good decision to verify consistency and uniqueness within the same second rather than introducing test delays. The comment clearly explains KSUID's second-level timestamp precision.


53-67: LGTM! Kubernetes compatibility validation.

The test correctly validates DNS-1123 subdomain compatibility using the standard pattern, confirming that the generated IDs are suitable for Kubernetes resource names.

pkg/db/sql_helpers.go (2)

36-66: LGTM! Clean field mapping implementation.

The field mapping logic is well-structured:

  • labels.* correctly maps to JSONB extraction: labels->>'xxx'
  • status.* maps to database columns via statusFieldMappings
  • Mappings are applied before disallowed field validation (correct order)

The statusFieldMappings includes the core status fields (phase, last_updated_time, last_transition_time, observed_generation, conditions).


79-79: LGTM! More accurate comment.

The comment change from "Check for allowed fields" to "Check for disallowed fields" better describes the actual logic (checking if field exists in disallowedFields map).

pkg/services/generic.go (3)

41-49: LGTM! Provider-specific field restrictions are well-structured.

The SearchDisallowedFields map correctly blocks spec fields for both Cluster and NodePool resources, preventing queries on provider-specific JSONB data. This aligns with the coding guideline to store specifications as JSONB without making them searchable.


171-181: LGTM! Field name mapping correctly moved before related-table detection.

Applying FieldNameWalk before treeWalkForRelatedTables prevents status and labels prefixes from being misinterpreted as related table names. The comment clearly documents the rationale.


347-358: LGTM! Cleaned up redundant call with clear documentation.

The comment accurately explains that FieldNameWalk is now called earlier in the pipeline, and the function correctly delegates to sqlFilter.Walk.

test/integration/search_field_mapping_test.go (5)

16-54: LGTM! Solid integration test for status.phase mapping.

The test correctly creates clusters with different status phases, queries using user-friendly syntax, and validates both inclusion and exclusion of expected results. As per coding guidelines, this provides good coverage of the search functionality.


56-99: LGTM! Time-based filtering test is well-structured.

Good use of relative time offsets to test status.last_updated_time mapping. The RFC3339 format in the search query aligns with standard timestamp handling.


101-145: LGTM! Labels JSONB query mapping test is thorough.

Correctly validates that labels.environment='production' maps to the appropriate JSONB query and returns only matching clusters.


147-163: LGTM! Negative test for disallowed spec field.

Important test that validates the SearchDisallowedFields configuration correctly rejects queries on the spec field with a 400 status.


165-227: LGTM! Combined AND query test validates complex search expressions.

Good coverage of multi-condition queries combining status and labels fields, with proper verification that only matching resources are returned.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
test/factories/factory.go (1)

14-22: Consider reusing api.NewID() to avoid duplicating encoding logic.

The uidAlphabet and uidEncoding definitions are duplicated from pkg/api/resource_id.go. If the encoding logic changes, both files must be updated in sync.

 package factories
 
 import (
-	"encoding/base32"
-
-	"github.com/segmentio/ksuid"
+	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/api"
 )
 
 type Factories struct {
 }
 
 // NewID generates a new unique identifier using KSUID with lowercase Base32 encoding.
 // The resulting identifier is compatible with Kubernetes DNS-1123 subdomain naming requirements.
 func (f *Factories) NewID() string {
-	return uidEncoding.EncodeToString(ksuid.New().Bytes())
+	return api.NewID()
 }
-
-// uidAlphabet is the lowercase alphabet used to encode unique identifiers.
-const uidAlphabet = "0123456789abcdefghijklmnopqrstuv"
-
-// uidEncoding is the lowercase variant of Base32 used to encode unique identifiers.
-var uidEncoding = base32.NewEncoding(uidAlphabet).WithPadding(base32.NoPadding)
test/factories/clusters.go (1)

114-139: Consider reusing NewClusterWithLabels to reduce duplication.

NewClusterWithStatusAndLabels duplicates the label update logic from NewClusterWithLabels. This could be simplified by delegating to the existing helper.

 func NewClusterWithStatusAndLabels(f *Factories, dbFactory db.SessionFactory, id string, phase string, lastUpdatedTime *time.Time, labels map[string]string) (*api.Cluster, error) {
 	cluster, err := NewClusterWithStatus(f, dbFactory, id, phase, lastUpdatedTime)
 	if err != nil {
 		return nil, err
 	}
 
 	if labels != nil {
-		labelsJSON, err := json.Marshal(labels)
-		if err != nil {
-			return nil, err
-		}
-
-		dbSession := dbFactory.New(context.Background())
-		err = dbSession.Model(cluster).Update("labels", labelsJSON).Error
+		labelsJSON, marshalErr := json.Marshal(labels)
+		if marshalErr != nil {
+			return nil, marshalErr
+		}
+		dbSession := dbFactory.New(context.Background())
+		err = dbSession.Model(cluster).Update("labels", labelsJSON).Error
 		if err != nil {
 			return nil, err
 		}
-
 		if err := reloadCluster(dbSession, cluster); err != nil {
 			return nil, err
 		}
 	}
 
 	return cluster, nil
 }

Alternatively, consider calling the label update logic through a shared helper to maintain consistency if this pattern grows.

test/factories/node_pools.go (1)

134-159: Same duplication pattern as clusters.go.

The label update logic in lines 141-155 duplicates NewNodePoolWithLabels. Consider applying the same refactor suggested for the cluster factories if you address that.

test/integration/search_field_mapping_test.go (1)

229-285: LGTM! Good parity test for NodePool field mapping.

This test ensures that NodePool resources support the same field mapping as Clusters. Testing both status.phase and labels.xxx in a single test function is acceptable, though splitting them could improve test isolation and error diagnosis.

Consider splitting into two separate test functions (TestSearchNodePoolStatusPhaseMapping and TestSearchNodePoolLabelsMapping) for better isolation and clearer failure diagnosis. This is a minor suggestion and the current approach is also fine.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9aded6e and b3fa8cb.

📒 Files selected for processing (14)
  • openapi/openapi.yaml (2 hunks)
  • pkg/api/resource_id.go (1 hunks)
  • pkg/api/resource_id_test.go (1 hunks)
  • pkg/db/migrations/202511111044_add_clusters.go (1 hunks)
  • pkg/db/migrations/202511111055_add_node_pools.go (2 hunks)
  • pkg/db/sql_helpers.go (3 hunks)
  • pkg/handlers/validation.go (2 hunks)
  • pkg/handlers/validation_test.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (3 hunks)
  • test/factories/clusters.go (2 hunks)
  • test/factories/factory.go (1 hunks)
  • test/factories/node_pools.go (2 hunks)
  • test/integration/search_field_mapping_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/api/resource_id_test.go
  • pkg/handlers/validation.go
  • pkg/handlers/validation_test.go
  • pkg/services/generic_test.go
🧰 Additional context used
📓 Path-based instructions (11)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Files:

  • test/factories/clusters.go
  • test/factories/node_pools.go
  • pkg/api/resource_id.go
  • test/integration/search_field_mapping_test.go
  • test/factories/factory.go
  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/services/generic.go
  • pkg/db/sql_helpers.go
test/factories/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use test data factories in test/factories/ to provide consistent test data instead of hardcoding values

Files:

  • test/factories/clusters.go
  • test/factories/node_pools.go
  • test/factories/factory.go
openapi/openapi.yaml

📄 CodeRabbit inference engine (CLAUDE.md)

TypeSpec definitions are maintained separately in hyperfleet-api-spec repository; only edit openapi/openapi.yaml in this repository for spec changes

Files:

  • openapi/openapi.yaml
pkg/api/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Files:

  • pkg/api/resource_id.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Files:

  • pkg/api/resource_id.go
  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/services/generic.go
  • pkg/db/sql_helpers.go
test/integration/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

test/integration/**/*.go: Create integration tests using Testcontainers for isolated PostgreSQL instances per test
All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Files:

  • test/integration/search_field_mapping_test.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use PostgreSQL 14.2 with GORM ORM for all database operations

Files:

  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/db/sql_helpers.go
pkg/db/migrations/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/db/migrations/**/*.go: Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns
Use polymorphic adapter_statuses table with owner_type and owner_id columns instead of separate status tables per resource type

Files:

  • pkg/db/migrations/202511111044_add_clusters.go
pkg/{handlers,services}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Always retrieve database session from context via db.NewContext() instead of creating new connections

Files:

  • pkg/services/generic.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/{handlers,services,dao}/**/*.go: Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Files:

  • pkg/services/generic.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Files:

  • pkg/services/generic.go
🧠 Learnings (10)
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/factories/**/*.go : Use test data factories in test/factories/ to provide consistent test data instead of hardcoding values

Applied to files:

  • test/factories/clusters.go
  • test/factories/node_pools.go
  • test/factories/factory.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for all database operations

Applied to files:

  • test/factories/clusters.go
  • test/factories/node_pools.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Always retrieve database session from context via db.NewContext() instead of creating new connections

Applied to files:

  • test/factories/clusters.go
  • test/factories/node_pools.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns

Applied to files:

  • test/factories/clusters.go
  • test/factories/node_pools.go
  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/services/generic.go
  • pkg/db/sql_helpers.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Always use the context-based database session from db.NewContext(ctx) instead of creating new connections to participate in HTTP request transactions

Applied to files:

  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use OCM_ENV environment variable to select configuration environment (development, unit_testing, integration_testing, production)

Applied to files:

  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Applied to files:

  • test/integration/search_field_mapping_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : Create integration tests using Testcontainers for isolated PostgreSQL instances per test

Applied to files:

  • test/integration/search_field_mapping_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns instead of separate status tables per resource type

Applied to files:

  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/db/sql_helpers.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/dao/**/*.go : Increment generation field on each spec update to enable optimistic concurrency control

Applied to files:

  • pkg/services/generic.go
  • pkg/db/sql_helpers.go
🧬 Code graph analysis (4)
test/factories/clusters.go (4)
pkg/api/cluster_types.go (1)
  • Cluster (11-34)
test/factories/factory.go (1)
  • Factories (9-10)
pkg/db/session.go (1)
  • SessionFactory (12-20)
pkg/db/migrations/migration_structs.go (1)
  • Model (36-41)
test/integration/search_field_mapping_test.go (2)
test/factories/clusters.go (3)
  • NewClusterWithStatus (62-86)
  • NewClusterWithLabels (89-112)
  • NewClusterWithStatusAndLabels (115-139)
test/factories/node_pools.go (2)
  • NewNodePoolWithStatus (82-106)
  • NewNodePoolWithLabels (109-132)
test/factories/factory.go (1)
pkg/api/resource_id.go (1)
  • NewID (14-16)
pkg/services/generic.go (1)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (129-220)
🔇 Additional comments (18)
pkg/db/migrations/202511111044_add_clusters.go (2)

70-74: Index on status_last_updated_time is well‑placed and consistent.

The new idx_clusters_status_last_updated_time index matches the existing indexing style (IF NOT EXISTS on a single column) and targets the field described as hot in sentinel queries; no changes needed.


80-82: Rollback cleanly removes the new index.

Dropping idx_clusters_status_last_updated_time with IF EXISTS and doing so before the other drops keeps migrate/rollback symmetric and safe; this is good as‑is.

pkg/api/resource_id.go (1)

9-22: LGTM!

The implementation correctly encodes KSUID bytes to a 32-character lowercase Base32 string. The custom alphabet ensures DNS-1123 compatibility (lowercase alphanumeric only), and the documentation clearly explains the design rationale.

test/factories/clusters.go (2)

55-86: LGTM! The reload helper and error handling are well implemented.

The reloadCluster helper properly propagates errors, addressing previous review feedback. Error handling throughout NewClusterWithStatus is correct.


88-112: LGTM!

Proper error handling for JSON marshaling and DB operations. Based on learnings, this follows the factory pattern for consistent test data.

pkg/db/sql_helpers.go (1)

36-43: LGTM! Static mapping is a safe approach.

The statusFieldMappings uses a static map lookup, which safely restricts user input to known column names without injection risk.

openapi/openapi.yaml (2)

410-413: LGTM! Clear documentation with practical examples.

The TSL query syntax examples (status.phase, labels.region) align with the new field mappings implemented in sql_helpers.go.


716-719: LGTM! RFC 1123 compliant pattern.

The updated pattern ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ correctly enforces DNS-1123 label constraints: starts and ends with alphanumeric, allows internal hyphens. Combined with minLength: 3, this ensures valid Kubernetes resource names.

test/factories/node_pools.go (2)

75-106: LGTM! Error handling addresses previous review feedback.

The reloadNodePool helper properly propagates errors, and all DB operations in NewNodePoolWithStatus correctly handle errors.


108-132: LGTM!

Proper error handling for JSON marshaling and DB operations. Based on learnings, this follows the factory pattern for consistent test data.

pkg/services/generic.go (3)

41-49: LGTM! Appropriate disallowed field mappings for provider-specific data.

The spec field is correctly blocked from search queries for both Cluster and NodePool, which aligns with the learning that cluster and nodepool specifications are stored as JSONB and are provider-specific. This prevents potentially expensive or meaningless searches on unindexed JSON blobs.


171-178: LGTM! Correct ordering of field name mapping before related table detection.

Moving FieldNameWalk to execute before treeWalkForRelatedTables is the right architectural decision. This ensures that user-friendly field names like status.phase and labels.xxx are transformed to their database equivalents (status_phase, labels->>'xxx') before the related table detection logic runs, preventing false positives where "status" or "labels" could be mistakenly identified as related resources.


347-357: LGTM! Clean separation of concerns.

The function is now more focused on its single responsibility of converting the TSL tree to a SQLizer. The comment clearly documents that field name mapping happens earlier in the pipeline, which aids future maintainability.

test/integration/search_field_mapping_test.go (5)

16-54: LGTM! Comprehensive test for status.phase field mapping.

Good coverage verifying both that the correct cluster is found and that clusters with non-matching status are excluded. The assertions properly validate the status field value after retrieval.


56-99: LGTM! Solid time-based filtering test.

Good test coverage for the status.last_updated_time field mapping with proper time comparison logic. Using RFC3339 format for the timestamp in the query is the correct approach.


101-145: LGTM! Good JSONB labels query test with appropriate nil checks.

The test properly validates the labels.xxx to labels->>'xxx' mapping for JSONB queries. The nil check on item.Labels (line 137) is a good defensive practice.


147-163: LGTM! Important validation test for blocked fields.

This test correctly verifies that queries on the spec field are rejected with a 400 status code, aligning with the SearchDisallowedFields configuration.


165-227: LGTM! Excellent combined query test coverage.

This test provides thorough coverage of AND query combinations, verifying that both status and label conditions must match. The test data setup with three distinct clusters (matching, wrong region, wrong status) effectively validates the query logic.

@yasun1 yasun1 force-pushed the search-capabilities branch from b3fa8cb to 1950219 Compare December 10, 2025 06:49
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (10)
pkg/api/resource_id.go (1)

18-22: Consider moving declarations before the function.

Conventionally, constants and package-level variables are placed before the functions that use them for improved readability. This is a minor style preference.

 package api
 
 import (
 	"encoding/base32"
 
 	"github.com/segmentio/ksuid"
 )
 
+// uidAlphabet is the lowercase alphabet used to encode unique identifiers.
+const uidAlphabet = "0123456789abcdefghijklmnopqrstuv"
+
+// uidEncoding is the lowercase variant of Base32 used to encode unique identifiers.
+var uidEncoding = base32.NewEncoding(uidAlphabet).WithPadding(base32.NoPadding)
+
 // NewID generates a new unique identifier using KSUID with lowercase Base32 encoding.
 // The resulting 32-character lowercase string is compatible with Kubernetes DNS-1123
 // subdomain naming requirements, making it suitable for use as Kubernetes resource names
 // and labels. The KSUID provides time-based ordering (second precision) and global
 // uniqueness without requiring a central server.
 func NewID() string {
 	return uidEncoding.EncodeToString(ksuid.New().Bytes())
 }
-
-// uidAlphabet is the lowercase alphabet used to encode unique identifiers.
-const uidAlphabet = "0123456789abcdefghijklmnopqrstuv"
-
-// uidEncoding is the lowercase variant of Base32 used to encode unique identifiers.
-var uidEncoding = base32.NewEncoding(uidAlphabet).WithPadding(base32.NoPadding)
pkg/db/migrations/202511111055_add_node_pools.go (1)

73-77: Node pool status_last_updated_time index matches search use case; consider partial index if you always filter out deleted rows

The index is well‑placed for sentinel “staleness” queries on status_last_updated_time and follows the existing indexing pattern. If your queries typically include WHERE deleted_at IS NULL alongside this column, a partial index like ON node_pools(status_last_updated_time) WHERE deleted_at IS NULL could shrink index size and improve performance, but it’s purely an optimization, not a blocker.

pkg/db/migrations/202511111044_add_clusters.go (1)

70-75: Clusters status_last_updated_time index is consistent and useful; optional scope tightening

The added index nicely aligns with the sentinel “find stale clusters” pattern and mirrors the node_pools migration. As with node_pools, if your real queries always add deleted_at IS NULL, you might get better performance with a partial index scoped to non‑deleted rows, but the current general index is perfectly valid and safe. Based on learnings, this also keeps spec as JSONB, which is in line with our migration guidelines.

pkg/handlers/validation.go (1)

46-46: Stale comment references old pattern.

The comment still mentions ^[a-z0-9-]+$ but the actual pattern is now RFC 1123-compliant.

-// validateName validates that a name field matches the pattern ^[a-z0-9-]+$ and length constraints
+// validateName validates that a name field matches RFC 1123 DNS subdomain pattern and length constraints
test/factories/node_pools.go (1)

134-159: Consider reusing DB session in combined factory.

NewNodePoolWithStatusAndLabels creates a new DB session on line 147 even though NewNodePoolWithStatus already created one. While this works correctly for tests, you could pass the session through for efficiency:

 func NewNodePoolWithStatusAndLabels(f *Factories, dbFactory db.SessionFactory, id string, phase string, lastUpdatedTime *time.Time, labels map[string]string) (*api.NodePool, error) {
 	nodePool, err := NewNodePoolWithStatus(f, dbFactory, id, phase, lastUpdatedTime)
 	if err != nil {
 		return nil, err
 	}

 	if labels != nil {
+		// Reuse session from factory
 		labelsJSON, err := json.Marshal(labels)

This is a minor optimization and acceptable as-is for test code.

test/integration/search_field_mapping_test.go (5)

3-24: Avoid double RegisterTestingT and consider dropping dot-import

test.RegisterIntegration(t) already calls gomega.RegisterTestingT(t), so the extra RegisterTestingT(t) in each test is redundant and can be removed. Also consider importing Gomega with a named alias instead of . for clearer call sites and to avoid polluting the package namespace.


56-99: Make time-based search test more deterministic

The relative time.Now() offsets are reasonable, but you can make this test more robust and environment-independent by anchoring everything to a fixed time in UTC (e.g. base := time.Date(..., time.UTC)) or calling time.Now().UTC() and formatting with threshold.UTC().Format(time.RFC3339). That avoids any surprises from local timezone configuration.


101-145: Tighten label assertions to guarantee mapping to response

Currently the assertion on item.Labels only runs when item.Labels != nil, so the test would still pass if the server filtered by labels.environment correctly but omitted labels from the response. It would be stronger to assert non-nil labels for the matching cluster and then check the key/value, e.g. Expect(item.Labels).NotTo(BeNil()) before dereferencing (or drop the if guard).


165-227: Consider also asserting label content in combined query test

The combined-conditions test correctly ensures that only the NotReady + us-east cluster is returned and that the other two variants are excluded. To fully validate label mapping here, you could also assert that the returned cluster’s labels include region=us-east, mirroring the label assertions in TestSearchLabelsMapping.


229-285: NodePool mapping tests are good; label assertions could be stricter

The NodePool status.phase mapping is well covered and mirrors the cluster test nicely. For the label-mapping part, you currently only assert that the labeled node pool appears in the results; to ensure response mapping as well as filtering, consider asserting that item.Labels is non-nil and contains environment=test for the matched node pool.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3fa8cb and 1950219.

📒 Files selected for processing (14)
  • openapi/openapi.yaml (2 hunks)
  • pkg/api/resource_id.go (1 hunks)
  • pkg/api/resource_id_test.go (1 hunks)
  • pkg/db/migrations/202511111044_add_clusters.go (1 hunks)
  • pkg/db/migrations/202511111055_add_node_pools.go (2 hunks)
  • pkg/db/sql_helpers.go (5 hunks)
  • pkg/handlers/validation.go (2 hunks)
  • pkg/handlers/validation_test.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (3 hunks)
  • test/factories/clusters.go (2 hunks)
  • test/factories/factory.go (1 hunks)
  • test/factories/node_pools.go (2 hunks)
  • test/integration/search_field_mapping_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/factories/factory.go
  • pkg/api/resource_id_test.go
🧰 Additional context used
📓 Path-based instructions (12)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/handlers/validation.go
  • pkg/services/generic_test.go
  • pkg/db/sql_helpers.go
  • pkg/handlers/validation_test.go
  • pkg/api/resource_id.go
  • test/factories/node_pools.go
  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/services/generic.go
  • test/factories/clusters.go
  • test/integration/search_field_mapping_test.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use PostgreSQL 14.2 with GORM ORM for all database operations

Files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/sql_helpers.go
  • pkg/db/migrations/202511111044_add_clusters.go
pkg/db/migrations/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/db/migrations/**/*.go: Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns
Use polymorphic adapter_statuses table with owner_type and owner_id columns instead of separate status tables per resource type

Files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/migrations/202511111044_add_clusters.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/handlers/validation.go
  • pkg/services/generic_test.go
  • pkg/db/sql_helpers.go
  • pkg/handlers/validation_test.go
  • pkg/api/resource_id.go
  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/services/generic.go
pkg/{handlers,services}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Always retrieve database session from context via db.NewContext() instead of creating new connections

Files:

  • pkg/handlers/validation.go
  • pkg/services/generic_test.go
  • pkg/handlers/validation_test.go
  • pkg/services/generic.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Files:

  • pkg/handlers/validation.go
  • pkg/handlers/validation_test.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/{handlers,services,dao}/**/*.go: Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Files:

  • pkg/handlers/validation.go
  • pkg/services/generic_test.go
  • pkg/handlers/validation_test.go
  • pkg/services/generic.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Files:

  • pkg/api/resource_id.go
test/factories/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use test data factories in test/factories/ to provide consistent test data instead of hardcoding values

Files:

  • test/factories/node_pools.go
  • test/factories/clusters.go
openapi/openapi.yaml

📄 CodeRabbit inference engine (CLAUDE.md)

TypeSpec definitions are maintained separately in hyperfleet-api-spec repository; only edit openapi/openapi.yaml in this repository for spec changes

Files:

  • openapi/openapi.yaml
test/integration/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

test/integration/**/*.go: Create integration tests using Testcontainers for isolated PostgreSQL instances per test
All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Files:

  • test/integration/search_field_mapping_test.go
🧠 Learnings (10)
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns

Applied to files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/services/generic_test.go
  • test/factories/node_pools.go
  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/services/generic.go
  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns instead of separate status tables per resource type

Applied to files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/migrations/202511111044_add_clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/dao/**/*.go : Increment generation field on each spec update to enable optimistic concurrency control

Applied to files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : Create integration tests using Testcontainers for isolated PostgreSQL instances per test

Applied to files:

  • pkg/services/generic_test.go
  • test/integration/search_field_mapping_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Applied to files:

  • pkg/services/generic_test.go
  • test/integration/search_field_mapping_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/factories/**/*.go : Use test data factories in test/factories/ to provide consistent test data instead of hardcoding values

Applied to files:

  • pkg/services/generic_test.go
  • test/factories/node_pools.go
  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for all database operations

Applied to files:

  • pkg/db/sql_helpers.go
  • test/factories/node_pools.go
  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Always retrieve database session from context via db.NewContext() instead of creating new connections

Applied to files:

  • test/factories/node_pools.go
  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Always use the context-based database session from db.NewContext(ctx) instead of creating new connections to participate in HTTP request transactions

Applied to files:

  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use OCM_ENV environment variable to select configuration environment (development, unit_testing, integration_testing, production)

Applied to files:

  • test/factories/clusters.go
🧬 Code graph analysis (6)
pkg/handlers/validation.go (1)
pkg/errors/errors.go (1)
  • Validation (214-216)
pkg/services/generic_test.go (1)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (154-245)
pkg/db/sql_helpers.go (1)
pkg/errors/errors.go (2)
  • ServiceError (100-109)
  • BadRequest (229-231)
test/factories/node_pools.go (4)
pkg/api/node_pool_types.go (1)
  • NodePool (12-40)
test/factories/factory.go (1)
  • Factories (9-10)
pkg/db/session.go (1)
  • SessionFactory (12-20)
pkg/db/migrations/migration_structs.go (1)
  • Model (36-41)
pkg/services/generic.go (1)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (154-245)
test/integration/search_field_mapping_test.go (4)
test/registration.go (1)
  • RegisterIntegration (13-24)
test/factories/clusters.go (3)
  • NewClusterWithStatus (62-86)
  • NewClusterWithLabels (89-112)
  • NewClusterWithStatusAndLabels (115-139)
pkg/api/resource_id.go (1)
  • NewID (14-16)
test/factories/node_pools.go (2)
  • NewNodePoolWithStatus (82-106)
  • NewNodePoolWithLabels (109-132)
🔇 Additional comments (27)
pkg/api/resource_id.go (2)

3-7: LGTM!

Imports are minimal and appropriate for the base32-encoded KSUID generation.


9-16: LGTM!

The implementation correctly encodes KSUID's 20 bytes to a 32-character lowercase string using base32. The resulting IDs are DNS-1123 compliant and maintain KSUID's time-ordering properties.

pkg/db/migrations/202511111055_add_node_pools.go (1)

99-101: Rollback correctly drops the new node_pools index first

Dropping idx_node_pools_status_last_updated_time explicitly and before the table keeps the rollback path clean and idempotent; this is consistent with your other index cleanup logic.

pkg/db/migrations/202511111044_add_clusters.go (1)

80-82: Rollback cleans up the new clusters index in a safe order

Dropping idx_clusters_status_last_updated_time up front with DROP INDEX IF EXISTS is correct and consistent with the rest of the rollback logic. No issues here.

pkg/handlers/validation.go (2)

10-12: LGTM! RFC 1123-compliant name pattern.

The updated regex correctly enforces Kubernetes DNS Subdomain naming: must start and end with alphanumeric characters, allowing hyphens only in the middle.


74-77: LGTM! Error message correctly describes the updated validation rules.

pkg/handlers/validation_test.go (1)

81-83: Good test coverage for RFC 1123 boundary constraints.

These test cases properly validate that names with leading/trailing hyphens are rejected per the updated pattern.

pkg/db/sql_helpers.go (4)

18-30: SQL injection mitigation is properly implemented.

The validation approach correctly prevents SQL injection by allowlisting safe characters before interpolating the key into the JSONB query expression. This addresses the previous review concern.


54-61: LGTM! Status field mappings enable user-friendly search syntax.

The mapping from status.xxx to database column names is clear and complete for the documented status fields.


74-86: LGTM! Labels handling with proper validation before query construction.

The flow correctly validates the label key before constructing the JSONB expression, preventing injection attacks.


15-16: Label key pattern is too restrictive for common label formats.

The pattern ^[a-z0-9_]+$ rejects Kubernetes-style labels with hyphens (e.g., my-label), dots (e.g., app.kubernetes.io/name), and uppercase characters. According to Kubernetes specifications, label names allow alphanumerics, hyphens, underscores, and dots. Consider expanding the pattern to:

-var labelKeyPattern = regexp.MustCompile(`^[a-z0-9_]+$`)
+var labelKeyPattern = regexp.MustCompile(`^[a-zA-Z0-9._\-]+$`)

This supports the full range of valid label key characters while maintaining SQL injection protection. Update the validation error message accordingly.

openapi/openapi.yaml (2)

410-413: Helpful search syntax documentation with examples.

The TSL query examples clearly demonstrate the supported search patterns for status, name filtering, and labels.


718-718: Pattern correctly aligns with backend validation.

The OpenAPI pattern matches the RFC 1123-compliant regex in pkg/handlers/validation.go, ensuring consistent validation at both API spec and runtime levels.

pkg/services/generic.go (3)

41-49: LGTM! Properly initialized disallowed fields for both resource types.

The spec field is correctly marked as non-searchable for both Cluster and NodePool, preventing provider-specific field queries.


171-181: Good architectural change: field mapping before related table detection.

Moving FieldNameWalk earlier in the pipeline correctly prevents status and labels prefixes from being misinterpreted as related table references. The comment clearly explains the rationale.


347-349: Clear documentation of the pipeline change.

The comment appropriately notes why FieldNameWalk is no longer called here, preventing future confusion.

pkg/services/generic_test.go (3)

35-37: Test case correctly validates spec field rejection.

Updated to test the actual disallowed field (spec) rather than a manually injected test field.


59-81: Comprehensive test coverage for field name mappings.

The test cases properly validate:

  • status.phasestatus_phase
  • status.last_updated_timestatus_last_updated_time
  • labels.environmentlabels->>'environment'
  • Direct id queries remain unchanged

92-95: Test flow correctly mirrors production pipeline.

The explicit FieldNameWalk call before treeWalkForSqlizer matches the updated production code flow in buildSearchValues.

test/factories/node_pools.go (2)

75-78: Good helper extraction for reload logic.

The reloadNodePool helper properly encapsulates the reload pattern and returns errors as requested in previous reviews.


80-106: LGTM! Clean factory for status-based node pool creation.

Properly creates the base node pool, updates status fields, and reloads to ensure consistent state.

test/factories/clusters.go (4)

55-58: Good helper extraction for reload logic.

Consistent with node_pools.go, properly encapsulates reload and returns errors.


60-86: LGTM! Factory correctly creates clusters with status fields.

The implementation properly handles optional lastUpdatedTime and ensures the cluster is reloaded after updates.


88-112: LGTM! Labels factory with proper JSON marshaling.

Correctly converts the labels map to JSON before persisting.


114-139: LGTM! Combined factory composes the individual helpers correctly.

The implementation reuses NewClusterWithStatus and adds labels handling when provided. As per coding guidelines, test factories provide consistent test data. This pattern is clear and maintainable.

test/integration/search_field_mapping_test.go (2)

16-54: Cluster status.phase mapping test looks solid

This test cleanly exercises the status.phasestatus_phase mapping, verifies filtering (excludes the Ready cluster), and asserts the enum value. No issues from a correctness standpoint.


147-163: Spec field rejection test is clear and sufficient

This test cleanly validates that spec is not searchable and that the API responds with 400 Bad Request. The behavior under test is straightforward and well covered.

@yasun1 yasun1 force-pushed the search-capabilities branch from 1950219 to 3eadee6 Compare December 10, 2025 11:07
…Base32 ID encoding

update

update

update

update
@yasun1 yasun1 force-pushed the search-capabilities branch from 3eadee6 to 61892d1 Compare December 10, 2025 11:13
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
pkg/db/sql_helpers.go (1)

15-30: Label key validation properly closes the SQL injection hole; charset is intentionally narrow

The validateLabelKey + labelKeyPattern flow now ensures only [a-z0-9_] keys make it into labels->>'%s', which removes the earlier injection risk from interpolating raw user input into the JSONB path. This looks solid and addresses the prior concern from earlier reviews.

The trade‑off is that perfectly valid labels containing characters outside this set (e.g. - or uppercase) will now be rejected at search time with a 400. If that’s acceptable for your API surface, this is fine; if you expect to support a broader label key charset later, you may want to relax the regex (while still keeping it free of quotes and operators).

Also applies to: 74-86

test/factories/node_pools.go (1)

75-79: Reload helper and error propagation look correct and address earlier feedback

The reloadNodePool helper plus the updated reload calls in all three factory functions now check First(...).Error and propagate failures, which fixes the earlier issue where reload errors were silently ignored.

This makes DB issues during tests much more visible and keeps the factories consistent with the analogous cluster helpers.

Also applies to: 101-105, 127-131, 153-155

🧹 Nitpick comments (7)
README.md (2)

11-16: Consider aligning documented Go/Postgres versions with your actual support policy

The README now advertises “Go 1.24 or higher” and “PostgreSQL 13+”. Internally you’re targeting a specific toolchain (Go 1.24.9 with FIPS crypto) and Postgres 14.x for migrations/ORM behavior.

If you truly depend on those baselines, it may be clearer to state them explicitly (e.g. “Go 1.24.9+” and “PostgreSQL 14+”) to avoid users trying to run against older minor versions that haven’t been exercised.

Also applies to: 212-215


184-204: TSL search docs match the implementation; you may want to mention label-key restrictions

The examples for status.phase and status.last_updated_time reflect the new status.* → status_* mappings, and labels.environment matches the labels->>'environment' translation, so the documented queries are consistent with the backend.

Given that the implementation now enforces a strict label key regex ([a-z0-9_]+ after the labels. prefix), it might be helpful to briefly call out that only “simple” lowercase label keys are currently searchable, so users don’t get surprised when querying labels with - or uppercase characters.

Also applies to: 564-570

test/factories/factory.go (1)

3-7: Test ID generation now matches production IDs; consider centralizing the encoder

Using uidEncoding.EncodeToString(ksuid.New().Bytes()) with the custom lowercase Base32 alphabet ensures factory IDs have the same 32‑character, DNS‑1123‑compatible format as the main pkg/api NewID() implementation, which is exactly what you want in tests.

The only downside is that uidAlphabet/uidEncoding are now defined in both pkg/api/resource_id.go and here. If you expect the ID format to evolve, it might be worth centralizing these constants in a small shared internal package so tests and production code can’t drift apart over time.

Also applies to: 12-16, 18-22

test/factories/clusters.go (3)

60-86: Consider accepting a context parameter for flexibility.

The function uses context.Background() when creating the DB session. While acceptable for test factories, accepting a context.Context parameter would provide more flexibility for tests that need specific context values or timeouts.

Example signature:

func NewClusterWithStatus(f *Factories, dbFactory db.SessionFactory, ctx context.Context, id string, phase string, lastUpdatedTime *time.Time) (*api.Cluster, error) {
    // ...
    dbSession := dbFactory.New(ctx)
    // ...
}

If not provided by the caller, context.Background() can be used as a default.


88-112: LGTM with same context parameter suggestion.

The implementation correctly marshals labels to JSON and updates the database. The same recommendation about accepting a context parameter (from the previous comment) applies here for consistency.


114-139: LGTM! Consider session reuse for efficiency.

The function correctly combines status and labels updates. It creates two separate DB sessions (one in NewClusterWithStatus at line 69, another at line 127), which is acceptable for test code but could be optimized by reusing a single session if performance becomes a concern.

test/integration/search_field_mapping_test.go (1)

231-285: LGTM! Consider splitting for better test isolation.

The test correctly verifies that NodePools support the same field mapping as Clusters for both status and labels. The test combines two searches (status.phase and labels.environment) in a single test function. While functional, splitting into separate tests would provide better isolation and clearer failure messages.

Consider splitting into:

  • TestSearchNodePoolStatusMapping
  • TestSearchNodePoolLabelsMapping
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1950219 and 3eadee6.

📒 Files selected for processing (15)
  • README.md (4 hunks)
  • openapi/openapi.yaml (2 hunks)
  • pkg/api/resource_id.go (1 hunks)
  • pkg/api/resource_id_test.go (1 hunks)
  • pkg/db/migrations/202511111044_add_clusters.go (1 hunks)
  • pkg/db/migrations/202511111055_add_node_pools.go (2 hunks)
  • pkg/db/sql_helpers.go (5 hunks)
  • pkg/handlers/validation.go (2 hunks)
  • pkg/handlers/validation_test.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (3 hunks)
  • test/factories/clusters.go (2 hunks)
  • test/factories/factory.go (1 hunks)
  • test/factories/node_pools.go (2 hunks)
  • test/integration/search_field_mapping_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/handlers/validation.go
  • pkg/handlers/validation_test.go
  • pkg/api/resource_id.go
  • pkg/api/resource_id_test.go
  • openapi/openapi.yaml
🧰 Additional context used
📓 Path-based instructions (9)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/db/sql_helpers.go
  • pkg/services/generic_test.go
  • test/factories/node_pools.go
  • pkg/services/generic.go
  • test/factories/factory.go
  • test/integration/search_field_mapping_test.go
  • test/factories/clusters.go
pkg/db/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use PostgreSQL 14.2 with GORM ORM for all database operations

Files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/db/sql_helpers.go
pkg/db/migrations/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/db/migrations/**/*.go: Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns
Use polymorphic adapter_statuses table with owner_type and owner_id columns instead of separate status tables per resource type

Files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/migrations/202511111044_add_clusters.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/db/sql_helpers.go
  • pkg/services/generic_test.go
  • pkg/services/generic.go
pkg/{handlers,services}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Always retrieve database session from context via db.NewContext() instead of creating new connections

Files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/{handlers,services,dao}/**/*.go: Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Files:

  • pkg/services/generic_test.go
  • pkg/services/generic.go
test/factories/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use test data factories in test/factories/ to provide consistent test data instead of hardcoding values

Files:

  • test/factories/node_pools.go
  • test/factories/factory.go
  • test/factories/clusters.go
test/integration/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

test/integration/**/*.go: Create integration tests using Testcontainers for isolated PostgreSQL instances per test
All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Files:

  • test/integration/search_field_mapping_test.go
🧠 Learnings (14)
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns

Applied to files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/services/generic_test.go
  • test/factories/node_pools.go
  • test/factories/clusters.go
  • README.md
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Use polymorphic adapter_statuses table with owner_type and owner_id columns instead of separate status tables per resource type

Applied to files:

  • pkg/db/migrations/202511111055_add_node_pools.go
  • pkg/db/migrations/202511111044_add_clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/dao/**/*.go : Increment generation field on each spec update to enable optimistic concurrency control

Applied to files:

  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/services/generic_test.go
  • README.md
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : Create integration tests using Testcontainers for isolated PostgreSQL instances per test

Applied to files:

  • pkg/services/generic_test.go
  • test/integration/search_field_mapping_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Applied to files:

  • pkg/services/generic_test.go
  • test/integration/search_field_mapping_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/factories/**/*.go : Use test data factories in test/factories/ to provide consistent test data instead of hardcoding values

Applied to files:

  • test/factories/node_pools.go
  • test/factories/factory.go
  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for all database operations

Applied to files:

  • test/factories/node_pools.go
  • test/factories/clusters.go
  • README.md
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Always retrieve database session from context via db.NewContext() instead of creating new connections

Applied to files:

  • test/factories/node_pools.go
  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Always use the context-based database session from db.NewContext(ctx) instead of creating new connections to participate in HTTP request transactions

Applied to files:

  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use OCM_ENV environment variable to select configuration environment (development, unit_testing, integration_testing, production)

Applied to files:

  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • README.md
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/**/*.go : Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Applied to files:

  • README.md
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/openapi/**/*.go : Use make generate to regenerate Go models from openapi/openapi.yaml via openapi-generator-cli v7.16.0 in Podman

Applied to files:

  • README.md
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to openapi/openapi.yaml : TypeSpec definitions are maintained separately in hyperfleet-api-spec repository; only edit openapi/openapi.yaml in this repository for spec changes

Applied to files:

  • README.md
🧬 Code graph analysis (5)
pkg/db/sql_helpers.go (1)
pkg/errors/errors.go (2)
  • ServiceError (100-109)
  • BadRequest (229-231)
pkg/services/generic.go (1)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (154-245)
test/factories/factory.go (1)
pkg/api/resource_id.go (1)
  • NewID (14-16)
test/integration/search_field_mapping_test.go (5)
test/registration.go (1)
  • RegisterIntegration (13-24)
test/factories/clusters.go (3)
  • NewClusterWithStatus (62-86)
  • NewClusterWithLabels (89-112)
  • NewClusterWithStatusAndLabels (115-139)
test/factories/factory.go (1)
  • Factories (9-10)
pkg/api/resource_id.go (1)
  • NewID (14-16)
test/factories/node_pools.go (2)
  • NewNodePoolWithStatus (82-106)
  • NewNodePoolWithLabels (109-132)
test/factories/clusters.go (4)
pkg/api/cluster_types.go (1)
  • Cluster (11-34)
test/factories/factory.go (1)
  • Factories (9-10)
pkg/db/session.go (1)
  • SessionFactory (12-20)
pkg/db/migrations/migration_structs.go (1)
  • Model (36-41)
🔇 Additional comments (14)
pkg/db/sql_helpers.go (1)

54-61: status. mapping and disallowed-field handling are coherent with search semantics*

The statusFieldMappings map and the early status.xxx → status_xxx rewrite in getField ensure that user-friendly queries like status.phase and status.last_updated_time are consistently mapped to the flattened columns before the disallowed-field check, which is exactly what the migrations/indexes expect.

Also worth noting: labels.* short‑circuits before the disallowed‑field logic, while status.* flows through it via the mapped name. That matches the current SearchDisallowedFields contents (only spec is blocked) but is something to keep in mind if you ever decide certain label keys should be non‑searchable.

Also applies to: 88-92, 104-108

pkg/services/generic.go (2)

41-49: SearchDisallowedFields now cleanly expresses spec-only blocking

Restricting the disallowed map to just "spec" for Cluster and NodePool, with allFieldsAllowed as the default, makes the behavior explicit and easy to extend per resource type later. The newListContext fallback logic to allFieldsAllowed keeps other resource types fully searchable by default.


171-176: FieldNameWalk-before-related-tables ordering is correct and avoids misclassification

Running db.FieldNameWalk immediately after parsing the TSL tree (and before treeWalkForRelatedTables and treeWalkForAddingTableName) is the right call: it ensures status.* and labels.* are normalized to their SQL forms before any related-table detection kicks in, so status/labels aren’t accidentally treated as relation prefixes.

The corresponding comment in treeWalkForSqlizer makes it clear that field mapping is a prior step, which should help prevent future duplication of FieldNameWalk in that stage.

Also applies to: 178-181, 347-349

pkg/db/migrations/202511111055_add_node_pools.go (1)

73-77: status_last_updated_time index for node_pools is well-scoped and rollback-safe

The idx_node_pools_status_last_updated_time index on status_last_updated_time matches the search pattern described in comments (sentinel queries for stale resources), and the IF NOT EXISTS plus explicit drop in rollback keeps the migration idempotent and reversible. This is consistent with the clusters migration and looks good.

Also applies to: 99-101

pkg/db/migrations/202511111044_add_clusters.go (1)

70-74: Clusters status_last_updated_time index mirrors node_pools strategy appropriately

The idx_clusters_status_last_updated_time index complements the node_pools index and matches the flattened status_last_updated_time column used by the new search mappings. Rollback ordering is correct (drop index before table), so this migration is safe and consistent.

Also applies to: 80-82

pkg/services/generic_test.go (1)

35-37: Tests accurately cover disallowed spec and new field mappings

The updated bad-input case (spec = '{}') now correctly asserts the spec is not a valid field name error, matching the SearchDisallowedFields behavior.

The additional SQL translation tests for:

  • status.phasestatus_phase = ?
  • status.last_updated_timestatus_last_updated_time < ?
  • labels.environmentlabels->>'environment' = ?
  • idid = ?

combined with the explicit FieldNameWalk call before treeWalkForSqlizer, give good coverage of the new mapping pipeline. This is a solid, focused test expansion.

Also applies to: 59-81, 92-95

test/factories/node_pools.go (1)

80-106: New NodePool status/label factories are well-structured for integration tests

NewNodePoolWithStatus, NewNodePoolWithLabels, and NewNodePoolWithStatusAndLabels build on NewNodePool, perform focused updates (status fields and/or labels JSON), and then reload via the shared helper. This keeps test setup concise while ensuring you always exercise the real DB schema and GORM mapping.

Using a db.SessionFactory argument instead of constructing sessions internally is also a nice touch for test control.

Also applies to: 108-132, 134-159

test/factories/clusters.go (2)

5-12: LGTM!

The new imports support JSON marshaling of labels, timestamp handling, GORM DB operations, and the db package for session management. All imports are appropriate for the new factory functionality.


55-58: LGTM!

The reloadCluster helper consolidates the reload pattern and properly handles errors, addressing the previous review comments about unchecked dbSession.First calls.

test/integration/search_field_mapping_test.go (5)

18-54: LGTM!

The test correctly verifies that status.phase user-friendly syntax maps to the status_phase database field. It includes both positive validation (NotReady cluster found) and negative validation (Ready cluster not found), following integration testing best practices.

Based on coding guidelines, integration tests use Testcontainers for isolated PostgreSQL instances.


59-99: LGTM!

The test correctly verifies time-based filtering with status.last_updated_time mapping. The test uses sufficient time margins (2 hours vs 30 minutes with a 1-hour threshold) to prevent flakiness from timestamp precision issues.


103-145: LGTM!

The test correctly verifies JSONB label mapping with labels.xxx syntax. Good defensive programming with the nil check for item.Labels at line 137 before asserting on the map contents.


149-163: LGTM!

The test correctly verifies that attempts to query the spec field are rejected with a 400 Bad Request, which is important for preventing arbitrary JSONB queries and maintaining search field constraints.


167-227: LGTM!

The test provides excellent coverage of combined AND queries, verifying that both status and labels field mappings work correctly together. It includes comprehensive negative validation by ensuring clusters that match only one condition are properly excluded.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (9)
pkg/handlers/validation.go (1)

10-12: Align validation comments with the new DNS‑1123 label regex

The behavior of namePattern and the error message is correct, but the comments are now misleading:

  • Line 10–11: the pattern is for a single DNS‑1123 label (no dots), while the comment says “DNS Subdomain Names”; consider clarifying that it matches a label suitable for use as a DNS‑1123 subdomain component.
  • Line 46: still references the old pattern ^[a-z0-9-]+$; update this to describe the current ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ rule (start/end alnum, hyphens only in the middle).

This is purely documentation, but tightening it will avoid future confusion when someone cross‑checks the regex and tests.

Also applies to: 46-47, 74-77

README.md (1)

11-11: Search docs look good; consider clarifying supported operators/fields with an in example

The new TSL search documentation and examples are clear and align with the implemented field mapping (name, status.phase, labels.<key>). Two optional tweaks that might help users:

  • Add a concrete in operator example (e.g., labels.env in ('dev','staging')) alongside the AND/OR examples so all listed operators are demonstrated.
  • Optionally call out that only the documented fields are searchable (e.g., spec is intentionally not supported) to avoid users guessing unsupported fields.

These are small doc improvements; behavior and examples are otherwise consistent and easy to follow.

Also applies to: 184-201, 561-567

test/factories/factory.go (1)

3-7: Avoid duplicating ID encoding logic; delegate to api.NewID

The KSUID+Base32 logic here mirrors pkg/api/resource_id.go, which is good for now but risks drift if the production ID format changes.

Given factories already depend on pkg/api types, you can simplify and guarantee consistency by delegating:

-import (
-	"encoding/base32"
-
-	"github.com/segmentio/ksuid"
-)
+import (
+	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/api"
+)
@@
-// NewID generates a new unique identifier using KSUID with lowercase Base32 encoding.
-// The resulting identifier is compatible with Kubernetes DNS-1123 subdomain naming requirements.
-func (f *Factories) NewID() string {
-	return uidEncoding.EncodeToString(ksuid.New().Bytes())
-}
-
-// uidAlphabet is the lowercase alphabet used to encode unique identifiers.
-const uidAlphabet = "0123456789abcdefghijklmnopqrstuv"
-
-// uidEncoding is the lowercase variant of Base32 used to encode unique identifiers.
-var uidEncoding = base32.NewEncoding(uidAlphabet).WithPadding(base32.NoPadding)
+// NewID delegates to the production API ID generator so tests use the same format.
+func (f *Factories) NewID() string {
+	return api.NewID()
+}

This keeps test IDs forever aligned with the runtime behavior and removes redundant encoding constants here.

Also applies to: 12-16, 18-22

pkg/api/resource_id_test.go (1)

8-30: Solid NewID tests; consider precompiling regexes to avoid duplication and overhead

The test coverage for NewID (length, charset, uniqueness, and DNS‑1123 compatibility) looks very good.

Two optional refinements:

  • In TestNewID, regexp.MustCompile("^[0-9a-v]{32}$") is called inside the loop; consider compiling it once (either at package scope or at the start of the test) and reusing it.
  • The DNS‑1123 pattern in TestNewID_K8sCompatible duplicates the validation regex used in the HTTP layer; if you ever expose that regex as a shared helper/const, using it here would reduce the risk of the tests silently diverging from the actual validation rule.

These are small test‑hygiene improvements; the current tests are already functionally strong.

Also applies to: 32-51, 53-67

pkg/api/resource_id.go (1)

3-7: NewID implementation matches requirements; use it as the single source of truth

The KSUID+lowercase Base32 implementation here cleanly satisfies the 32‑char, [0-9a-v] charset and DNS‑1123‑compatible requirements described in the comments and tests.

Given this is the canonical ID generator:

  • It’s a good idea for other code (notably test factories) to delegate to api.NewID() rather than duplicating uidAlphabet/uidEncoding logic elsewhere. That keeps all callers aligned if the encoding ever needs to change.
  • Keeping uidAlphabet/uidEncoding unexported here is fine; callers only need NewID().

No functional issues found; just recommend making this the single source of truth for ID generation across the codebase.

Also applies to: 9-22

test/factories/node_pools.go (1)

5-8: Node pool factory helpers correctly update DB state and handle reload errors

The new helpers look well‑structured:

  • reloadNodePool centralizes the reload logic and now properly checks First(...).Error, addressing the earlier concern about ignored reload errors.
  • NewNodePoolWithStatus and NewNodePoolWithStatusAndLabels update status_phase and optional status_last_updated_time in the DB, then reload, which ensures tests see the same shape as real persisted data.
  • NewNodePoolWithLabels mirrors the same pattern for labels and correctly propagates JSON marshal and DB errors.

One small nuance: if NewNodePoolWithLabels is ever called with labels == nil, it will store JSON null in the column rather than an empty object; if that matters for any tests, you could special‑case nil to write {} instead. Otherwise this looks solid for factory usage.

Also applies to: 13-15, 75-79, 80-106, 108-132, 134-159

test/factories/clusters.go (2)

114-139: Minor optimization opportunity: redundant reload when labels are provided.

When labels != nil, the cluster is reloaded twice: once inside NewClusterWithStatus and again after updating labels. For test factories this is acceptable, but could be optimized by extracting an internal helper that skips the intermediate reload.


62-62: Consider API consistency with existing factory methods.

The new functions are standalone (factories.NewClusterWithStatus(f, ...)) while existing functions are methods (f.NewCluster(...)). This inconsistency exists because dbFactory is needed. Consider adding db.SessionFactory to the Factories struct to enable a consistent method-based API, or document this design choice.

test/integration/search_field_mapping_test.go (1)

231-285: Consider splitting into separate tests for status and labels.

This test validates two distinct behaviors (status.phase mapping and labels mapping) for NodePools. While functional, splitting into TestSearchNodePoolStatusPhaseMapping and TestSearchNodePoolLabelsMapping would provide better test isolation and clearer failure diagnostics.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3eadee6 and 61892d1.

📒 Files selected for processing (15)
  • README.md (4 hunks)
  • openapi/openapi.yaml (2 hunks)
  • pkg/api/resource_id.go (1 hunks)
  • pkg/api/resource_id_test.go (1 hunks)
  • pkg/db/migrations/202511111044_add_clusters.go (1 hunks)
  • pkg/db/migrations/202511111055_add_node_pools.go (2 hunks)
  • pkg/db/sql_helpers.go (5 hunks)
  • pkg/handlers/validation.go (2 hunks)
  • pkg/handlers/validation_test.go (1 hunks)
  • pkg/services/generic.go (3 hunks)
  • pkg/services/generic_test.go (3 hunks)
  • test/factories/clusters.go (2 hunks)
  • test/factories/factory.go (1 hunks)
  • test/factories/node_pools.go (2 hunks)
  • test/integration/search_field_mapping_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/db/sql_helpers.go
  • pkg/db/migrations/202511111044_add_clusters.go
  • pkg/db/migrations/202511111055_add_node_pools.go
  • openapi/openapi.yaml
  • pkg/services/generic_test.go
🧰 Additional context used
📓 Path-based instructions (9)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Files:

  • pkg/handlers/validation_test.go
  • pkg/services/generic.go
  • test/factories/clusters.go
  • pkg/handlers/validation.go
  • test/factories/factory.go
  • pkg/api/resource_id.go
  • test/factories/node_pools.go
  • pkg/api/resource_id_test.go
  • test/integration/search_field_mapping_test.go
pkg/{handlers,services}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Always retrieve database session from context via db.NewContext() instead of creating new connections

Files:

  • pkg/handlers/validation_test.go
  • pkg/services/generic.go
  • pkg/handlers/validation.go
pkg/handlers/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all HTTP handlers following the pattern: parse request → call service/DAO → handle errors → send response

Files:

  • pkg/handlers/validation_test.go
  • pkg/handlers/validation.go
pkg/{handlers,services,dao}/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/{handlers,services,dao}/**/*.go: Use structured logging via pkg/logger/logger.go with context propagation including opid, accountID, and tx_id
Return errors using the structured ServiceError type from pkg/errors/ with HttpCode, Code, and Reason fields

Files:

  • pkg/handlers/validation_test.go
  • pkg/services/generic.go
  • pkg/handlers/validation.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement JWT authentication middleware that validates tokens and extracts account ID and username from claims

Files:

  • pkg/handlers/validation_test.go
  • pkg/services/generic.go
  • pkg/handlers/validation.go
  • pkg/api/resource_id.go
  • pkg/api/resource_id_test.go
pkg/services/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement adapter status aggregation: phase is Ready if all adapters report Ready=True, Failed if any report Ready=False, NotReady otherwise

Files:

  • pkg/services/generic.go
test/factories/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use test data factories in test/factories/ to provide consistent test data instead of hardcoding values

Files:

  • test/factories/clusters.go
  • test/factories/factory.go
  • test/factories/node_pools.go
pkg/api/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Files:

  • pkg/api/resource_id.go
  • pkg/api/resource_id_test.go
test/integration/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

test/integration/**/*.go: Create integration tests using Testcontainers for isolated PostgreSQL instances per test
All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Files:

  • test/integration/search_field_mapping_test.go
🧠 Learnings (13)
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/factories/**/*.go : Use test data factories in test/factories/ to provide consistent test data instead of hardcoding values

Applied to files:

  • test/factories/clusters.go
  • test/factories/factory.go
  • test/factories/node_pools.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/**/*.go : Use PostgreSQL 14.2 with GORM ORM for all database operations

Applied to files:

  • test/factories/clusters.go
  • README.md
  • test/factories/node_pools.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/{handlers,services}/**/*.go : Always retrieve database session from context via db.NewContext() instead of creating new connections

Applied to files:

  • test/factories/clusters.go
  • test/factories/node_pools.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/db/migrations/**/*.go : Store cluster and nodepool specifications as JSONB in PostgreSQL schema, never as separate columns

Applied to files:

  • test/factories/clusters.go
  • README.md
  • test/factories/node_pools.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Always use the context-based database session from db.NewContext(ctx) instead of creating new connections to participate in HTTP request transactions

Applied to files:

  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to cmd/hyperfleet/environments/**/*.go : Use OCM_ENV environment variable to select configuration environment (development, unit_testing, integration_testing, production)

Applied to files:

  • test/factories/clusters.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to **/*.go : Use Go 1.24.9 with FIPS-compliant crypto enabled (CGO_ENABLED=1, GOEXPERIMENT=boringcrypto)

Applied to files:

  • README.md
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/**/*.go : Use //go:embed directive to embed the OpenAPI specification at compile time from pkg/api/openapi/api/openapi.yaml

Applied to files:

  • README.md
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/api/openapi/**/*.go : Use make generate to regenerate Go models from openapi/openapi.yaml via openapi-generator-cli v7.16.0 in Podman

Applied to files:

  • README.md
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to pkg/dao/**/*.go : Increment generation field on each spec update to enable optimistic concurrency control

Applied to files:

  • README.md
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to openapi/openapi.yaml : TypeSpec definitions are maintained separately in hyperfleet-api-spec repository; only edit openapi/openapi.yaml in this repository for spec changes

Applied to files:

  • README.md
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : All API endpoints must be integrated tested in test/integration/ covering CRUD operations, pagination, search, and error cases

Applied to files:

  • pkg/api/resource_id_test.go
  • test/integration/search_field_mapping_test.go
📚 Learning: 2025-12-03T15:47:39.989Z
Learnt from: CR
Repo: openshift-hyperfleet/hyperfleet-api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-03T15:47:39.989Z
Learning: Applies to test/integration/**/*.go : Create integration tests using Testcontainers for isolated PostgreSQL instances per test

Applied to files:

  • pkg/api/resource_id_test.go
  • test/integration/search_field_mapping_test.go
🧬 Code graph analysis (5)
pkg/services/generic.go (1)
pkg/db/sql_helpers.go (1)
  • FieldNameWalk (154-245)
test/factories/clusters.go (4)
pkg/api/cluster_types.go (1)
  • Cluster (11-34)
test/factories/factory.go (1)
  • Factories (9-10)
pkg/db/session.go (1)
  • SessionFactory (12-20)
pkg/db/migrations/migration_structs.go (1)
  • Model (36-41)
pkg/handlers/validation.go (1)
pkg/errors/errors.go (1)
  • Validation (214-216)
test/factories/node_pools.go (4)
pkg/api/node_pool_types.go (1)
  • NodePool (12-40)
test/factories/factory.go (1)
  • Factories (9-10)
pkg/db/session.go (1)
  • SessionFactory (12-20)
pkg/db/migrations/migration_structs.go (1)
  • Model (36-41)
pkg/api/resource_id_test.go (1)
pkg/api/resource_id.go (1)
  • NewID (14-16)
🔇 Additional comments (11)
pkg/handlers/validation_test.go (1)

72-94: Good coverage for boundary hyphen cases

The added invalidNames ("-test", "test-", "-test-") correctly exercise the new “must start and end alphanumeric” rule and keep the assertion tied to the updated error message substring. This nicely closes the gap on common DNS‑1123 boundary mistakes.

pkg/services/generic.go (1)

41-50: Field mapping ordering and disallowed spec fields look correct

The refactor here tightens the search pipeline nicely:

  • Centralizing SearchDisallowedFields per resource type and defaulting to allFieldsAllowed makes it explicit that spec is intentionally non‑searchable/non‑sortable for both Cluster and NodePool.
  • Running db.FieldNameWalk before treeWalkForRelatedTables is the right fix to keep status.* and labels.* from being treated as related resources while still allowing true related fields (e.g., creator.username) to be resolved by treeWalkForRelatedTables.
  • Updating treeWalkForSqlizer to assume the mapping is already applied keeps responsibilities clear and avoids double‑walking the tree.

Given the existing tests around field mapping and search, this ordering change looks sound. No functional issues spotted.

Also applies to: 67-88, 160-201, 347-355

test/factories/clusters.go (3)

55-58: LGTM! Clean helper extraction.

The reloadCluster helper nicely consolidates the reload logic across all factory functions, addressing the past review feedback about code duplication.


60-86: LGTM! Well-structured status update factory.

The conditional update logic for lastUpdatedTime and proper error handling look correct. Based on learnings, this factory provides consistent test data.


88-112: LGTM! Clean labels factory implementation.

JSON marshaling and database update are properly error-handled.

test/integration/search_field_mapping_test.go (6)

1-14: LGTM! Appropriate imports for integration testing.

The use of gomega with dot import follows common BDD-style testing patterns. As per coding guidelines, this test uses factories for consistent test data.


18-54: LGTM! Comprehensive status phase mapping test.

The test correctly verifies both positive (NotReady cluster found) and negative (Ready cluster excluded) conditions for the status.phase field mapping.


59-99: LGTM! Good coverage of time-based filtering.

The test properly validates timestamp comparison using RFC3339 format and verifies correct filtering behavior for status.last_updated_time.


103-145: LGTM! Well-structured labels mapping test.

The defensive nil check on item.Labels (line 137) and the verification of both cluster inclusion and exclusion provide good coverage.


149-163: LGTM! Important negative test case.

Testing that spec field queries are rejected with 400 Bad Request validates the field access control mechanism.


167-227: LGTM! Thorough combined query test.

The test matrix (matching, wrong region, wrong status) provides excellent coverage for verifying AND query behavior with both status and label conditions.

@rh-amarin
Copy link
Contributor

/lgtm

@openshift-ci
Copy link

openshift-ci bot commented Dec 10, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: rh-amarin, yasun1

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-merge-bot openshift-merge-bot bot merged commit 2ee9190 into openshift-hyperfleet:main Dec 10, 2025
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants