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+ }
0 commit comments