Skip to content

Commit ab3518a

Browse files
committed
feat(endpoints): implement full crud functionality with pagination
1 parent 893486f commit ab3518a

File tree

4 files changed

+224
-11
lines changed

4 files changed

+224
-11
lines changed

db/queries/endpoints.sql

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@ RETURNING *;
1616
SELECT * FROM endpoints
1717
WHERE id = ? LIMIT 1;
1818

19-
-- name: ListEndpoints :many
19+
-- name: ListEndpointsPaginated :many
2020
SELECT * FROM endpoints
2121
WHERE collection_id = ?
22-
ORDER BY name;
22+
ORDER BY name
23+
LIMIT ? OFFSET ?;
24+
25+
-- name: CountEndpointsByCollection :one
26+
SELECT COUNT(*) FROM endpoints
27+
WHERE collection_id = ?;
2328

2429
-- name: UpdateEndpoint :one
2530
UPDATE endpoints

internal/database/endpoints.sql.go

Lines changed: 22 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endpoints/manager.go

Lines changed: 185 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,213 @@ package endpoints
22

33
import (
44
"context"
5+
"database/sql"
6+
"encoding/json"
7+
"fmt"
58

9+
"github.com/maniac-en/req/internal/crud"
610
"github.com/maniac-en/req/internal/database"
11+
"github.com/maniac-en/req/internal/log"
712
)
813

914
func NewEndpointsManager(db *database.Queries) *EndpointsManager {
1015
return &EndpointsManager{DB: db}
1116
}
1217

1318
func (e *EndpointsManager) Create(ctx context.Context, name string) (EndpointEntity, error) {
14-
return EndpointEntity{}, nil
19+
return EndpointEntity{}, fmt.Errorf("use CreateEndpoint to create an endpoint with full data")
1520
}
1621

1722
func (e *EndpointsManager) Read(ctx context.Context, id int64) (EndpointEntity, error) {
18-
return EndpointEntity{}, nil
23+
if err := crud.ValidateID(id); err != nil {
24+
log.Debug("endpoint read failed validation", "id", id)
25+
return EndpointEntity{}, crud.ErrInvalidInput
26+
}
27+
28+
log.Debug("reading endpoint", "id", id)
29+
endpoint, err := e.DB.GetEndpoint(ctx, id)
30+
if err != nil {
31+
if err == sql.ErrNoRows {
32+
log.Debug("endpoint not found", "id", id)
33+
return EndpointEntity{}, crud.ErrNotFound
34+
}
35+
log.Error("failed to read endpoint", "id", id, "error", err)
36+
return EndpointEntity{}, err
37+
}
38+
39+
return EndpointEntity{Endpoint: endpoint}, nil
1940
}
2041

2142
func (e *EndpointsManager) Update(ctx context.Context, id int64, name string) (EndpointEntity, error) {
22-
return EndpointEntity{}, nil
43+
return EndpointEntity{}, fmt.Errorf("use UpdateEndpoint to update an endpoint with full data")
2344
}
2445

2546
func (e *EndpointsManager) Delete(ctx context.Context, id int64) error {
47+
if err := crud.ValidateID(id); err != nil {
48+
log.Debug("endpoint delete failed validation", "id", id)
49+
return crud.ErrInvalidInput
50+
}
51+
52+
log.Debug("deleting endpoint", "id", id)
53+
err := e.DB.DeleteEndpoint(ctx, id)
54+
if err != nil {
55+
log.Error("failed to delete endpoint", "id", id, "error", err)
56+
return err
57+
}
58+
59+
log.Info("deleted endpoint", "id", id)
2660
return nil
2761
}
2862

2963
func (e *EndpointsManager) List(ctx context.Context) ([]EndpointEntity, error) {
30-
return nil, nil
64+
return nil, fmt.Errorf("use ListByCollection to list endpoints for a specific collection")
65+
}
66+
67+
func (e *EndpointsManager) ListByCollection(ctx context.Context, collectionID int64, limit, offset int) (*PaginatedEndpoints, error) {
68+
if err := crud.ValidateID(collectionID); err != nil {
69+
log.Debug("endpoint list failed collection validation", "collection_id", collectionID)
70+
return nil, crud.ErrInvalidInput
71+
}
72+
73+
log.Debug("listing paginated endpoints", "collection_id", collectionID, "limit", limit, "offset", offset)
74+
75+
total, err := e.DB.CountEndpointsByCollection(ctx, collectionID)
76+
if err != nil {
77+
log.Error("failed to count endpoints", "collection_id", collectionID, "error", err)
78+
return nil, err
79+
}
80+
81+
endpoints, err := e.DB.ListEndpointsPaginated(ctx, database.ListEndpointsPaginatedParams{
82+
CollectionID: collectionID,
83+
Limit: int64(limit),
84+
Offset: int64(offset),
85+
})
86+
if err != nil {
87+
log.Error("failed to get paginated endpoints", "collection_id", collectionID, "limit", limit, "offset", offset, "error", err)
88+
return nil, err
89+
}
90+
91+
entities := make([]EndpointEntity, len(endpoints))
92+
for i, endpoint := range endpoints {
93+
entities[i] = EndpointEntity{Endpoint: endpoint}
94+
}
95+
96+
// Calculate pagination metadata
97+
totalPages := int((total + int64(limit) - 1) / int64(limit)) // Ceiling division
98+
currentPage := (offset / limit) + 1
99+
hasNext := offset+len(endpoints) < int(total)
100+
hasPrev := offset > 0
101+
102+
result := &PaginatedEndpoints{
103+
Endpoints: entities,
104+
Total: total,
105+
Offset: int(offset),
106+
Limit: int(limit),
107+
HasNext: hasNext,
108+
HasPrev: hasPrev,
109+
TotalPages: totalPages,
110+
CurrentPage: currentPage,
111+
}
112+
log.Info("retrieved endpoints", "collection_id", collectionID, "count", len(entities), "total", total, "page", currentPage, "total_pages", totalPages)
113+
return result, nil
31114
}
32115

