Skip to main content

Overview

The Notifications Service handles creation and delivery of user notifications. Service Details:
  • Package: notifications
  • Port: 50054
  • Proto: proto/notifications/notifications.proto

Service Definition

service NotificationsService {
    rpc CreateNotification(CreateNotificationRequest) returns (CreateNotificationResponse) {}
    rpc MarkNotificationsRead(MarkNotificationsReadRequest) returns (MarkNotificationsReadResponse) {}
    rpc ListNotifications(ListNotificationsRequest) returns (ListNotificationsResponse) {}
}

RPC Methods

CreateNotification

Internal API - Not exposed to public. Used by other services to create notifications.
Create a new notification for a user. Request:
user_id
string
required
ID of the user to notify
kind
string
required
Type of notification (e.g., “workflow_failed”, “job_completed”, “threshold_reached”)
payload
string
required
JSON string containing notification data
Response:
id
string
Unique identifier of the created notification
Example:
req := &notifications.CreateNotificationRequest{
    UserId: "user-123",
    Kind:   "workflow_failed",
    Payload: `{
        "workflow_id": "workflow-456",
        "workflow_name": "API Monitor",
        "failure_count": 3
    }`,
}

resp, err := client.CreateNotification(ctx, req)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Notification created: %s\n", resp.Id)
# Using grpcurl (internal service)
grpcurl -plaintext -d '{
  "user_id": "user-123",
  "kind": "workflow_failed",
  "payload": "{\"workflow_id\": \"workflow-456\"}"
}' localhost:50054 notifications.NotificationsService/CreateNotification

MarkNotificationsRead

Mark one or more notifications as read. Request:
ids
string[]
required
Array of notification IDs to mark as read
user_id
string
required
User ID (for authorization)
Response: Empty response on success. Example:
req := &notifications.MarkNotificationsReadRequest{
    Ids: []string{"notif-001", "notif-002", "notif-003"},
    UserId: "user-123",
}

_, err := client.MarkNotificationsRead(ctx, req)
if err != nil {
    log.Fatal(err)
}

fmt.Println("Notifications marked as read")
# Using grpcurl
grpcurl -plaintext -d '{
  "ids": ["notif-001", "notif-002"],
  "user_id": "user-123"
}' localhost:50054 notifications.NotificationsService/MarkNotificationsRead

ListNotifications

Retrieve a paginated list of notifications for a user. Request:
user_id
string
required
User ID
cursor
string
Pagination cursor (empty for first page)
Response:
notifications
NotificationResponse[]
Array of notification objects
cursor
string
Cursor for next page (empty if no more pages)
NotificationResponse:
id
string
Notification ID
kind
string
Notification type
payload
string
JSON notification data
read_at
string
Timestamp when marked as read (empty if unread)
created_at
string
Creation timestamp
updated_at
string
Last update timestamp
Example:
req := &notifications.ListNotificationsRequest{
    UserId: "user-123",
    Cursor: "",
}

resp, err := client.ListNotifications(ctx, req)
if err != nil {
    log.Fatal(err)
}

for _, notif := range resp.Notifications {
    status := "unread"
    if notif.ReadAt != "" {
        status = "read"
    }
    fmt.Printf("[%s] %s: %s\n", status, notif.Kind, notif.Payload)
}

// Fetch next page if available
if resp.Cursor != "" {
    nextReq := &notifications.ListNotificationsRequest{
        UserId: "user-123",
        Cursor: resp.Cursor,
    }
    // ... fetch next page
}
# Using grpcurl
grpcurl -plaintext -d '{
  "user_id": "user-123",
  "cursor": ""
}' localhost:50054 notifications.NotificationsService/ListNotifications

Notification Types

Common notification kinds used in Chronoverse:
KindDescriptionPayload Example
workflow_failedWorkflow reached failure threshold{"workflow_id": "...", "failure_count": 3}
workflow_terminatedWorkflow was terminated{"workflow_id": "...", "reason": "max_failures"}
job_completedJob execution completed{"job_id": "...", "status": "success"}
job_failedJob execution failed{"job_id": "...", "error": "..."}
threshold_warningApproaching failure threshold{"workflow_id": "...", "current": 2, "max": 3}

Connection Example

package main

import (
    "context"
    "log"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    pb "github.com/hitesh22rana/chronoverse/pkg/proto/go/notifications"
)

func main() {
    conn, err := grpc.Dial(
        "localhost:50054",
        grpc.WithTransportCredentials(insecure.NewCredentials()),
    )
    if err != nil {
        log.Fatalf("Failed to connect: %v", err)
    }
    defer conn.Close()

    client := pb.NewNotificationsServiceClient(conn)
    ctx := context.Background()

    // List notifications
    resp, err := client.ListNotifications(ctx, &pb.ListNotificationsRequest{
        UserId: "user-123",
        Cursor: "",
    })
    if err != nil {
        log.Fatalf("ListNotifications failed: %v", err)
    }

    unreadCount := 0
    for _, notif := range resp.Notifications {
        if notif.ReadAt == "" {
            unreadCount++
        }
    }

    log.Printf("Total: %d, Unread: %d", len(resp.Notifications), unreadCount)
}

Integration Example

Example of creating a notification when a workflow fails:
// In Workflows Service - after detecting failure threshold
if thresholdReached {
    // Call Notifications Service
    notifClient := notifications.NewNotificationsServiceClient(notifConn)
    
    _, err := notifClient.CreateNotification(ctx, &notifications.CreateNotificationRequest{
        UserId: workflow.UserId,
        Kind:   "workflow_failed",
        Payload: fmt.Sprintf(`{
            "workflow_id": "%s",
            "workflow_name": "%s",
            "failure_count": %d
        }`, workflow.Id, workflow.Name, workflow.ConsecutiveJobFailuresCount),
    })
    
    if err != nil {
        log.Printf("Failed to create notification: %v", err)
    }
}

Build docs developers (and LLMs) love