alerts_management
Find full example heremain.go
Copy
Ask AI
package main
import (
"context"
"crypto/tls"
"fmt"
"os"
"slices"
alertsservicesv1grpc "buf.build/gen/go/getsynq/api/grpc/go/synq/alerts/services/v1/servicesv1grpc"
alertsservicesv1 "buf.build/gen/go/getsynq/api/protocolbuffers/go/synq/alerts/services/v1"
alertsv1 "buf.build/gen/go/getsynq/api/protocolbuffers/go/synq/alerts/v1"
entitiesv1 "buf.build/gen/go/getsynq/api/protocolbuffers/go/synq/entities/v1"
queriesv1 "buf.build/gen/go/getsynq/api/protocolbuffers/go/synq/queries/v1"
"golang.org/x/oauth2/clientcredentials"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/oauth"
)
func main() {
ctx := context.Background()
host := "developer.synq.io"
port := "443"
apiUrl := fmt.Sprintf("%s:%s", host, port)
clientID := os.Getenv("SYNQ_CLIENT_ID")
clientSecret := os.Getenv("SYNQ_CLIENT_SECRET")
slackChannel := os.Getenv("SLACK_CHANNEL")
if clientID == "" || clientSecret == "" {
panic("SYNQ_CLIENT_ID and SYNQ_CLIENT_SECRET must be set")
}
if slackChannel == "" {
panic("SLACK_CHANNEL must be set (e.g., 'C1234567890')")
}
tokenURL := fmt.Sprintf("https://%s/oauth2/token", host)
config := &clientcredentials.Config{
ClientID: clientID,
ClientSecret: clientSecret,
TokenURL: tokenURL,
}
oauthTokenSource := oauth.TokenSource{TokenSource: config.TokenSource(ctx)}
creds := credentials.NewTLS(&tls.Config{InsecureSkipVerify: false})
opts := []grpc.DialOption{
grpc.WithTransportCredentials(creds),
grpc.WithPerRPCCredentials(oauthTokenSource),
grpc.WithAuthority(host),
}
conn, err := grpc.DialContext(ctx, apiUrl, opts...)
if err != nil {
panic(err)
}
defer conn.Close()
fmt.Printf("Connected to API...\n\n")
alertsApi := alertsservicesv1grpc.NewAlertsServiceClient(conn)
// Define the FQN
alertFQN := "example.alerts.critical-failures"
var alertId string // To store created alert ID
var originalAlert *alertsv1.Alert
// ========================================
// STEP 1: CREATE ALERT
// ========================================
{
fmt.Println("=== Step 1: Creating Alert ===")
// Create entity query using WithType to match ClickHouse tables
entityQuery := &alertsv1.EntityGroupQuery{
Parts: []*alertsv1.SelectionQuery{
{
Parts: []*alertsv1.SelectionQuery_QueryPart{
{
Part: &alertsv1.SelectionQuery_QueryPart_WithType{
WithType: &queriesv1.WithType{
Types: []*queriesv1.WithType_Type{
{
EntityType: &queriesv1.WithType_Type_Default{
Default: entitiesv1.EntityType_ENTITY_TYPE_CLICKHOUSE_TABLE,
},
},
},
},
},
},
},
Operand: queriesv1.QueryOperand_QUERY_OPERAND_AND,
},
},
}
// Configure alert for FATAL severity failures only
alertSettings := &alertsv1.AlertSettings{
Settings: &alertsv1.AlertSettings_EntityFailure{
EntityFailure: &alertsv1.EntityFailureAlertSettings{
Severities: []alertsv1.EntityFailureAlertSettings_Severity{
alertsv1.EntityFailureAlertSettings_SEVERITY_FATAL,
},
NotifyUpstream: false,
AllowSqlTestAuditLink: true,
Ongoing: &alertsv1.OngoingAlertsStrategy{
Strategy: &alertsv1.OngoingAlertsStrategy_Disabled_{
Disabled: &alertsv1.OngoingAlertsStrategy_Disabled{},
},
},
},
},
}
// Configure Slack target
targets := []*alertsv1.AlertingTarget{
{
Target: &alertsv1.AlertingTarget_Slack{
Slack: &alertsv1.SlackTarget{
Channel: slackChannel,
},
},
},
}
resp, err := alertsApi.Create(ctx, &alertsservicesv1.CreateRequest{
Name: "Critical Failures Alert",
Fqn: alertFQN,
Trigger: entityQuery,
Targets: targets,
Settings: alertSettings,
})
if err != nil {
panic(fmt.Sprintf("Failed to create alert: %v", err))
}
alertId = resp.Alert.Id
fmt.Printf("✓ Created alert: %s\n", resp.Alert.Id)
}
// List all alerts and find the one we created
{
fmt.Println("\n--- Listing All Alerts ---")
resp, err := alertsApi.List(ctx, &alertsservicesv1.ListRequest{})
if err != nil {
panic(fmt.Sprintf("Failed to list alerts: %v", err))
}
if slices.Contains(resp.AlertsIds, alertId) {
fmt.Printf("✓ Found created alert in list: %s\n", alertId)
}
}
// ========================================
// STEP 2: UPDATE ALERT
// ========================================
{
fmt.Println("\n=== Step 2: Updating Alert ===")
// First, get the alert by FQN
resp, err := alertsApi.BatchGet(ctx, &alertsservicesv1.BatchGetRequest{
Identifiers: []*alertsservicesv1.AlertIdentifier{
{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
},
})
if err != nil {
panic(fmt.Sprintf("Failed to get alert: %v", err))
}
if resp.Alerts == nil || resp.Alerts[alertFQN] == nil {
panic(fmt.Sprintf("No alert found with FQN: %s", alertFQN))
}
originalAlert = resp.Alerts[alertFQN]
fmt.Printf("✓ Retrieved alert: %s\n", originalAlert.Id)
// Update to include both FATAL and ERROR severities
updatedSettings := originalAlert.Settings.GetEntityFailure()
if updatedSettings == nil {
panic("Alert is not an Entity Failure alert")
}
updatedSettings.Severities = append(updatedSettings.Severities, alertsv1.EntityFailureAlertSettings_SEVERITY_ERROR)
// Update with new name to reflect both severities
updatedName := "Critical and Error Failures Alert"
updateResp, err := alertsApi.Update(ctx, &alertsservicesv1.UpdateRequest{
Identifier: &alertsservicesv1.AlertIdentifier{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
Name: &updatedName,
Settings: &alertsv1.AlertSettings{
Settings: &alertsv1.AlertSettings_EntityFailure{
EntityFailure: updatedSettings,
},
},
})
if err != nil {
panic(fmt.Sprintf("Failed to update alert: %v", err))
}
updatedAlert := updateResp.Alert
fmt.Printf("✓ Updated alert: %s\n", updatedAlert.Id)
// Validate that updated alert has both severities
entityFailureSettings := updatedAlert.Settings.GetEntityFailure()
if entityFailureSettings == nil {
panic("Updated alert is not an Entity Failure alert")
}
hasFatal := false
hasError := false
for _, severity := range entityFailureSettings.Severities {
if severity == alertsv1.EntityFailureAlertSettings_SEVERITY_FATAL {
hasFatal = true
}
if severity == alertsv1.EntityFailureAlertSettings_SEVERITY_ERROR {
hasError = true
}
}
if !hasFatal || !hasError {
panic("Updated alert does not have both FATAL and ERROR severities")
}
fmt.Println("✓ Verified alert now has both FATAL and ERROR severities")
}
// ========================================
// STEP 3: TOGGLE ALERT (DISABLE/ENABLE)
// ========================================
{
fmt.Println("\n=== Step 3: Toggling Alert ===")
// Disable the alert
fmt.Println("\n--- Disabling Alert ---")
_, err := alertsApi.ToggleEnabled(ctx, &alertsservicesv1.ToggleEnabledRequest{
Identifier: &alertsservicesv1.AlertIdentifier{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
IsEnabled: false,
})
if err != nil {
panic(fmt.Sprintf("Failed to disable alert: %v", err))
}
// Verify disabled
resp, err := alertsApi.BatchGet(ctx, &alertsservicesv1.BatchGetRequest{
Identifiers: []*alertsservicesv1.AlertIdentifier{
{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
},
})
if err != nil {
panic(fmt.Sprintf("Failed to get alert: %v", err))
}
for _, alert := range resp.Alerts {
if alert.IsDisabled {
fmt.Println("✓ Alert is disabled")
} else {
panic("✗ Alert is still enabled")
}
}
// Re-enable the alert
fmt.Println("\n--- Re-enabling Alert ---")
_, err = alertsApi.ToggleEnabled(ctx, &alertsservicesv1.ToggleEnabledRequest{
Identifier: &alertsservicesv1.AlertIdentifier{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
IsEnabled: true,
})
if err != nil {
panic(fmt.Sprintf("Failed to enable alert: %v", err))
}
// Verify enabled
resp, err = alertsApi.BatchGet(ctx, &alertsservicesv1.BatchGetRequest{
Identifiers: []*alertsservicesv1.AlertIdentifier{
{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
},
})
if err != nil {
panic(fmt.Sprintf("Failed to get alert: %v", err))
}
for _, alert := range resp.Alerts {
if !alert.IsDisabled {
fmt.Println("✓ Alert is enabled")
} else {
panic("✗ Alert is still disabled")
}
}
}
// ========================================
// STEP 4: DELETE ALERT
// ========================================
{
fmt.Println("\n=== Step 4: Deleting Alert ===")
// Verify alert exists before deletion
resp, err := alertsApi.BatchGet(ctx, &alertsservicesv1.BatchGetRequest{
Identifiers: []*alertsservicesv1.AlertIdentifier{
{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
},
})
if err != nil {
panic(fmt.Sprintf("Failed to get alert: %v", err))
}
if len(resp.Alerts) == 0 {
fmt.Println("Alert not found - may have been already deleted")
return
}
// Delete the alert by FQN
_, err = alertsApi.Delete(ctx, &alertsservicesv1.DeleteRequest{
Identifier: &alertsservicesv1.AlertIdentifier{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
})
if err != nil {
panic(fmt.Sprintf("Failed to delete alert: %v", err))
}
fmt.Println("✓ Alert deleted successfully")
// Verify alert is deleted
resp, err = alertsApi.BatchGet(ctx, &alertsservicesv1.BatchGetRequest{
Identifiers: []*alertsservicesv1.AlertIdentifier{
{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
},
})
if err != nil {
panic(fmt.Sprintf("Failed to get alert: %v", err))
}
if len(resp.Alerts) == 0 {
fmt.Println("✓ Verified alert deletion (not found in batch get)")
} else {
panic("✗ Warning: Alert may still exist")
}
}
fmt.Println("\n========================================")
fmt.Println("Done! Complete alert lifecycle demonstrated:")
fmt.Println(" 1. Created alert")
fmt.Println(" 2. Updated alert settings")
fmt.Println(" 3. Toggled alert on/off")
fmt.Println(" 4. Deleted alert")
fmt.Println("========================================")
}
main.go
Copy
Ask AI
package main
import (
"context"
"crypto/tls"
"fmt"
"os"
alertsservicesv1grpc "buf.build/gen/go/getsynq/api/grpc/go/synq/alerts/services/v1/servicesv1grpc"
alertsservicesv1 "buf.build/gen/go/getsynq/api/protocolbuffers/go/synq/alerts/services/v1"
alertsv1 "buf.build/gen/go/getsynq/api/protocolbuffers/go/synq/alerts/v1"
entitiesv1 "buf.build/gen/go/getsynq/api/protocolbuffers/go/synq/entities/v1"
queriesv1 "buf.build/gen/go/getsynq/api/protocolbuffers/go/synq/queries/v1"
"github.com/google/uuid"
"golang.org/x/oauth2/clientcredentials"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/oauth"
)
func main() {
ctx := context.Background()
host := "developer.synq.io"
port := "443"
apiUrl := fmt.Sprintf("%s:%s", host, port)
clientID := os.Getenv("SYNQ_CLIENT_ID")
clientSecret := os.Getenv("SYNQ_CLIENT_SECRET")
slackChannel := os.Getenv("SLACK_CHANNEL")
if clientID == "" || clientSecret == "" {
panic("SYNQ_CLIENT_ID and SYNQ_CLIENT_SECRET must be set")
}
if slackChannel == "" {
panic("SLACK_CHANNEL must be set (e.g., 'C1234567890')")
}
tokenURL := fmt.Sprintf("https://%s/oauth2/token", host)
config := &clientcredentials.Config{
ClientID: clientID,
ClientSecret: clientSecret,
TokenURL: tokenURL,
}
oauthTokenSource := oauth.TokenSource{TokenSource: config.TokenSource(ctx)}
creds := credentials.NewTLS(&tls.Config{InsecureSkipVerify: false})
opts := []grpc.DialOption{
grpc.WithTransportCredentials(creds),
grpc.WithPerRPCCredentials(oauthTokenSource),
grpc.WithAuthority(host),
}
conn, err := grpc.DialContext(ctx, apiUrl, opts...)
if err != nil {
panic(err)
}
defer conn.Close()
fmt.Printf("Connected to API...\n\n")
alertsApi := alertsservicesv1grpc.NewAlertsServiceClient(conn)
// Define alert properties with owner
alertFQN := "example.alerts.owned-alert"
ownerPath := "team.data-platform"
ownershipID := uuid.NewString()
var createdAlertID string
// Create a new alert with an owner
{
fmt.Println("=== Creating Alert with Owner ===")
fmt.Printf("FQN: %s\n", alertFQN)
fmt.Printf("Owner Path: %s\n", ownerPath)
fmt.Printf("Ownership ID: %s\n\n", ownershipID)
// Create entity query that matches ClickHouse tables
entityQuery := &alertsv1.EntityGroupQuery{
Parts: []*alertsv1.SelectionQuery{
{
Parts: []*alertsv1.SelectionQuery_QueryPart{
{
Part: &alertsv1.SelectionQuery_QueryPart_WithType{
WithType: &queriesv1.WithType{
Types: []*queriesv1.WithType_Type{
{
EntityType: &queriesv1.WithType_Type_Default{
Default: entitiesv1.EntityType_ENTITY_TYPE_CLICKHOUSE_TABLE,
},
},
},
},
},
},
},
Operand: queriesv1.QueryOperand_QUERY_OPERAND_AND,
},
},
}
// Configure alert for FATAL severity failures
alertSettings := &alertsv1.AlertSettings{
Settings: &alertsv1.AlertSettings_EntityFailure{
EntityFailure: &alertsv1.EntityFailureAlertSettings{
Severities: []alertsv1.EntityFailureAlertSettings_Severity{
alertsv1.EntityFailureAlertSettings_SEVERITY_FATAL,
},
NotifyUpstream: false,
AllowSqlTestAuditLink: true,
Ongoing: &alertsv1.OngoingAlertsStrategy{
Strategy: &alertsv1.OngoingAlertsStrategy_Disabled_{
Disabled: &alertsv1.OngoingAlertsStrategy_Disabled{},
},
},
},
},
}
// Configure Slack target
targets := []*alertsv1.AlertingTarget{
{
Target: &alertsv1.AlertingTarget_Slack{
Slack: &alertsv1.SlackTarget{
Channel: slackChannel,
},
},
},
}
// Create the alert with owner information
resp, err := alertsApi.Create(ctx, &alertsservicesv1.CreateRequest{
Name: "Owned Alert Example",
Fqn: alertFQN,
Trigger: entityQuery,
Targets: targets,
Settings: alertSettings,
Owner: &alertsv1.Alert_Owner{
OwnerPath: ownerPath,
OwnershipId: ownershipID,
},
})
if err != nil {
panic(fmt.Sprintf("Failed to create alert with owner: %v", err))
}
createdAlertID = resp.Alert.Id
fmt.Printf("✓ Alert created successfully: %s\n", createdAlertID)
if resp.Alert.Owner == nil {
panic("Alert was created but owner was not set")
}
fmt.Printf(" Owner Path: %s\n", resp.Alert.Owner.OwnerPath)
fmt.Printf(" Ownership ID: %s\n\n", resp.Alert.Owner.OwnershipId)
}
// List alerts filtered by owner
{
fmt.Println("=== Listing Alerts by Owner ===")
fmt.Printf("Filtering by owner: %s (ownership: %s)\n\n", ownerPath, ownershipID)
resp, err := alertsApi.List(ctx, &alertsservicesv1.ListRequest{
Owner: &alertsv1.Alert_Owner{
OwnerPath: ownerPath,
OwnershipId: ownershipID,
},
})
if err != nil {
panic(fmt.Sprintf("Failed to list alerts by owner: %v", err))
}
fmt.Printf("Found %d alert(s) for this owner:\n", len(resp.AlertsIds))
for i, alertID := range resp.AlertsIds {
fmt.Printf(" %d. %s\n", i+1, alertID)
}
fmt.Println()
// Verify our alert is in the list
found := false
for _, alertID := range resp.AlertsIds {
if alertID == createdAlertID {
found = true
break
}
}
if found {
fmt.Printf("✓ Our alert '%s' was found in the owner's alerts list\n\n", createdAlertID)
} else {
panic("Our alert was not found in the owner's alerts list")
}
}
// Verify the alert owner by getting it directly
{
fmt.Println("=== Verifying Alert Owner ===")
resp, err := alertsApi.BatchGet(ctx, &alertsservicesv1.BatchGetRequest{
Identifiers: []*alertsservicesv1.AlertIdentifier{
{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
},
})
if err != nil {
panic(fmt.Sprintf("Failed to get alert: %v", err))
}
alert := resp.Alerts[alertFQN]
if alert == nil {
panic("Alert not found")
}
if alert.Owner == nil {
panic("Alert has no owner")
}
if alert.Owner.OwnerPath != ownerPath {
panic(fmt.Sprintf("Owner path mismatch: expected %s, got %s", ownerPath, alert.Owner.OwnerPath))
}
if alert.Owner.OwnershipId != ownershipID {
panic(fmt.Sprintf("Ownership ID mismatch: expected %s, got %s", ownershipID, alert.Owner.OwnershipId))
}
fmt.Println("✓ Alert owner verified successfully")
fmt.Printf(" Owner Path: %s\n", alert.Owner.OwnerPath)
fmt.Printf(" Ownership ID: %s\n\n", alert.Owner.OwnershipId)
}
// Delete the created alert
{
fmt.Println("=== Cleaning Up: Deleting Alert ===")
_, err := alertsApi.Delete(ctx, &alertsservicesv1.DeleteRequest{
Identifier: &alertsservicesv1.AlertIdentifier{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
})
if err != nil {
panic(fmt.Sprintf("Failed to delete alert: %v", err))
}
fmt.Printf("✓ Alert deleted successfully: %s\n", alertFQN)
}
// Verify alert was deleted
{
fmt.Println("\n=== Verifying Deletion ===")
resp, err := alertsApi.BatchGet(ctx, &alertsservicesv1.BatchGetRequest{
Identifiers: []*alertsservicesv1.AlertIdentifier{
{
Identifier: &alertsservicesv1.AlertIdentifier_Fqn{
Fqn: alertFQN,
},
},
},
})
if err != nil {
fmt.Printf("✓ Alert no longer exists (error getting it: %v)\n", err)
} else if len(resp.Alerts) == 0 || resp.Alerts[alertFQN] == nil {
fmt.Println("✓ Alert successfully deleted (not found in batch get)")
} else {
panic("✗ Warning: Alert may still exist")
}
}
fmt.Println("\nDone! Alert with owner created, verified, and cleaned up successfully.")
}