Skip to content

Commit be78973

Browse files
committed
feat: add get all tracked objects endpoint
1 parent 8b9c71f commit be78973

File tree

4 files changed

+166
-10
lines changed

4 files changed

+166
-10
lines changed

config.example.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ debug:
2121
server:
2222
listenAddress: "0.0.0.0"
2323
listenPort: 8080
24-
enableDebug: true
24+
enableDebug: false
25+
defaultPageLimit: 50
26+
maxPageLimit: 1000
2527

2628
submit:
2729
url: "https://cardano-preview.blockfrost.io/api/v0/tx/submit"

internal/api/api.go

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"net/http"
77
"net/http/pprof"
8+
"strconv"
89
"time"
910

1011
"zenGate-Global/merkle-oracle-node/internal/config"
@@ -89,7 +90,8 @@ func Start(
8990
// Health check endpoint
9091
router.GET("/healthcheck", handleHealthcheck)
9192

92-
// Object endpoint
93+
// Object endpoints
94+
router.GET("/objects", handleGetAllObjectIDs)
9395
router.GET("/objects/:id", handleGetObjectByID)
9496

9597
// Setup metrics endpoint
@@ -201,7 +203,13 @@ func handleGetObjectByID(c *gin.Context) {
201203
if timestampParam != "" {
202204
parsed, err := time.Parse(time.RFC3339, timestampParam)
203205
if err != nil {
204-
BadRequest(c, fmt.Errorf("invalid timestamp format, expected RFC3339 (e.g. 2024-01-15T10:30:00Z): %w", err))
206+
BadRequest(
207+
c,
208+
fmt.Errorf(
209+
"invalid timestamp format, expected RFC3339 (e.g. 2024-01-15T10:30:00Z): %w",
210+
err,
211+
),
212+
)
205213
return
206214
}
207215
targetTimestamp = &parsed
@@ -222,7 +230,11 @@ func handleGetObjectByID(c *gin.Context) {
222230
var responseTimestamp time.Time
223231

224232
if targetTimestamp != nil {
225-
objectValues, err = db.GetObjectValuesAtTimestamp(c.Request.Context(), id, *targetTimestamp)
233+
objectValues, err = db.GetObjectValuesAtTimestamp(
234+
c.Request.Context(),
235+
id,
236+
*targetTimestamp,
237+
)
226238
responseTimestamp = *targetTimestamp
227239
} else {
228240
objectValues, err = db.GetObjectCurrentValues(c.Request.Context(), id)
@@ -249,6 +261,85 @@ func handleGetObjectByID(c *gin.Context) {
249261
c.JSON(http.StatusOK, response)
250262
}
251263

264+
// handleGetAllObjectIDs godoc
265+
// @Summary Get All Object IDs
266+
// @Description Retrieves a paginated list of all tracked object IDs from the database.
267+
// @Tags objects
268+
// @Accept json
269+
// @Produce json
270+
// @Param limit query int false "Number of items per page (default: 20, max: 1000)"
271+
// @Param offset query int false "Number of items to skip (default: 0)"
272+
// @Success 200 {object} database.GetAllObjectIDsResult "Paginated list of object IDs"
273+
// @Failure 400 {object} map[string]string "Invalid query parameters"
274+
// @Failure 500 {object} map[string]string "Internal server error"
275+
// @Router /objects [get]
276+
func handleGetAllObjectIDs(c *gin.Context) {
277+
db := c.MustGet("db").(*database.Database)
278+
cfg := c.MustGet("cfg").(*config.Config)
279+
280+
limitStr := c.Query("limit")
281+
limit := cfg.Server.DefaultPageLimit
282+
if limitStr != "" {
283+
parsedLimit, err := strconv.Atoi(limitStr)
284+
if err != nil {
285+
BadRequest(
286+
c,
287+
fmt.Errorf("invalid limit parameter: must be a number"),
288+
)
289+
return
290+
}
291+
if parsedLimit <= 0 {
292+
BadRequest(
293+
c,
294+
fmt.Errorf("invalid limit parameter: must be greater than 0"),
295+
)
296+
return
297+
}
298+
if parsedLimit > cfg.Server.MaxPageLimit {
299+
BadRequest(
300+
c,
301+
fmt.Errorf(
302+
"invalid limit parameter: maximum allowed is %d",
303+
cfg.Server.MaxPageLimit,
304+
),
305+
)
306+
return
307+
}
308+
limit = parsedLimit
309+
}
310+
311+
offsetStr := c.Query("offset")
312+
offset := 0
313+
if offsetStr != "" {
314+
parsedOffset, err := strconv.Atoi(offsetStr)
315+
if err != nil {
316+
BadRequest(
317+
c,
318+
fmt.Errorf("invalid offset parameter: must be a number"),
319+
)
320+
return
321+
}
322+
if parsedOffset < 0 {
323+
BadRequest(
324+
c,
325+
fmt.Errorf(
326+
"invalid offset parameter: must be greater than or equal to 0",
327+
),
328+
)
329+
return
330+
}
331+
offset = parsedOffset
332+
}
333+
334+
result, err := db.GetAllObjectIDs(c.Request.Context(), limit, offset)
335+
if err != nil {
336+
ServerError(c, fmt.Errorf("failed to get object IDs: %w", err))
337+
return
338+
}
339+
340+
c.JSON(http.StatusOK, result)
341+
}
342+
252343
func ServerError(c *gin.Context, err error) {
253344
logging.GetLogger().
254345
Error("server error", "error", err, "path", c.Request.URL.Path)

internal/config/config.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ type Config struct {
2929
}
3030

3131
type ServerConfig struct {
32-
ListenAddress string `yaml:"listenAddress"`
33-
ListenPort int `yaml:"listenPort"`
34-
EnableDebug bool `yaml:"enableDebug"`
32+
ListenAddress string `yaml:"listenAddress"`
33+
ListenPort int `yaml:"listenPort"`
34+
EnableDebug bool `yaml:"enableDebug"`
35+
DefaultPageLimit int `yaml:"defaultPageLimit" envconfig:"SERVER_DEFAULT_PAGE_LIMIT"`
36+
MaxPageLimit int `yaml:"maxPageLimit" envconfig:"SERVER_MAX_PAGE_LIMIT"`
3537
}
3638

3739
type IndexerConfig struct {
@@ -174,6 +176,13 @@ func (c *Config) setDefaults() {
174176
c.Server.ListenPort = 8080
175177
}
176178

179+
if c.Server.DefaultPageLimit == 0 {
180+
c.Server.DefaultPageLimit = 50
181+
}
182+
if c.Server.MaxPageLimit == 0 {
183+
c.Server.MaxPageLimit = 1000
184+
}
185+
177186
// Network defaults
178187
if c.Network == "" {
179188
c.Network = network

internal/database/operation.go

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,48 @@ func (d *Database) GetObjectByID(
460460
return &obj, nil
461461
}
462462

463+
type GetAllObjectIDsResult struct {
464+
ObjectIDs []string `json:"objectIds"`
465+
Total int64 `json:"total"`
466+
Limit int `json:"limit"`
467+
Offset int `json:"offset"`
468+
HasMore bool `json:"hasMore"`
469+
}
470+
471+
// GetAllObjectIDs returns a paginated list of all tracked object IDs
472+
func (d *Database) GetAllObjectIDs(
473+
ctx context.Context,
474+
limit, offset int,
475+
) (*GetAllObjectIDsResult, error) {
476+
var objectIDs []string
477+
var total int64
478+
479+
// Get total count
480+
if err := d.db.WithContext(ctx).
481+
Model(&Object{}).
482+
Count(&total).Error; err != nil {
483+
return nil, fmt.Errorf("failed to count objects: %w", err)
484+
}
485+
486+
// Get paginated object IDs
487+
if err := d.db.WithContext(ctx).
488+
Model(&Object{}).
489+
Select("id").
490+
Order("id ASC").
491+
Limit(limit).
492+
Offset(offset).
493+
Pluck("id", &objectIDs).Error; err != nil {
494+
return nil, fmt.Errorf("failed to get object IDs: %w", err)
495+
}
496+
497+
return &GetAllObjectIDsResult{
498+
ObjectIDs: objectIDs,
499+
Total: total,
500+
Limit: limit,
501+
Offset: offset,
502+
}, nil
503+
}
504+
463505
// GetObjectCurrentValues returns the current key-value pairs for an object as a flat map
464506
func (d *Database) GetObjectCurrentValues(
465507
ctx context.Context,
@@ -477,7 +519,11 @@ func (d *Database) GetObjectCurrentValues(
477519
Joins(`JOIN value ON value.value_hash = key.current_value_hash`).
478520
Where(`key.object_id = ? AND key.deleted_at IS NULL AND NOT key.is_deleted`, id).
479521
Scan(&rows).Error; err != nil {
480-
return nil, fmt.Errorf("failed to get current values for object %s: %w", id, err)
522+
return nil, fmt.Errorf(
523+
"failed to get current values for object %s: %w",
524+
id,
525+
err,
526+
)
481527
}
482528

483529
out := make(map[string]any, len(rows))
@@ -486,7 +532,11 @@ func (d *Database) GetObjectCurrentValues(
486532
dec := json.NewDecoder(bytes.NewReader(r.Raw))
487533
dec.UseNumber()
488534
if err := dec.Decode(&v); err != nil {
489-
return nil, fmt.Errorf("failed to unmarshal value for key %s: %w", r.RawKey, err)
535+
return nil, fmt.Errorf(
536+
"failed to unmarshal value for key %s: %w",
537+
r.RawKey,
538+
err,
539+
)
490540
}
491541
out[r.RawKey] = v
492542
}
@@ -548,7 +598,11 @@ func (d *Database) GetObjectValuesAtTimestamp(
548598
dec := json.NewDecoder(bytes.NewReader(r.Raw))
549599
dec.UseNumber()
550600
if err := dec.Decode(&v); err != nil {
551-
return nil, fmt.Errorf("failed to unmarshal value for key %s: %w", r.RawKey, err)
601+
return nil, fmt.Errorf(
602+
"failed to unmarshal value for key %s: %w",
603+
r.RawKey,
604+
err,
605+
)
552606
}
553607
out[r.RawKey] = v
554608
}

0 commit comments

Comments
 (0)