diff --git a/cmd/api/docs/docs.go b/cmd/api/docs/docs.go index 6e0b9fbb4..2684f75b7 100644 --- a/cmd/api/docs/docs.go +++ b/cmd/api/docs/docs.go @@ -2740,6 +2740,66 @@ const docTemplate = `{ } } }, + "/v1/implicit/{network}/{counter}": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "operations" + ], + "summary": "Get implicit operation", + "operationId": "get-implicit-operation", + "parameters": [ + { + "type": "string", + "description": "Network", + "name": "network", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Counter", + "name": "counter", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/handlers.Operation" + } + } + }, + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + } + } + } + }, "/v1/json_schema": { "post": { "description": "Get JSON schema from micheline", @@ -2922,6 +2982,18 @@ const docTemplate = `{ "description": "Search operation in mempool or not", "name": "with_mempool", "in": "query" + }, + { + "type": "boolean", + "description": "Include storage diff to operations or not", + "name": "with_storage_diff", + "in": "query" + }, + { + "type": "string", + "description": "Network", + "name": "network", + "in": "query" } ], "responses": { diff --git a/cmd/api/docs/swagger.json b/cmd/api/docs/swagger.json index a401cd9a0..a8333b809 100644 --- a/cmd/api/docs/swagger.json +++ b/cmd/api/docs/swagger.json @@ -2730,6 +2730,66 @@ } } }, + "/v1/implicit/{network}/{counter}": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "operations" + ], + "summary": "Get implicit operation", + "operationId": "get-implicit-operation", + "parameters": [ + { + "type": "string", + "description": "Network", + "name": "network", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Counter", + "name": "counter", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/handlers.Operation" + } + } + }, + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/gin.H" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + } + } + } + }, "/v1/json_schema": { "post": { "description": "Get JSON schema from micheline", @@ -2912,6 +2972,18 @@ "description": "Search operation in mempool or not", "name": "with_mempool", "in": "query" + }, + { + "type": "boolean", + "description": "Include storage diff to operations or not", + "name": "with_storage_diff", + "in": "query" + }, + { + "type": "string", + "description": "Network", + "name": "network", + "in": "query" } ], "responses": { diff --git a/cmd/api/docs/swagger.yaml b/cmd/api/docs/swagger.yaml index c7e400175..2ca98a168 100644 --- a/cmd/api/docs/swagger.yaml +++ b/cmd/api/docs/swagger.yaml @@ -3236,6 +3236,46 @@ paths: summary: Show indexer head for the network tags: - head + /v1/implicit/{network}/{counter}: + get: + consumes: + - application/json + operationId: get-implicit-operation + parameters: + - description: Network + in: path + name: network + required: true + type: string + - description: Counter + in: path + name: counter + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/handlers.Operation' + type: array + "204": + description: No Content + schema: + $ref: '#/definitions/gin.H' + "400": + description: Bad Request + schema: + $ref: '#/definitions/handlers.Error' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handlers.Error' + summary: Get implicit operation + tags: + - operations /v1/json_schema: post: consumes: @@ -3356,6 +3396,14 @@ paths: in: query name: with_mempool type: boolean + - description: Include storage diff to operations or not + in: query + name: with_storage_diff + type: boolean + - description: Network + in: query + name: network + type: string produces: - application/json responses: diff --git a/cmd/api/handlers/operations.go b/cmd/api/handlers/operations.go index 17cfcc7d2..955e35c8d 100644 --- a/cmd/api/handlers/operations.go +++ b/cmd/api/handlers/operations.go @@ -86,6 +86,8 @@ func GetContractOperations() gin.HandlerFunc { // @ID get-opg // @Param hash path string true "Operation group hash" minlength(51) maxlength(51) // @Param with_mempool query bool false "Search operation in mempool or not" +// @Param with_storage_diff query bool false "Include storage diff to operations or not" +// @Param network query string false "Network" // @Accept json // @Produce json // @Success 200 {array} Operation @@ -110,19 +112,34 @@ func GetOperation() gin.HandlerFunc { operations := make([]operation.Operation, 0) var foundContext *config.Context - for _, ctx := range ctxs { + + network := modelTypes.NewNetwork(queryReq.Network) + if ctx, ok := ctxs[network]; ok { op, err := ctx.Operations.GetByHash(req.Hash) if err != nil { if !ctx.Storage.IsRecordNotFound(err) { handleError(c, ctx.Storage, err, 0) return } - continue - } - operations = append(operations, op...) - if len(operations) > 0 { + } else { foundContext = ctx - break + operations = append(operations, op...) + } + } else { + for _, ctx := range ctxs { + op, err := ctx.Operations.GetByHash(req.Hash) + if err != nil { + if !ctx.Storage.IsRecordNotFound(err) { + handleError(c, ctx.Storage, err, 0) + return + } + continue + } + operations = append(operations, op...) + if len(operations) > 0 { + foundContext = ctx + break + } } } @@ -149,7 +166,7 @@ func GetOperation() gin.HandlerFunc { return } - resp, err := PrepareOperations(foundContext, operations, true) + resp, err := PrepareOperations(foundContext, operations, queryReq.WithStorageDiff) if handleError(c, foundContext.Storage, err, 0) { return } @@ -158,6 +175,43 @@ func GetOperation() gin.HandlerFunc { } } +// GetImplicitOperation godoc +// @Summary Get implicit operation +// @DescriptionGet implicit operation +// @Tags operations +// @ID get-implicit-operation +// @Param network path string true "Network" +// @Param counter path integer true "Counter" +// @Accept json +// @Produce json +// @Success 200 {array} Operation +// @Success 204 {object} gin.H +// @Failure 400 {object} Error +// @Failure 500 {object} Error +// @Router /v1/implicit/{network}/{counter} [get] +func GetImplicitOperation() gin.HandlerFunc { + return func(c *gin.Context) { + ctx := c.MustGet("context").(*config.Context) + + var req ImplicitOperationRequest + if err := c.BindUri(&req); handleError(c, ctx.Storage, err, http.StatusBadRequest) { + return + } + + op, err := ctx.Operations.GetImplicitOperation(req.Counter) + if handleError(c, ctx.Storage, err, 0) { + return + } + + resp, err := PrepareOperations(ctx, []operation.Operation{op}, false) + if handleError(c, ctx.Storage, err, 0) { + return + } + + c.SecureJSON(http.StatusOK, resp) + } +} + // GetOperationErrorLocation godoc // @Summary Get code line where operation failed // @Description Get code line where operation failed diff --git a/cmd/api/handlers/requests.go b/cmd/api/handlers/requests.go index 975cd7310..ea872611c 100644 --- a/cmd/api/handlers/requests.go +++ b/cmd/api/handlers/requests.go @@ -130,6 +130,13 @@ type OperationGroupContentRequest struct { Counter int64 `uri:"counter" binding:"required" example:"123456"` } +// ImplicitOperationRequest - +type ImplicitOperationRequest struct { + getByNetwork + + Counter int64 `uri:"counter" binding:"required" example:"123456"` +} + // FormatterRequest - type FormatterRequest struct { Inline bool `form:"inline"` @@ -164,7 +171,9 @@ type bigMapSearchRequest struct { } type opgRequest struct { - WithMempool bool `form:"with_mempool"` + WithMempool bool `form:"with_mempool" binding:"omitempty"` + WithStorageDiff bool `form:"with_storage_diff" binding:"omitempty"` + Network string `form:"network" binding:"omitempty,network" example:"mainnet"` } type getEntrypointDataRequest struct { diff --git a/cmd/api/main.go b/cmd/api/main.go index 1f464f15b..e50d40859 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -96,6 +96,7 @@ func (api *app) makeRouter() { opg.GET("", handlers.ContextsMiddleware(api.Contexts), handlers.GetOperation()) opg.GET(":counter", handlers.ContextsMiddleware(api.Contexts), handlers.GetByHashAndCounter()) } + v1.GET("implicit/:network/:counter", handlers.NetworkMiddleware(api.Contexts), handlers.GetImplicitOperation()) v1.GET("search", handlers.ContextsMiddleware(api.Contexts), handlers.Search()) v1.POST("json_schema", handlers.MainnetMiddleware(api.Contexts), handlers.JSONSchema()) v1.POST("michelson", handlers.ContextsMiddleware(api.Contexts), handlers.CodeFromMichelson()) diff --git a/internal/models/mock/operation/mock.go b/internal/models/mock/operation/mock.go index bac8f8c61..b547b30ad 100644 --- a/internal/models/mock/operation/mock.go +++ b/internal/models/mock/operation/mock.go @@ -94,6 +94,21 @@ func (mr *MockRepositoryMockRecorder) GetByHashAndCounter(hash, counter interfac return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByHashAndCounter", reflect.TypeOf((*MockRepository)(nil).GetByHashAndCounter), hash, counter) } +// GetImplicitOperation mocks base method +func (m *MockRepository) GetImplicitOperation(counter int64) (model.Operation, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetImplicitOperation", counter) + ret0, _ := ret[0].(model.Operation) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetImplicitOperation indicates an expected call of GetImplicitOperation +func (mr *MockRepositoryMockRecorder) GetImplicitOperation(counter interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetImplicitOperation", reflect.TypeOf((*MockRepository)(nil).GetImplicitOperation), counter) +} + // OPG mocks base method func (m *MockRepository) OPG(address string, size, lastID int64) ([]model.OPG, error) { m.ctrl.T.Helper() diff --git a/internal/models/operation/repository.go b/internal/models/operation/repository.go index 4bb2dec3a..c0ee7cc12 100644 --- a/internal/models/operation/repository.go +++ b/internal/models/operation/repository.go @@ -11,6 +11,7 @@ type Repository interface { Last(filter map[string]interface{}, lastID int64) (Operation, error) GetByHash(hash string) ([]Operation, error) GetByHashAndCounter(hash string, counter int64) ([]Operation, error) + GetImplicitOperation(counter int64) (Operation, error) OPG(address string, size, lastID int64) ([]OPG, error) // GetOperations - get operation by `filter`. `Size` - if 0 - return all, else certain `size` operations. diff --git a/internal/postgres/operation/storage.go b/internal/postgres/operation/storage.go index fbad3e008..136f589f3 100644 --- a/internal/postgres/operation/storage.go +++ b/internal/postgres/operation/storage.go @@ -387,6 +387,18 @@ func (storage *Storage) GetByHashAndCounter(hash string, counter int64) ([]opera return operations, err } +// GetImplicitOperation - +func (storage *Storage) GetImplicitOperation(counter int64) (operation.Operation, error) { + var op operation.Operation + err := storage.DB.Model((*operation.Operation)(nil)). + Where("hash is null"). + Where("counter = ?", counter). + Relation("Destination").Relation("Source").Relation("Initiator").Relation("Delegate"). + Order("id asc"). + Select(&op) + return op, err +} + func getDAppQuery(db pg.DBI, ids []int64, period string) (*orm.Query, error) { query := db.Model((*operation.Operation)(nil)). Where("status = ?", types.OperationStatusApplied)