33-
func (e *EndpointsManager) ListPaginated(ctx context.Context, limit, offset int) (*PaginatedEndpoints, error) {
34-
return nil, nil
116+
func (e *EndpointsManager) CreateEndpoint(ctx context.Context, data EndpointData) (EndpointEntity, error) {
117+
if err := crud.ValidateID(data.CollectionID); err != nil {
118+
log.Debug("endpoint creation failed collection validation", "collection_id", data.CollectionID)
119+
return EndpointEntity{}, crud.ErrInvalidInput
120+
}
121+
if err := crud.ValidateName(data.Name); err != nil {
122+
log.Debug("endpoint creation failed name validation", "name", data.Name)
123+
return EndpointEntity{}, crud.ErrInvalidInput
124+
}
125+
if data.Method == "" || data.URL == "" {
126+
log.Debug("endpoint creation failed - method and URL required", "method", data.Method, "url", data.URL)
127+
return EndpointEntity{}, crud.ErrInvalidInput
128+
}
129+
130+
headersJSON := data.Headers
131+
if headersJSON == "" {
132+
headersJSON = "{}"
133+
}
134+
135+
queryParamsJSON := "{}"
136+
if len(data.QueryParams) > 0 {
137+
qpBytes, err := json.Marshal(data.QueryParams)
138+
if err != nil {
139+
log.Error("failed to marshal query params", "error", err)
140+
return EndpointEntity{}, err
141+
}
142+
queryParamsJSON = string(qpBytes)
143+
}
144+
145+
log.Debug("creating endpoint", "collection_id", data.CollectionID, "name", data.Name, "method", data.Method, "url", data.URL)
146+
endpoint, err := e.DB.CreateEndpoint(ctx, database.CreateEndpointParams{
147+
CollectionID: data.CollectionID,
148+
Name: data.Name,
149+
Method: data.Method,
150+
Url: data.URL,
151+
Headers: headersJSON,
152+
QueryParams: queryParamsJSON,
153+
RequestBody: data.RequestBody,
154+
})
155+
if err != nil {
156+
log.Error("failed to create endpoint", "collection_id", data.CollectionID, "name", data.Name, "error", err)
157+
return EndpointEntity{}, err
158+
}
159+
160+
log.Info("created endpoint", "id", endpoint.ID, "name", endpoint.Name, "collection_id", endpoint.CollectionID)
161+
return EndpointEntity{Endpoint: endpoint}, nil
162+
}
163+
164+
func (e *EndpointsManager) UpdateEndpoint(ctx context.Context, id int64, data EndpointData) (EndpointEntity, error) {
165+
if err := crud.ValidateID(id); err != nil {
166+
log.Debug("endpoint update failed ID validation", "id", id)
167+
return EndpointEntity{}, crud.ErrInvalidInput
168+
}
169+
if err := crud.ValidateName(data.Name); err != nil {
170+
log.Debug("endpoint update failed name validation", "name", data.Name)
171+
return EndpointEntity{}, crud.ErrInvalidInput
172+
}
173+
if data.Method == "" || data.URL == "" {
174+
log.Debug("endpoint update failed - method and URL required", "method", data.Method, "url", data.URL)
175+
return EndpointEntity{}, crud.ErrInvalidInput
176+
}
177+
178+
headersJSON := data.Headers
179+
if headersJSON == "" {
180+
headersJSON = "{}"
181+
}
182+
183+
queryParamsJSON := "{}"
184+
if len(data.QueryParams) > 0 {
185+
qpBytes, err := json.Marshal(data.QueryParams)
186+
if err != nil {
187+
log.Error("failed to marshal query params", "error", err)
188+
return EndpointEntity{}, err
189+
}
190+
queryParamsJSON = string(qpBytes)
191+
}
192+
193+
log.Debug("updating endpoint", "id", id, "name", data.Name, "method", data.Method, "url", data.URL)
194+
endpoint, err := e.DB.UpdateEndpoint(ctx, database.UpdateEndpointParams{
195+
Name: data.Name,
196+
Method: data.Method,
197+
Url: data.URL,
198+
Headers: headersJSON,
199+
QueryParams: queryParamsJSON,
200+
RequestBody: data.RequestBody,
201+
ID: id,
202+
})
203+
if err != nil {
204+
if err == sql.ErrNoRows {
205+
log.Debug("endpoint not found for update", "id", id)
206+
return EndpointEntity{}, crud.ErrNotFound
207+
}
208+
log.Error("failed to update endpoint", "id", id, "name", data.Name, "error", err)
209+
return EndpointEntity{}, err
210+
}
211+
212+
log.Info("updated endpoint", "id", endpoint.ID, "name", endpoint.Name)
213+
return EndpointEntity{Endpoint: endpoint}, nil
35214
}

internal/endpoints/models.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,13 @@ type PaginatedEndpoints struct {
5151
TotalPages int `json:"total_pages"`
5252
CurrentPage int `json:"current_page"`
5353
}
54+
55+
type EndpointData struct {
56+
CollectionID int64
57+
Name string
58+
Method string
59+
URL string
60+
Headers string
61+
QueryParams map[string]string
62+
RequestBody string
63+
}

0 commit comments

Comments
 (0)