-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #39 from teknologi-umum/feat/incident-writer
feat(backend): submit incident
- Loading branch information
Showing
5 changed files
with
292 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package main | ||
|
||
import ( | ||
"time" | ||
) | ||
|
||
type IncidentSeverity uint | ||
|
||
const ( | ||
IncidentSeverityInformational IncidentSeverity = iota | ||
IncidentSeverityWarning | ||
IncidentSeverityError | ||
IncidentSeverityFatal | ||
) | ||
|
||
func (s IncidentSeverity) IsValid() bool { | ||
switch s { | ||
case IncidentSeverityInformational, IncidentSeverityWarning, IncidentSeverityError, IncidentSeverityFatal: | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
type IncidentStatus uint | ||
|
||
const ( | ||
IncidentStatusInvestigating IncidentStatus = iota | ||
IncidentStatusIdentified | ||
IncidentStatusMonitoring | ||
IncidentStatusResolved | ||
IncidentStatusScheduled | ||
) | ||
|
||
func (s IncidentStatus) IsValid() bool { | ||
switch s { | ||
case IncidentStatusInvestigating, IncidentStatusIdentified, IncidentStatusMonitoring, IncidentStatusResolved, IncidentStatusScheduled: | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
type Incident struct { | ||
MonitorID string `json:"monitor_id"` | ||
Title string `json:"title"` | ||
Description string `json:"description"` | ||
Timestamp time.Time `json:"timestamp"` | ||
Severity IncidentSeverity `json:"severity"` | ||
Status IncidentStatus `json:"status"` | ||
CreatedBy string `json:"created_by"` | ||
} | ||
|
||
func (i Incident) Validate() error { | ||
err := NewValidationError() | ||
|
||
if i.Timestamp.IsZero() { | ||
err.AddIssue("timestamp", "shouldn't be zero") | ||
} | ||
|
||
if !i.Severity.IsValid() { | ||
err.AddIssue("severity", "invalid") | ||
} | ||
|
||
if !i.Status.IsValid() { | ||
err.AddIssue("status", "invalid") | ||
} | ||
|
||
if err.HasIssues() { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package main_test | ||
|
||
import ( | ||
"errors" | ||
main "semyi" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestIncidentValidate(t *testing.T) { | ||
validPayload := main.Incident{ | ||
MonitorID: "a84c2c59-748c-48d0-b628-4a73b1c3a8d7", | ||
Title: "test", | ||
Description: "description test", | ||
Timestamp: time.Date(2000, 7, 24, 4, 30, 15, 0, time.UTC), | ||
Severity: main.IncidentSeverityError, | ||
Status: main.IncidentStatusInvestigating, | ||
} | ||
|
||
t.Run("Should return error if payload is invalid", func(t *testing.T) { | ||
t.Run("Timestamp", func(t *testing.T) { | ||
validPayloadCopy := validPayload | ||
mockTimestamps := []time.Time{ | ||
time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC), | ||
} | ||
|
||
for _, timestamp := range mockTimestamps { | ||
validPayloadCopy.Timestamp = timestamp | ||
|
||
err := validPayloadCopy.Validate() | ||
if err == nil { | ||
t.Error("expect error, got nil") | ||
} | ||
|
||
var expectError *main.ValidationError | ||
if !errors.As(err, &expectError) { | ||
t.Errorf("expect error: %T, but got : %T", expectError, err) | ||
} | ||
} | ||
}) | ||
t.Run("severity", func(t *testing.T) { | ||
validPayloadCopy := validPayload | ||
mockSeverity := []uint{4, 5, 6} | ||
|
||
for _, severity := range mockSeverity { | ||
validPayloadCopy.Severity = main.IncidentSeverity(severity) | ||
|
||
err := validPayloadCopy.Validate() | ||
if err == nil { | ||
t.Error("expect error, got nil") | ||
} | ||
|
||
var expectError *main.ValidationError | ||
if !errors.As(err, &expectError) { | ||
t.Errorf("expect error: %T, but got : %T", expectError, err) | ||
} | ||
} | ||
}) | ||
t.Run("status", func(t *testing.T) { | ||
validPayloadCopy := validPayload | ||
mockStatus := []uint{5, 6, 7} | ||
|
||
for _, status := range mockStatus { | ||
validPayloadCopy.Status = main.IncidentStatus(status) | ||
|
||
err := validPayloadCopy.Validate() | ||
if err == nil { | ||
t.Error("expect error, got nil") | ||
} | ||
|
||
var expectError *main.ValidationError | ||
if !errors.As(err, &expectError) { | ||
t.Errorf("expect error: %T, but got : %T", expectError, err) | ||
} | ||
} | ||
}) | ||
}) | ||
|
||
t.Run("Shouldn't return error if payload is valid", func(t *testing.T) { | ||
err := validPayload.Validate() | ||
if err != nil { | ||
t.Errorf("expect error nil, but got %v", err) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"database/sql" | ||
"fmt" | ||
"time" | ||
) | ||
|
||
type IncidentWriter struct { | ||
db *sql.DB | ||
} | ||
|
||
func NewIncidentWriter(db *sql.DB) *IncidentWriter { | ||
return &IncidentWriter{ | ||
db: db, | ||
} | ||
} | ||
|
||
func (w *IncidentWriter) Write(ctx context.Context, incident Incident) error { | ||
conn, err := w.db.Conn(ctx) | ||
if err != nil { | ||
return fmt.Errorf("failed to get database connection: %w", err) | ||
} | ||
|
||
incidentStatus := incident.Status | ||
if incident.Timestamp.After(time.Now()) { | ||
incidentStatus = IncidentStatusScheduled | ||
} | ||
|
||
_, err = conn.ExecContext(ctx, "INSERT INTO incident_data (monitor_id, title, description, timestamp, severity, status, created_by) VALUES (?, ?, ?, ?, ?, ?, ?)", | ||
incident.MonitorID, | ||
incident.Title, | ||
incident.Description, | ||
incident.Timestamp, | ||
incident.Severity, | ||
incidentStatus, | ||
incident.CreatedBy, | ||
) | ||
if err != nil { | ||
return fmt.Errorf("failed to submit incident: %w", err) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters