Skip to content

chore: init go events#641

Merged
j7m4 merged 5 commits intomainfrom
go-events
Aug 5, 2025
Merged

chore: init go events#641
j7m4 merged 5 commits intomainfrom
go-events

Conversation

@adityachoudhari26
Copy link
Copy Markdown
Member

@adityachoudhari26 adityachoudhari26 commented Aug 4, 2025

Summary by CodeRabbit

  • New Features
    • Introduced foundational event structures for workspace resources, enabling representation of resource creation, update, and deletion events with associated metadata and payloads.
    • Added detailed resource data structures to capture resource state, including support for tracking changes between previous and current states.
    • Implemented an event processing framework that registers and dispatches handlers for different event types, enhancing event handling and logging capabilities.
    • Integrated event processing into the Kafka consumer to handle incoming events dynamically.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Aug 4, 2025

Walkthrough

A new events package was added, defining core event types, base event structures, and resource lifecycle event types with handlers for creation, update, and deletion. The Kafka consumer was updated to use this package for event unmarshaling and processing. A utility function for payload parsing was introduced to support JSON serialization and deserialization.

Changes

Cohort / File(s) Change Summary
Core Event Types and Processing
apps/workspace-engine/pkg/events/events.go
Added foundational event types: EventType string with constants for resource lifecycle events, BaseEvent and RawEvent structs for event metadata and payloads. Introduced EventHandler type, handler registry, and MessageReader struct with methods to register and dispatch event handlers by event type, including logging and error handling for unknown event types and unmarshaling failures.
Resource Event Types and Handlers
apps/workspace-engine/pkg/events/resource.go
Defined Resource struct representing workspace resources with metadata and timestamps. Added event structs for resource lifecycle events (ResourceCreatedEvent, ResourceUpdatedEvent, ResourceDeletedEvent) embedding BaseEvent and carrying appropriate payloads. Implemented handler functions for each event type that parse payloads, log events, and handle errors.
Payload Parsing Utility
apps/workspace-engine/pkg/events/common.go
Added parsePayload function that marshals an arbitrary payload to JSON and unmarshals it into a target structure, returning detailed errors on failure, to facilitate safe and consistent event payload decoding.
Kafka Consumer Integration
apps/workspace-engine/pkg/kafka/consumer.go
Removed local Event struct and replaced event unmarshaling with usage of the events.MessageReader to handle message reading and dispatching. Updated the consumer loop to pass Kafka messages to the MessageReader.ReadMessage method, logging errors and continuing on failure, while preserving existing Kafka consumption control flow. Removed direct JSON unmarshaling and logging of events in the consumer.

Sequence Diagram(s)

sequenceDiagram
    participant Kafka
    participant Consumer
    participant MessageReader
    participant EventHandler

    Kafka->>Consumer: Receive Kafka message
    Consumer->>MessageReader: ReadMessage(ctx, msg)
    MessageReader->>EventHandler: Dispatch handler based on EventType
    EventHandler->>MessageReader: Return error or nil
    MessageReader->>Consumer: Return error or nil
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • init kafka consumer #642: Refactors Kafka consumer to use the new events package for event types and processing, replacing the local event struct and adding event handler dispatching, directly building upon the initial consumer implementation.

Poem

🐇 Hopping through code with a joyful cheer,
Events now have handlers, so crystal clear.
Resources born, changed, or laid to rest,
Each event processed with care and zest.
A rabbit’s delight in structured flow,
“Review these changes, and watch us grow!” 🌿✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch go-events

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@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 (2)
apps/workspace-engine/pkg/events/resource.go (2)

5-18: Well-designed Resource struct with minor type preference.

The Resource struct is comprehensive and well-structured. All fields have appropriate types and JSON tags.

Consider using any instead of interface{} for the Config field to align with Go 1.18+ conventions:

-	Config      map[string]interface{} `json:"config"`
+	Config      map[string]any         `json:"config"`

25-31: Consider extracting the payload struct for better reusability.

The inline struct for the payload works but could be extracted to improve code organization and potential reusability.

Consider extracting the payload struct:

