From 33c1e2562a08224120fe7309ba4fb8ff4da95594 Mon Sep 17 00:00:00 2001 From: Bruno Andreghetti Date: Mon, 7 Jun 2021 08:39:59 -0300 Subject: [PATCH] Implement target time in getHistory tx --- transactions/readAssetHistory.go | 73 ++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/transactions/readAssetHistory.go b/transactions/readAssetHistory.go index e7d127b..25e5c16 100644 --- a/transactions/readAssetHistory.go +++ b/transactions/readAssetHistory.go @@ -24,11 +24,17 @@ var ReadAssetHistory = Transaction{ DataType: "@key", Required: true, }, + { + Tag: "timeTarget", + Description: "Optional parameter to retrieve specific version of the asset.", + DataType: "datetime", + }, }, ReadOnly: true, Routine: func(stub shim.ChaincodeStubInterface, req map[string]interface{}) ([]byte, errors.ICCError) { // This is safe to do because validation is done before calling routine key := req["key"].(assets.Key) + timeTarget := req["timeTarget"] // Get asset's history from blockchain historyIterator, err := stub.GetHistoryForKey(key.Key()) @@ -44,33 +50,64 @@ var ReadAssetHistory = Transaction{ return nil, errors.NewCCError("history not found", 404) } - response := make([]map[string]interface{}, 0) + if timeTarget == nil { + response := make([]map[string]interface{}, 0) + for historyIterator.HasNext() { + queryResponse, err := historyIterator.Next() + if err != nil { + return nil, errors.WrapError(err, "error iterating response") + } + + data := make(map[string]interface{}) - for historyIterator.HasNext() { - queryResponse, err := historyIterator.Next() + if queryResponse.IsDelete { + data["_isDelete"] = queryResponse.IsDelete + } else { + err = json.Unmarshal(queryResponse.Value, &data) + if err != nil { + return nil, errors.WrapError(err, "failed to unmarshal queryResponse's values") + } + } + data["_timestamp"] = time.Unix(queryResponse.Timestamp.Seconds, int64(queryResponse.Timestamp.Nanos)).Format(time.RFC3339) + response = append(response, data) + } + responseJSON, err := json.Marshal(response) if err != nil { - return nil, errors.WrapError(err, "error iterating response") + return nil, errors.WrapError(err, "error marshaling response") } - data := make(map[string]interface{}) + return responseJSON, nil + } else { + response := make(map[string]interface{}) + target := timeTarget.(time.Time) + closestTime := time.Time{} - if queryResponse.IsDelete { - data["_isDelete"] = queryResponse.IsDelete - } else { - err = json.Unmarshal(queryResponse.Value, &data) + for historyIterator.HasNext() { + queryResponse, err := historyIterator.Next() if err != nil { - return nil, errors.WrapError(err, "failed to unmarshal queryResponse's values") + return nil, errors.WrapError(err, "error iterating response") + } + + timestamp := queryResponse.Timestamp.AsTime() + if timestamp.Before(target) && timestamp.After(closestTime) { + closestTime = timestamp + if !queryResponse.IsDelete { + err = json.Unmarshal(queryResponse.Value, &response) + if err != nil { + return nil, errors.WrapError(err, "failed to unmarshal queryResponse's values") + } + } + response["_isDelete"] = queryResponse.IsDelete + response["_timestamp"] = timestamp.Format(time.RFC3339) } } - data["_timestamp"] = time.Unix(queryResponse.Timestamp.Seconds, int64(queryResponse.Timestamp.Nanos)).Format(time.RFC3339) - response = append(response, data) - } - responseJSON, err := json.Marshal(response) - if err != nil { - return nil, errors.WrapError(err, "error marshaling response") - } + responseJSON, err := json.Marshal(response) + if err != nil { + return nil, errors.WrapError(err, "error marshaling response") + } - return responseJSON, nil + return responseJSON, nil + } }, }