diff --git a/cmd/api/docs/docs.go b/cmd/api/docs/docs.go index 310981b30..f7666a116 100644 --- a/cmd/api/docs/docs.go +++ b/cmd/api/docs/docs.go @@ -87,6 +87,63 @@ var doc = `{ } } }, + "/v1/account/{network}/{address}/count": { + "get": { + "description": "Get account token balances count grouped by count", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "account" + ], + "summary": "Get account token balances count grouped by count", + "operationId": "get-account-token-balances-count", + "parameters": [ + { + "type": "string", + "description": "Network", + "name": "network", + "in": "path", + "required": true + }, + { + "maxLength": 36, + "minLength": 36, + "type": "string", + "description": "Address", + "name": "address", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + } + } + } + }, "/v1/account/{network}/{address}/metadata": { "get": { "description": "Returns full metadata for account", @@ -4105,6 +4162,12 @@ var doc = `{ "contract": { "type": "string" }, + "creators": { + "type": "array", + "items": { + "type": "string" + } + }, "decimals": { "type": "integer", "x-nullable": true @@ -4121,6 +4184,12 @@ var doc = `{ "type": "string", "x-nullable": true }, + "formats": { + "type": "array", + "items": { + "type": "object" + } + }, "is_boolean_amount": { "type": "boolean", "x-nullable": true @@ -4151,6 +4220,12 @@ var doc = `{ "type": "string", "x-nullable": true }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, "thumbnail_uri": { "type": "string", "x-nullable": true @@ -4185,6 +4260,12 @@ var doc = `{ "contract": { "type": "string" }, + "creators": { + "type": "array", + "items": { + "type": "string" + } + }, "decimals": { "type": "integer", "x-nullable": true @@ -4201,6 +4282,12 @@ var doc = `{ "type": "string", "x-nullable": true }, + "formats": { + "type": "array", + "items": { + "type": "object" + } + }, "is_boolean_amount": { "type": "boolean", "x-nullable": true @@ -4228,6 +4315,12 @@ var doc = `{ "type": "string", "x-nullable": true }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, "thumbnail_uri": { "type": "string", "x-nullable": true @@ -4322,6 +4415,12 @@ var doc = `{ "contract": { "type": "string" }, + "creators": { + "type": "array", + "items": { + "type": "string" + } + }, "decimals": { "type": "integer", "x-nullable": true @@ -4338,6 +4437,12 @@ var doc = `{ "type": "string", "x-nullable": true }, + "formats": { + "type": "array", + "items": { + "type": "object" + } + }, "is_boolean_amount": { "type": "boolean", "x-nullable": true @@ -4365,6 +4470,12 @@ var doc = `{ "type": "string", "x-nullable": true }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, "thumbnail_uri": { "type": "string", "x-nullable": true diff --git a/cmd/api/docs/swagger.json b/cmd/api/docs/swagger.json index eb5f21ba6..72adfd94c 100644 --- a/cmd/api/docs/swagger.json +++ b/cmd/api/docs/swagger.json @@ -69,6 +69,63 @@ } } }, + "/v1/account/{network}/{address}/count": { + "get": { + "description": "Get account token balances count grouped by count", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "account" + ], + "summary": "Get account token balances count grouped by count", + "operationId": "get-account-token-balances-count", + "parameters": [ + { + "type": "string", + "description": "Network", + "name": "network", + "in": "path", + "required": true + }, + { + "maxLength": 36, + "minLength": 36, + "type": "string", + "description": "Address", + "name": "address", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + } + } + } + }, "/v1/account/{network}/{address}/metadata": { "get": { "description": "Returns full metadata for account", @@ -4087,6 +4144,12 @@ "contract": { "type": "string" }, + "creators": { + "type": "array", + "items": { + "type": "string" + } + }, "decimals": { "type": "integer", "x-nullable": true @@ -4103,6 +4166,12 @@ "type": "string", "x-nullable": true }, + "formats": { + "type": "array", + "items": { + "type": "object" + } + }, "is_boolean_amount": { "type": "boolean", "x-nullable": true @@ -4133,6 +4202,12 @@ "type": "string", "x-nullable": true }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, "thumbnail_uri": { "type": "string", "x-nullable": true @@ -4167,6 +4242,12 @@ "contract": { "type": "string" }, + "creators": { + "type": "array", + "items": { + "type": "string" + } + }, "decimals": { "type": "integer", "x-nullable": true @@ -4183,6 +4264,12 @@ "type": "string", "x-nullable": true }, + "formats": { + "type": "array", + "items": { + "type": "object" + } + }, "is_boolean_amount": { "type": "boolean", "x-nullable": true @@ -4210,6 +4297,12 @@ "type": "string", "x-nullable": true }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, "thumbnail_uri": { "type": "string", "x-nullable": true @@ -4304,6 +4397,12 @@ "contract": { "type": "string" }, + "creators": { + "type": "array", + "items": { + "type": "string" + } + }, "decimals": { "type": "integer", "x-nullable": true @@ -4320,6 +4419,12 @@ "type": "string", "x-nullable": true }, + "formats": { + "type": "array", + "items": { + "type": "object" + } + }, "is_boolean_amount": { "type": "boolean", "x-nullable": true @@ -4347,6 +4452,12 @@ "type": "string", "x-nullable": true }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, "thumbnail_uri": { "type": "string", "x-nullable": true diff --git a/cmd/api/docs/swagger.yaml b/cmd/api/docs/swagger.yaml index 6712c04be..39ec68e3b 100644 --- a/cmd/api/docs/swagger.yaml +++ b/cmd/api/docs/swagger.yaml @@ -828,6 +828,10 @@ definitions: x-nullable: true contract: type: string + creators: + items: + type: string + type: array decimals: type: integer x-nullable: true @@ -840,6 +844,10 @@ definitions: external_uri: type: string x-nullable: true + formats: + items: + type: object + type: array is_boolean_amount: type: boolean x-nullable: true @@ -862,6 +870,10 @@ definitions: symbol: type: string x-nullable: true + tags: + items: + type: string + type: array thumbnail_uri: type: string x-nullable: true @@ -886,6 +898,10 @@ definitions: type: string contract: type: string + creators: + items: + type: string + type: array decimals: type: integer x-nullable: true @@ -898,6 +914,10 @@ definitions: external_uri: type: string x-nullable: true + formats: + items: + type: object + type: array is_boolean_amount: type: boolean x-nullable: true @@ -918,6 +938,10 @@ definitions: symbol: type: string x-nullable: true + tags: + items: + type: string + type: array thumbnail_uri: type: string x-nullable: true @@ -983,6 +1007,10 @@ definitions: x-nullable: true contract: type: string + creators: + items: + type: string + type: array decimals: type: integer x-nullable: true @@ -995,6 +1023,10 @@ definitions: external_uri: type: string x-nullable: true + formats: + items: + type: object + type: array is_boolean_amount: type: boolean x-nullable: true @@ -1015,6 +1047,10 @@ definitions: symbol: type: string x-nullable: true + tags: + items: + type: string + type: array thumbnail_uri: type: string x-nullable: true @@ -1469,6 +1505,45 @@ paths: summary: Get account info tags: - account + /v1/account/{network}/{address}/count: + get: + consumes: + - application/json + description: Get account token balances count grouped by count + operationId: get-account-token-balances-count + parameters: + - description: Network + in: path + name: network + required: true + type: string + - description: Address + in: path + maxLength: 36 + minLength: 36 + name: address + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: integer + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/handlers.Error' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handlers.Error' + summary: Get account token balances count grouped by count + tags: + - account /v1/account/{network}/{address}/metadata: get: consumes: diff --git a/cmd/api/handlers/account.go b/cmd/api/handlers/account.go index 57293267e..7c03a8fae 100644 --- a/cmd/api/handlers/account.go +++ b/cmd/api/handlers/account.go @@ -161,3 +161,28 @@ func (ctx *Context) getAccountBalances(network, address string, req tokenBalance return &response, nil } + +// GetAccountTokenBalancesGroupedCount godoc +// @Summary Get account token balances count grouped by count +// @Description Get account token balances count grouped by count +// @Tags account +// @ID get-account-token-balances-count +// @Param network path string true "Network" +// @Param address path string true "Address" minlength(36) maxlength(36) +// @Accept json +// @Produce json +// @Success 200 {object} map[string]int64 +// @Failure 400 {object} Error +// @Failure 500 {object} Error +// @Router /v1/account/{network}/{address}/count [get] +func (ctx *Context) GetAccountTokenBalancesGroupedCount(c *gin.Context) { + var req getContractRequest + if err := c.BindUri(&req); ctx.handleError(c, err, http.StatusBadRequest) { + return + } + res, err := ctx.TokenBalances.CountByContract(req.Network, req.Address) + if ctx.handleError(c, err, 0) { + return + } + c.JSON(http.StatusOK, res) +} diff --git a/cmd/api/main.go b/cmd/api/main.go index 411377fa2..fee9a10b6 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -180,6 +180,7 @@ func (api *app) makeRouter() { account.GET("", api.Context.GetInfo) account.GET("metadata", api.Context.GetMetadata) account.GET("token_balances", api.Context.GetAccountTokenBalances) + account.GET("count", api.Context.GetAccountTokenBalancesGroupedCount) } fa12 := v1.Group("tokens/:network") diff --git a/internal/elastic/tokenbalance/storage.go b/internal/elastic/tokenbalance/storage.go index 8f35e0f72..04a5e1df7 100644 --- a/internal/elastic/tokenbalance/storage.go +++ b/internal/elastic/tokenbalance/storage.go @@ -158,3 +158,43 @@ func (storage *Storage) BurnNft(network, contract string, tokenID int64) error { } return nil } + +type countByContractAgg struct { + Aggs struct { + Count struct { + Buckets []core.Bucket `json:"buckets"` + } `json:"count"` + } `json:"aggregations"` +} + +// CountByContract - +func (storage *Storage) CountByContract(network, address string) (map[string]int64, error) { + query := core.NewQuery().Query( + core.Bool( + core.Filter( + core.Term("network", network), + core.MatchPhrase("address", address), + ), + core.MustNot( + core.Term("balance", "0"), + ), + ), + ).Add( + core.Aggs( + core.AggItem{ + Name: "count", + Body: core.TermsAgg("contract.keyword", core.MaxQuerySize), + }, + ), + ).Zero() + + var resp countByContractAgg + if err := storage.es.Query([]string{models.DocTokenBalances}, query, &resp); err != nil { + return nil, err + } + result := make(map[string]int64) + for _, b := range resp.Aggs.Count.Buckets { + result[b.Key] = b.DocCount + } + return result, nil +} diff --git a/internal/models/mock/tokenbalance/mock.go b/internal/models/mock/tokenbalance/mock.go index 658dad072..741fde5de 100644 --- a/internal/models/mock/tokenbalance/mock.go +++ b/internal/models/mock/tokenbalance/mock.go @@ -91,3 +91,18 @@ func (mr *MockRepositoryMockRecorder) BurnNft(network, contract, tokenID interfa mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BurnNft", reflect.TypeOf((*MockRepository)(nil).BurnNft), network, contract, tokenID) } + +// CountByContract mocks base method +func (m *MockRepository) CountByContract(network, address string) (map[string]int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CountByContract", network, address) + ret0, _ := ret[0].(map[string]int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CountByContract indicates an expected call of CountByContract +func (mr *MockRepositoryMockRecorder) CountByContract(network, address interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountByContract", reflect.TypeOf((*MockRepository)(nil).CountByContract), network, address) +} diff --git a/internal/models/tokenbalance/repository.go b/internal/models/tokenbalance/repository.go index 550d4ae52..8a2618397 100644 --- a/internal/models/tokenbalance/repository.go +++ b/internal/models/tokenbalance/repository.go @@ -6,4 +6,5 @@ type Repository interface { Update(updates []*TokenBalance) error GetHolders(network, contract string, tokenID int64) ([]TokenBalance, error) BurnNft(network, contract string, tokenID int64) error + CountByContract(network, address string) (map[string]int64, error) } diff --git a/internal/reindexer/tokenbalance/storage.go b/internal/reindexer/tokenbalance/storage.go index 5ce9a4be0..7228c398e 100644 --- a/internal/reindexer/tokenbalance/storage.go +++ b/internal/reindexer/tokenbalance/storage.go @@ -97,3 +97,8 @@ func (storage *Storage) GetAccountBalances(network, address, contract string, si func (storage *Storage) BurnNft(network, contract string, tokenID int64) error { return nil } + +// CountByContract - +func (storage *Storage) CountByContract(network, address string) (map[string]int64, error) { + return nil, nil +}