+type ResourceUpdatePayload struct {
+	Current  Resource `json:"current"`
+	Previous Resource `json:"previous"`
+}
+
 type ResourceUpdatedEvent struct {
 	BaseEvent
-	Payload struct {
-		Current  Resource `json:"current"`
-		Previous Resource `json:"previous"`
-	}
+	Payload ResourceUpdatePayload `json:"payload"`
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb5b837 and 18960da.

📒 Files selected for processing (2)
  • apps/workspace-engine/pkg/events/events.go (1 hunks)
  • apps/workspace-engine/pkg/events/resource.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
PR: ctrlplanedev/ctrlplane#0
File: apps/selector-engine/CLAUDE.md:0-0
Timestamp: 2025-08-04T17:39:09.108Z
Learning: Applies to apps/selector-engine/pkg/model/selector/types.go : When adding a new condition type: add the condition type to 'types.go' in 'pkg/model/selector/'.
Learnt from: CR
PR: ctrlplanedev/ctrlplane#0
File: apps/selector-engine/CLAUDE.md:0-0
Timestamp: 2025-08-04T17:39:09.108Z
Learning: Applies to apps/selector-engine/pkg/model/selector/*.go : When adding a new condition type: create a new condition struct in 'pkg/model/selector/' implementing the 'Condition' interface.
Learnt from: CR
PR: ctrlplanedev/ctrlplane#0
File: apps/selector-engine/CLAUDE.md:0-0
Timestamp: 2025-08-04T17:39:09.108Z
Learning: Applies to apps/selector-engine/pkg/mapping/protobuf_mappings.go : When adding a new condition type: add mapping functions in 'pkg/mapping/protobuf_mappings.go'.
Learnt from: CR
PR: ctrlplanedev/ctrlplane#0
File: apps/selector-engine/CLAUDE.md:0-0
Timestamp: 2025-08-04T17:39:09.108Z
Learning: Applies to apps/selector-engine/**/*_test.go : Follow the existing test structure in '*_test.go' files.
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#637
File: packages/events/src/kafka/client.ts:10-16
Timestamp: 2025-08-01T04:41:41.345Z
Learning: User adityachoudhari26 prefers not to add null safety checks for required environment variables when they are guaranteed to be present in their deployment configuration, similar to their preference for simplicity over defensive programming in test code.
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#601
File: e2e/tests/api/policies/retry-policy.spec.ts:23-24
Timestamp: 2025-06-24T23:52:50.732Z
Learning: The user adityachoudhari26 prefers not to add null safety checks or defensive programming in test code, particularly in e2e tests, as they prioritize simplicity and focus on the main functionality being tested rather than comprehensive error handling within the test itself.
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#601
File: e2e/tests/api/policies/retry-policy.spec.ts:117-130
Timestamp: 2025-06-24T23:53:25.398Z
Learning: User adityachoudhari26 prefers to keep non-null assertions in e2e test code without extensive null safety checks, reasoning that test failures serve the same purpose of catching issues and the extra validation doesn't add much value in test contexts.
🔇 Additional comments (4)
apps/workspace-engine/pkg/events/events.go (2)

10-16: LGTM! Well-structured base event design.

The BaseEvent struct provides a solid foundation with all necessary metadata fields. The use of float64 for timestamp aligns with common JavaScript/JSON timestamp representations, and the JSON tags are properly configured.


18-21: LGTM! Flexible raw event structure.

The RawEvent struct effectively extends BaseEvent with a flexible payload using map[string]any, which is appropriate for handling arbitrary event data. The omitempty tag is correctly applied.

apps/workspace-engine/pkg/events/resource.go (2)

20-23: LGTM! Clean resource created event structure.

The ResourceCreatedEvent follows the established pattern by embedding BaseEvent and carrying a single Resource payload. The design is consistent and appropriate.


33-36: LGTM! Consistent resource deleted event structure.

The ResourceDeletedEvent maintains consistency with the ResourceCreatedEvent pattern, which is appropriate for the event system design.

Copy link
Copy Markdown
Contributor

@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 (2)
apps/workspace-engine/pkg/events/common.go (1)

8-19: Consider using generics for better type safety and performance.

The current implementation uses double JSON encoding/decoding which is inefficient and loses compile-time type safety. Consider using Go generics for a more performant and type-safe approach.

-func parsePayload(payload any, target any) error {
-	payloadBytes, err := json.Marshal(payload)
-	if err != nil {
-		return fmt.Errorf("failed to marshal payload: %w", err)
-	}
-
-	if err := json.Unmarshal(payloadBytes, target); err != nil {
-		return fmt.Errorf("failed to unmarshal payload: %w", err)
-	}
-
-	return nil
-}
+func parsePayload[T any](payload any, target *T) error {
+	payloadBytes, err := json.Marshal(payload)
+	if err != nil {
+		return fmt.Errorf("failed to marshal payload: %w", err)
+	}
+
+	if err := json.Unmarshal(payloadBytes, target); err != nil {
+		return fmt.Errorf("failed to unmarshal payload: %w", err)
+	}
+
+	return nil
+}

This approach provides compile-time type safety and makes the target parameter clearly a pointer type.

apps/workspace-engine/pkg/kafka/consumer.go (1)

77-77: Consider reducing log verbosity for large events.

Logging the entire rawEvent structure might be verbose for large payloads. Consider logging only essential fields like event type and ID.

-		log.Info("Received event", "event", rawEvent)
+		log.Info("Received event", "type", rawEvent.Type, "timestamp", rawEvent.Timestamp)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18960da and ebafc11.

📒 Files selected for processing (4)
  • apps/workspace-engine/pkg/events/common.go (1 hunks)
  • apps/workspace-engine/pkg/events/events.go (1 hunks)
  • apps/workspace-engine/pkg/events/resource.go (1 hunks)
  • apps/workspace-engine/pkg/kafka/consumer.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/workspace-engine/pkg/events/events.go
  • apps/workspace-engine/pkg/events/resource.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#637
File: packages/events/src/kafka/client.ts:10-16
Timestamp: 2025-08-01T04:41:41.345Z
Learning: User adityachoudhari26 prefers not to add null safety checks for required environment variables when they are guaranteed to be present in their deployment configuration, similar to their preference for simplicity over defensive programming in test code.
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#601
File: e2e/tests/api/policies/retry-policy.spec.ts:23-24
Timestamp: 2025-06-24T23:52:50.732Z
Learning: The user adityachoudhari26 prefers not to add null safety checks or defensive programming in test code, particularly in e2e tests, as they prioritize simplicity and focus on the main functionality being tested rather than comprehensive error handling within the test itself.
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#601
File: e2e/tests/api/policies/retry-policy.spec.ts:117-130
Timestamp: 2025-06-24T23:53:25.398Z
Learning: User adityachoudhari26 prefers to keep non-null assertions in e2e test code without extensive null safety checks, reasoning that test failures serve the same purpose of catching issues and the extra validation doesn't add much value in test contexts.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
🔇 Additional comments (3)
apps/workspace-engine/pkg/kafka/consumer.go (3)

50-50: LGTM! Clean integration of the events package.

Good approach creating the EventProcessor once and reusing it throughout the consumer lifecycle.


70-71: LGTM! Proper use of the events package types.

The switch from local Event struct to events.RawEvent improves code organization and reusability.


79-82: LGTM! Appropriate error handling for event processing.

The error handling correctly logs the failure and continues processing the next message, which is the right approach for a message queue consumer.

Copy link
Copy Markdown
Contributor

@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 (2)
apps/workspace-engine/pkg/events/events.go (2)

22-33: Consider adding validation for required BaseEvent fields

The event structures are well-designed. However, consider adding a validation method to ensure required fields like EventType, WorkspaceID, and EventID are not empty when processing events.

Add a validation method:

func (e BaseEvent) Validate() error {
    if e.EventType == "" {
        return fmt.Errorf("eventType is required")
    }
    if e.WorkspaceID == "" {
        return fmt.Errorf("workspaceId is required")
    }
    if e.EventID == "" {
        return fmt.Errorf("eventId is required")
    }
    return nil
}

54-66: Consider adding logging for better observability

The message processing logic is correct. Consider adding debug/info logging to track event processing for better observability in production.

Add logging statements:

 func (p *MessageReader) ReadMessage(ctx context.Context, msg *kafka.Message) error {
 	var rawEvent RawEvent
 	if err := json.Unmarshal(msg.Value, &rawEvent); err != nil {
+		p.log.Error("failed to unmarshal event", "error", err)
 		return fmt.Errorf("failed to unmarshal event: %w", err)
 	}
 
+	p.log.Debug("processing event", "type", rawEvent.EventType, "eventId", rawEvent.EventID)
+
 	handler, ok := p.handlers[rawEvent.EventType]
 	if !ok {
+		p.log.Warn("unknown event type", "type", rawEvent.EventType)
 		return fmt.Errorf("no handler found for event type: %s", rawEvent.EventType)
 	}
 
-	return handler(ctx, rawEvent)
+	if err := handler(ctx, rawEvent); err != nil {
+		p.log.Error("handler failed", "type", rawEvent.EventType, "error", err)
+		return err
+	}
+	return nil
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebafc11 and 92665fa.

📒 Files selected for processing (2)
  • apps/workspace-engine/pkg/events/events.go (1 hunks)
  • apps/workspace-engine/pkg/kafka/consumer.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/workspace-engine/pkg/kafka/consumer.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#637
File: packages/events/src/kafka/client.ts:10-16
Timestamp: 2025-08-01T04:41:41.345Z
Learning: User adityachoudhari26 prefers not to add null safety checks for required environment variables when they are guaranteed to be present in their deployment configuration, similar to their preference for simplicity over defensive programming in test code.
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#601
File: e2e/tests/api/policies/retry-policy.spec.ts:23-24
Timestamp: 2025-06-24T23:52:50.732Z
Learning: The user adityachoudhari26 prefers not to add null safety checks or defensive programming in test code, particularly in e2e tests, as they prioritize simplicity and focus on the main functionality being tested rather than comprehensive error handling within the test itself.
Learnt from: adityachoudhari26
PR: ctrlplanedev/ctrlplane#601
File: e2e/tests/api/policies/retry-policy.spec.ts:117-130
Timestamp: 2025-06-24T23:53:25.398Z
Learning: User adityachoudhari26 prefers to keep non-null assertions in e2e test code without extensive null safety checks, reasoning that test failures serve the same purpose of catching issues and the extra validation doesn't add much value in test contexts.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Typecheck
  • GitHub Check: Lint
🔇 Additional comments (2)
apps/workspace-engine/pkg/events/events.go (2)

14-20: Event type definitions look good!

The EventType and resource event constants are well-defined. The previously missing ResourceDeleted constant has been properly added.


43-52: Constructor implementation looks good!

The NewMessageReader properly initializes all handlers and the logger instance. All resource event types are registered with their corresponding handlers.

@j7m4 j7m4 merged commit dbbdecb into main Aug 5, 2025
5 checks passed
@j7m4 j7m4 deleted the go-events branch August 5, 2025 23:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants