Skip to content

Commit 23e0355

Browse files
committed
feat(history): add manager and entity models
- Add basic CRUD interfaces - Implement conditional fetching, and listing of history
1 parent 00d07c2 commit 23e0355

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed

internal/history/manager.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package history
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"encoding/json"
7+
"fmt"
8+
"time"
9+
10+
"github.com/maniac-en/req/internal/crud"
11+
"github.com/maniac-en/req/internal/database"
12+
"github.com/maniac-en/req/internal/log"
13+
)
14+
15+
func NewHistoryManager(db *database.Queries) *HistoryManager {
16+
return &HistoryManager{DB: db}
17+
}
18+
19+
func (h *HistoryManager) Create(ctx context.Context, name string) (HistoryEntity, error) {
20+
return HistoryEntity{}, fmt.Errorf("use RecordExecution to create history entries")
21+
}
22+
23+
func (h *HistoryManager) Read(ctx context.Context, id int64) (HistoryEntity, error) {
24+
if err := crud.ValidateID(id); err != nil {
25+
return HistoryEntity{}, err
26+
}
27+
28+
history, err := h.DB.GetHistoryById(ctx, id)
29+
if err != nil {
30+
log.Error("failed to read history entry", "id", id, "error", err)
31+
return HistoryEntity{}, err
32+
}
33+
34+
log.Debug("read history entry", "id", id)
35+
return HistoryEntity{History: history}, nil
36+
}
37+
38+
func (h *HistoryManager) Update(ctx context.Context, id int64, name string) (HistoryEntity, error) {
39+
return HistoryEntity{}, fmt.Errorf("history entries are immutable")
40+
}
41+
42+
func (h *HistoryManager) Delete(ctx context.Context, id int64) error {
43+
if err := crud.ValidateID(id); err != nil {
44+
return err
45+
}
46+
47+
err := h.DB.DeleteHistoryEntry(ctx, id)
48+
if err != nil {
49+
log.Error("failed to delete history entry", "id", id, "error", err)
50+
return err
51+
}
52+
53+
log.Info("deleted history entry", "id", id)
54+
return nil
55+
}
56+
57+
func (h *HistoryManager) List(ctx context.Context) ([]HistoryEntity, error) {
58+
return nil, fmt.Errorf("use ListByCollection to list history entries")
59+
}
60+
61+
func (h *HistoryManager) ListByCollection(ctx context.Context, collectionID int64, limit, offset int) ([]HistoryEntity, error) {
62+
if err := crud.ValidateID(collectionID); err != nil {
63+
return nil, err
64+
}
65+
66+
summaries, err := h.DB.GetHistoryByCollection(ctx, database.GetHistoryByCollectionParams{
67+
CollectionID: sql.NullInt64{Int64: collectionID, Valid: true},
68+
Limit: int64(limit),
69+
Offset: int64(offset),
70+
})
71+
if err != nil {
72+
log.Error("failed to list history by collection", "collection_id", collectionID, "error", err)
73+
return nil, err
74+
}
75+
76+
entities := make([]HistoryEntity, len(summaries))
77+
for i, summary := range summaries {
78+
entities[i] = HistoryEntity{History: database.History{
79+
ID: summary.ID,
80+
Method: summary.Method,
81+
Url: summary.Url,
82+
StatusCode: summary.StatusCode,
83+
ExecutedAt: summary.ExecutedAt,
84+
EndpointName: summary.EndpointName,
85+
}}
86+
}
87+
88+
log.Info("listed history by collection", "collection_id", collectionID, "count", len(entities), "limit", limit, "offset", offset)
89+
return entities, nil
90+
}
91+
92+
type ExecutionData struct {
93+
CollectionID int64
94+
CollectionName string
95+
EndpointName string
96+
Method string
97+
URL string
98+
Headers map[string]string
99+
QueryParams map[string]string
100+
RequestBody string
101+
StatusCode int
102+
ResponseBody string
103+
ResponseHeaders map[string][]string
104+
Duration time.Duration
105+
ResponseSize int64
106+
}
107+
108+
func (h *HistoryManager) RecordExecution(ctx context.Context, data ExecutionData) (HistoryEntity, error) {
109+
if err := validateExecutionData(data); err != nil {
110+
log.Error("invalid execution data", "error", err)
111+
return HistoryEntity{}, err
112+
}
113+
114+
log.Debug("recording execution", "method", data.Method, "url", data.URL, "status", data.StatusCode)
115+
116+
requestHeaders, _ := json.Marshal(data.Headers)
117+
queryParams, _ := json.Marshal(data.QueryParams)
118+
responseHeaders, _ := json.Marshal(data.ResponseHeaders)
119+
120+
params := database.CreateHistoryEntryParams{
121+
CollectionID: sql.NullInt64{Int64: data.CollectionID, Valid: data.CollectionID > 0},
122+
CollectionName: sql.NullString{String: data.CollectionName, Valid: data.CollectionName != ""},
123+
EndpointName: sql.NullString{String: data.EndpointName, Valid: data.EndpointName != ""},
124+
Method: data.Method,
125+
Url: data.URL,
126+
StatusCode: int64(data.StatusCode),
127+
Duration: data.Duration.Milliseconds(),
128+
ResponseSize: sql.NullInt64{Int64: data.ResponseSize, Valid: data.ResponseSize > 0},
129+
RequestHeaders: sql.NullString{String: string(requestHeaders), Valid: true},
130+
QueryParams: sql.NullString{String: string(queryParams), Valid: true},
131+
RequestBody: sql.NullString{String: data.RequestBody, Valid: data.RequestBody != ""},
132+
ResponseBody: sql.NullString{String: data.ResponseBody, Valid: data.ResponseBody != ""},
133+
ResponseHeaders: sql.NullString{String: string(responseHeaders), Valid: true},
134+
ExecutedAt: time.Now().Format(time.RFC3339),
135+
}
136+
137+
history, err := h.DB.CreateHistoryEntry(ctx, params)
138+
if err != nil {
139+
log.Error("failed to record execution", "error", err)
140+
return HistoryEntity{}, err
141+
}
142+
143+
log.Info("recorded execution", "id", history.ID, "collection_id", data.CollectionID, "status", data.StatusCode)
144+
return HistoryEntity{History: history}, nil
145+
}
146+
147+
func validateExecutionData(data ExecutionData) error {
148+
if err := crud.ValidateName(data.Method); err != nil {
149+
return fmt.Errorf("invalid method: %w", err)
150+
}
151+
152+
if err := crud.ValidateName(data.URL); err != nil {
153+
return fmt.Errorf("invalid URL: %w", err)
154+
}
155+
156+
if data.StatusCode < 100 || data.StatusCode > 599 {
157+
return fmt.Errorf("invalid status code: %d", data.StatusCode)
158+
}
159+
160+
return nil
161+
}

internal/history/models.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package history
2+
3+
import (
4+
"time"
5+
6+
"github.com/maniac-en/req/internal/database"
7+
)
8+
9+
type HistoryManager struct {
10+
DB *database.Queries
11+
}
12+
13+
type HistoryEntity struct {
14+
database.History
15+
}
16+
17+
func (h HistoryEntity) GetID() int64 {
18+
return h.ID
19+
}
20+
21+
func (h HistoryEntity) GetName() string {
22+
return h.Method + " " + h.Url
23+
}
24+
25+
func (h HistoryEntity) GetCreatedAt() time.Time {
26+
t, _ := time.Parse(time.RFC3339, h.ExecutedAt)
27+
return t
28+
}
29+
30+
func (h HistoryEntity) GetUpdatedAt() time.Time {
31+
return h.GetCreatedAt()
32+
}

0 commit comments

Comments
 (0)