From 6f334813807da878a61cf9f6750502f0a622fff9 Mon Sep 17 00:00:00 2001 From: Siddharth More Date: Sun, 5 May 2024 17:04:10 -0700 Subject: [PATCH] update docs --- disperser/dataapi/blobs_handlers.go | 15 +++++++ disperser/dataapi/docs/docs.go | 57 +++++++++++++++++++++++++++ disperser/dataapi/docs/swagger.json | 57 +++++++++++++++++++++++++++ disperser/dataapi/docs/swagger.yaml | 37 +++++++++++++++++ disperser/dataapi/server.go | 41 +++++++++++++++++++ disperser/dataapi/server_test.go | 61 +++++++++++++++++++++++++++++ 6 files changed, 268 insertions(+) diff --git a/disperser/dataapi/blobs_handlers.go b/disperser/dataapi/blobs_handlers.go index 21d477300d..d77ecf8edc 100644 --- a/disperser/dataapi/blobs_handlers.go +++ b/disperser/dataapi/blobs_handlers.go @@ -35,6 +35,21 @@ func (s *server) getBlobs(ctx context.Context, limit int) ([]*BlobMetadataRespon return s.convertBlobMetadatasToBlobMetadataResponse(ctx, blobMetadatas) } +func (s *server) getBlobCountByAccountId(ctx context.Context, accountID string) (*BlobCountForAccountIdResponse, error) { + s.logger.Info("Calling get blob", "AccountId", accountID) + + metadataCount, err := s.blobstore.GetBlobMetadataCountByAccountID(ctx, accountID) + if err != nil { + return nil, err + } + + s.logger.Debug("Got blob metadata count for AccountId", "AccountId", accountID, "metadataCount", metadataCount) + return &BlobCountForAccountIdResponse{ + Count: metadataCount, + AccountId: accountID, + }, nil +} + func (s *server) convertBlobMetadatasToBlobMetadataResponse(ctx context.Context, metadatas []*disperser.BlobMetadata) ([]*BlobMetadataResponse, error) { var ( err error diff --git a/disperser/dataapi/docs/docs.go b/disperser/dataapi/docs/docs.go index cc65c2114e..b4fd6956d0 100644 --- a/disperser/dataapi/docs/docs.go +++ b/disperser/dataapi/docs/docs.go @@ -114,6 +114,52 @@ const docTemplate = `{ } } }, + "/feed/blobs/{accountId}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Feed" + ], + "summary": "Fetch blob metadata count by AccountId", + "parameters": [ + { + "type": "string", + "description": "AccountId", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dataapi.BlobCountForAccountIdResponse" + } + }, + "400": { + "description": "error: Bad request", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "404": { + "description": "error: Not found", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "500": { + "description": "error: Server error", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + } + } + } + }, "/feed/blobs/{blob_key}": { "get": { "produces": [ @@ -632,6 +678,17 @@ const docTemplate = `{ } } }, + "dataapi.BlobCountForAccountIdResponse": { + "type": "object", + "properties": { + "account_id": { + "type": "string" + }, + "count": { + "type": "integer" + } + } + }, "dataapi.BlobMetadataResponse": { "type": "object", "properties": { diff --git a/disperser/dataapi/docs/swagger.json b/disperser/dataapi/docs/swagger.json index 3f0f3c421e..68d0cabb08 100644 --- a/disperser/dataapi/docs/swagger.json +++ b/disperser/dataapi/docs/swagger.json @@ -110,6 +110,52 @@ } } }, + "/feed/blobs/{accountId}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Feed" + ], + "summary": "Fetch blob metadata count by AccountId", + "parameters": [ + { + "type": "string", + "description": "AccountId", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dataapi.BlobCountForAccountIdResponse" + } + }, + "400": { + "description": "error: Bad request", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "404": { + "description": "error: Not found", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "500": { + "description": "error: Server error", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + } + } + } + }, "/feed/blobs/{blob_key}": { "get": { "produces": [ @@ -628,6 +674,17 @@ } } }, + "dataapi.BlobCountForAccountIdResponse": { + "type": "object", + "properties": { + "account_id": { + "type": "string" + }, + "count": { + "type": "integer" + } + } + }, "dataapi.BlobMetadataResponse": { "type": "object", "properties": { diff --git a/disperser/dataapi/docs/swagger.yaml b/disperser/dataapi/docs/swagger.yaml index b424c07acf..0d3c524c0f 100644 --- a/disperser/dataapi/docs/swagger.yaml +++ b/disperser/dataapi/docs/swagger.yaml @@ -22,6 +22,13 @@ definitions: data was posted to the DA node. type: integer type: object + dataapi.BlobCountForAccountIdResponse: + properties: + account_id: + type: string + count: + type: integer + type: object dataapi.BlobMetadataResponse: properties: batch_header_hash: @@ -305,6 +312,36 @@ paths: summary: Fetch blobs metadata list tags: - Feed + /feed/blobs/{accountId}: + get: + parameters: + - description: AccountId + in: path + name: accountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dataapi.BlobCountForAccountIdResponse' + "400": + description: 'error: Bad request' + schema: + $ref: '#/definitions/dataapi.ErrorResponse' + "404": + description: 'error: Not found' + schema: + $ref: '#/definitions/dataapi.ErrorResponse' + "500": + description: 'error: Server error' + schema: + $ref: '#/definitions/dataapi.ErrorResponse' + summary: Fetch blob metadata count by AccountId + tags: + - Feed /feed/blobs/{blob_key}: get: parameters: diff --git a/disperser/dataapi/server.go b/disperser/dataapi/server.go index 9f53a7e14f..a99d34f36f 100644 --- a/disperser/dataapi/server.go +++ b/disperser/dataapi/server.go @@ -151,6 +151,12 @@ type ( DispersalOnline bool `json:"dispersal_online"` RetrievalOnline bool `json:"retrieval_online"` } + + BlobCountForAccountIdResponse struct { + Count int32 `json:"count"` + AccountId string `json:"account_id"` + } + ErrorResponse struct { Error string `json:"error"` } @@ -244,6 +250,7 @@ func (s *server) Start() error { { feed.GET("/blobs", s.FetchBlobsHandler) feed.GET("/blobs/:blob_key", s.FetchBlobHandler) + feed.GET("/blobs/:accountId", s.FetchBlobCountByAccountIdHandler) } operatorsInfo := v1.Group("/operators-info") { @@ -446,6 +453,40 @@ func (s *server) FetchBlobsHandler(c *gin.Context) { }) } +// FetchBlobCountByAccountIdHandler godoc +// +// @Summary Fetch blob metadata count by AccountId +// @Tags Feed +// @Produce json +// @Param accountId path string true "AccountId" +// @Success 200 {object} BlobCountForAccountIdResponse +// @Failure 400 {object} ErrorResponse "error: Bad request" +// @Failure 404 {object} ErrorResponse "error: Not found" +// @Failure 500 {object} ErrorResponse "error: Server error" +// @Router /feed/blobs/{accountId} [get] +func (s *server) FetchBlobCountByAccountIdHandler(c *gin.Context) { + timer := prometheus.NewTimer(prometheus.ObserverFunc(func(f float64) { + s.metrics.ObserveLatency("FetchBlobCountByAccountIdHandler", f*1000) // make milliseconds + })) + defer timer.ObserveDuration() + + accountId := c.Param("accountId") + if accountId == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "account id empty string is invalid"}) + } + + blobMetadataCountByAccountIdResp, err := s.getBlobCountByAccountId(c.Request.Context(), accountId) + if err != nil { + s.metrics.IncrementFailedRequestNum("FetchBlobCountByAccountIdHandler") + errorResponse(c, err) + return + } + + s.metrics.IncrementSuccessfulRequestNum("FetchBlobCountByAccountIdHandler") + c.Writer.Header().Set(cacheControlParam, fmt.Sprintf("max-age=%d", maxFeedBlobAage)) + c.JSON(http.StatusOK, blobMetadataCountByAccountIdResp) +} + // FetchMetricsHandler godoc // // @Summary Fetch metrics diff --git a/disperser/dataapi/server_test.go b/disperser/dataapi/server_test.go index d897ccbd25..ce8ebea298 100644 --- a/disperser/dataapi/server_test.go +++ b/disperser/dataapi/server_test.go @@ -233,6 +233,65 @@ func TestFetchBlobsHandler(t *testing.T) { assert.Equal(t, 2, len(response.Data)) } +func TestFetchBlobCountByAccountIdHandler(t *testing.T) { + r := setUpRouter() + + blob := makeTestBlob(0, 80) + key := queueBlob(t, &blob, blobstore) + markBlobConfirmed(t, &blob, key, expectedBatchHeaderHash, blobstore) + accountd := "test" + r.GET("/v1/feed/blobs/:accountId", testDataApiServer.FetchBlobCountByAccountIdHandler) + + w := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/v1/feed/blobs/"+accountd, nil) + r.ServeHTTP(w, req) + + res := w.Result() + defer res.Body.Close() + + data, err := io.ReadAll(res.Body) + assert.NoError(t, err) + + var response dataapi.BlobCountForAccountIdResponse + err = json.Unmarshal(data, &response) + assert.NoError(t, err) + assert.NotNil(t, response) + + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Equal(t, int32(1), response.Count) + assert.Equal(t, "test", response.AccountId) +} + +func TestFetchBlobCountByAccountIdInvalidHandler(t *testing.T) { + r := setUpRouter() + + blob := makeTestBlob(0, 80) + key := queueBlob(t, &blob, blobstore) + markBlobConfirmed(t, &blob, key, expectedBatchHeaderHash, blobstore) + // BlobAccountId is "test" + // Search by AccountId test1 + accountd := "test1" + r.GET("/v1/feed/blobs/:accountId", testDataApiServer.FetchBlobCountByAccountIdHandler) + + w := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/v1/feed/blobs/"+accountd, nil) + r.ServeHTTP(w, req) + + res := w.Result() + defer res.Body.Close() + + data, err := io.ReadAll(res.Body) + assert.NoError(t, err) + + var response dataapi.BlobCountForAccountIdResponse + err = json.Unmarshal(data, &response) + assert.NoError(t, err) + assert.NotNil(t, response) + + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Equal(t, int32(0), response.Count) +} + func TestFetchMetricsHandler(t *testing.T) { defer goleak.VerifyNone(t) @@ -1529,9 +1588,11 @@ func markBlobConfirmed(t *testing.T, blob *core.Blob, key disperser.BlobKey, bat BlobStatus: disperser.Confirmed, Expiry: 0, NumRetries: 0, + AccountID: "test", RequestMetadata: &disperser.RequestMetadata{ BlobRequestHeader: core.BlobRequestHeader{ SecurityParams: blob.RequestHeader.SecurityParams, + BlobAuthHeader: blob.RequestHeader.BlobAuthHeader, }, RequestedAt: expectedRequestedAt, BlobSize: uint(len(blob.Data)),