From 1629a19bf637ae9e37c83d2f2547cf08cb3059e1 Mon Sep 17 00:00:00 2001 From: Nana-EC <56320167+Nana-EC@users.noreply.github.com> Date: Thu, 27 Jan 2022 16:48:00 +0000 Subject: [PATCH] Update topicMessage REST response to use transactionId json format (#3222) In v0.49.0 we added transactionId info to the topicMessage chunkInfo response. Given the nature of the transactionId format a more scalable format should be used to allow for expansion - Add a `transactionIdViewModel.js` to handle display format logic from Protobuf or db schema - Update `topicMessageViewModel.js` to use `transactionIdViewModel.js` as a component - Add a `transactionId.js` model class to dictate transactionId format for use by `transactionIdViewModel` - Update OpenAPI spec - Update spec test files - Update viewmodel test js files Signed-off-by: Nana-EC Signed-off-by: Matheus DallRosa --- ...-chunk-initial-transaction-proto.spec.json | 11 ++-- ...t-timestamp-and-payer-account-id.spec.json | 9 ++- ...topicmessages-13-with-chunk-data.spec.json | 18 ++++-- hedera-mirror-rest/__tests__/utils.test.js | 15 ----- .../viewmodel/topicMessageViewModel.test.js | 18 ++++-- hedera-mirror-rest/api/v1/openapi.yml | 32 +++++----- hedera-mirror-rest/model/index.js | 1 + hedera-mirror-rest/model/transactionId.js | 32 ++++++++++ hedera-mirror-rest/utils.js | 13 ----- hedera-mirror-rest/viewmodel/index.js | 1 + .../viewmodel/topicMessageViewModel.js | 23 ++++---- .../viewmodel/transactionIdViewModel.js | 58 +++++++++++++++++++ 12 files changed, 158 insertions(+), 73 deletions(-) create mode 100644 hedera-mirror-rest/model/transactionId.js create mode 100644 hedera-mirror-rest/viewmodel/transactionIdViewModel.js diff --git a/hedera-mirror-rest/__tests__/specs/topicmessage-09-with-chunk-initial-transaction-proto.spec.json b/hedera-mirror-rest/__tests__/specs/topicmessage-09-with-chunk-initial-transaction-proto.spec.json index e307046fa3b..dc36a17b927 100644 --- a/hedera-mirror-rest/__tests__/specs/topicmessage-09-with-chunk-initial-transaction-proto.spec.json +++ b/hedera-mirror-rest/__tests__/specs/topicmessage-09-with-chunk-initial-transaction-proto.spec.json @@ -31,11 +31,14 @@ "responseStatus": 200, "responseJson": { "chunk_info": { + "initial_transaction_id": { + "account_id": "0.0.3", + "nonce": 1, + "scheduled": true, + "transaction_valid_start": "1234567890.000000000" + }, "number": 2, - "total": 3, - "initial_transaction_id": "0.0.3-1234567890-000000000", - "nonce": 1, - "scheduled": true + "total": 3 }, "consensus_timestamp": "1234567890.000000002", "message": "bWVzc2FnZQ==", diff --git a/hedera-mirror-rest/__tests__/specs/topicmessage-10-with-chunk-valid-start-timestamp-and-payer-account-id.spec.json b/hedera-mirror-rest/__tests__/specs/topicmessage-10-with-chunk-valid-start-timestamp-and-payer-account-id.spec.json index fd27a7d2332..d8537cfaa35 100644 --- a/hedera-mirror-rest/__tests__/specs/topicmessage-10-with-chunk-valid-start-timestamp-and-payer-account-id.spec.json +++ b/hedera-mirror-rest/__tests__/specs/topicmessage-10-with-chunk-valid-start-timestamp-and-payer-account-id.spec.json @@ -31,10 +31,13 @@ "responseStatus": 200, "responseJson": { "chunk_info": { - "initial_transaction_id": "0.0.10-1234567890-000000000", - "nonce": null, + "initial_transaction_id": { + "account_id": "0.0.10", + "nonce": null, + "scheduled": null, + "transaction_valid_start": "1234567890.000000000" + }, "number": 2, - "scheduled": null, "total": 3 }, "consensus_timestamp": "1234567890.000000002", diff --git a/hedera-mirror-rest/__tests__/specs/topicmessages-13-with-chunk-data.spec.json b/hedera-mirror-rest/__tests__/specs/topicmessages-13-with-chunk-data.spec.json index 66d35a0ae93..efb43a8a148 100644 --- a/hedera-mirror-rest/__tests__/specs/topicmessages-13-with-chunk-data.spec.json +++ b/hedera-mirror-rest/__tests__/specs/topicmessages-13-with-chunk-data.spec.json @@ -51,10 +51,13 @@ }, { "chunk_info": { - "initial_transaction_id": "0.0.3-1234567890-000000000", - "nonce": 1, + "initial_transaction_id": { + "account_id": "0.0.3", + "nonce": 1, + "scheduled": true, + "transaction_valid_start": "1234567890.000000000" + }, "number": 1, - "scheduled": true, "total": 1 }, "consensus_timestamp": "1234567890.000000002", @@ -67,10 +70,13 @@ }, { "chunk_info": { - "initial_transaction_id": "0.0.10-1234567890-000000000", - "nonce": null, + "initial_transaction_id": { + "account_id": "0.0.10", + "nonce": null, + "scheduled": null, + "transaction_valid_start": "1234567890.000000000" + }, "number": 2, - "scheduled": null, "total": 3 }, "consensus_timestamp": "1234567890.000000003", diff --git a/hedera-mirror-rest/__tests__/utils.test.js b/hedera-mirror-rest/__tests__/utils.test.js index b038516ef90..ed98306e549 100644 --- a/hedera-mirror-rest/__tests__/utils.test.js +++ b/hedera-mirror-rest/__tests__/utils.test.js @@ -120,21 +120,6 @@ describe('Utils createTransactionId tests', () => { }); }); -describe('Utils createTransactionIdFromProto tests', () => { - test('Verify correct result for valid input', () => { - const timestamp = Timestamp.create({seconds: 1234567890, nanos: 123}); - const accountId = AccountID.create({shardNum: 1, realmNum: 2, accountNum: 3}); - const transactionId = TransactionID.create({accountID: accountId, transactionValidStart: timestamp}); - expect(utils.createTransactionIdFromProto(transactionId)).toEqual('1.2.3-1234567890-000000123'); - }); - test('Verify correct result for default input', () => { - const timestamp = Timestamp.create(); - const accountId = AccountID.create({accountNum: 0}); //accountNum must be populated - const transactionId = TransactionID.create({accountID: accountId, transactionValidStart: timestamp}); - expect(utils.createTransactionIdFromProto(transactionId)).toEqual('0.0.0-0-000000000'); - }); -}); - describe('Utils encodeKey', () => { test('Null', () => expect(utils.encodeKey(null)).toBe(null)); [ diff --git a/hedera-mirror-rest/__tests__/viewmodel/topicMessageViewModel.test.js b/hedera-mirror-rest/__tests__/viewmodel/topicMessageViewModel.test.js index d775d4e3812..8e7e30ee0e5 100644 --- a/hedera-mirror-rest/__tests__/viewmodel/topicMessageViewModel.test.js +++ b/hedera-mirror-rest/__tests__/viewmodel/topicMessageViewModel.test.js @@ -47,10 +47,13 @@ describe('topicMessageViewModel tests', () => { const expected = buildDefaultTopicMessageViewModel(); expected.chunk_info = { - initial_transaction_id: '0.0.3-1234567890-000000000', - nonce: null, + initial_transaction_id: { + account_id: '0.0.3', + nonce: null, + scheduled: null, + transaction_valid_start: '1234567890.000000000', + }, number: 1, - scheduled: null, total: 10, }; @@ -69,11 +72,14 @@ describe('topicMessageViewModel tests', () => { const expected = buildDefaultTopicMessageViewModel(); expected.chunk_info = { - initial_transaction_id: '0.0.3-1234567890-000000321', - nonce: 1, + initial_transaction_id: { + account_id: '0.0.3', + nonce: 1, + scheduled: true, + transaction_valid_start: '1234567890.000000321', + }, number: 1, total: 10, - scheduled: true, }; expect(actual).toEqual(expected); diff --git a/hedera-mirror-rest/api/v1/openapi.yml b/hedera-mirror-rest/api/v1/openapi.yml index de6f0b10831..dddcda982d4 100644 --- a/hedera-mirror-rest/api/v1/openapi.yml +++ b/hedera-mirror-rest/api/v1/openapi.yml @@ -1005,23 +1005,13 @@ components: nullable: true properties: initial_transaction_id: - type: string - nonce: - type: integer - nullable: true + $ref: '#/components/schemas/TransactionId' number: + example: 1 type: integer - scheduled: - type: boolean - nullable: true total: + example: 2 type: integer - example: - initial_transaction_id: "0.0.3-1234567890-000000321" - nonce: 2 - number: 1 - scheduled: true - total: 2 Contract: type: object properties: @@ -1864,6 +1854,22 @@ components: - 0.0.8 - 0.0.72 token_id: 0.0.90001 + TransactionId: + type: object + properties: + account_id: + $ref: '#/components/schemas/EntityId' + nonce: + example: 0 + type: integer + minimum: 0 + nullable: true + scheduled: + example: false + type: boolean + nullable: true + transaction_valid_start: + $ref: '#/components/schemas/Timestamp' Transactions: type: array items: diff --git a/hedera-mirror-rest/model/index.js b/hedera-mirror-rest/model/index.js index 0a9fd153a57..ec0bd689375 100644 --- a/hedera-mirror-rest/model/index.js +++ b/hedera-mirror-rest/model/index.js @@ -39,6 +39,7 @@ module.exports = { TokenTransfer: require('./tokenTransfer'), TopicMessage: require('./topicMessage'), Transaction: require('./transaction'), + TransactionId: require('./transactionId'), TransactionResult: require('./transactionResult'), TransactionType: require('./transactionType'), }; diff --git a/hedera-mirror-rest/model/transactionId.js b/hedera-mirror-rest/model/transactionId.js new file mode 100644 index 00000000000..79dc838564a --- /dev/null +++ b/hedera-mirror-rest/model/transactionId.js @@ -0,0 +1,32 @@ +/*- + * ‌ + * Hedera Mirror Node + * ​ + * Copyright (C) 2019 - 2022 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ + +'use strict'; + +class TransactionId { + constructor(payerAccountId, validStartTimestamp, nonce, scheduled) { + this.payerAccountId = payerAccountId; + this.nonce = nonce; + this.scheduled = scheduled; + this.validStartTimestamp = validStartTimestamp; + } +} + +module.exports = TransactionId; diff --git a/hedera-mirror-rest/utils.js b/hedera-mirror-rest/utils.js index 55085d6e9fe..94356875e6d 100644 --- a/hedera-mirror-rest/utils.js +++ b/hedera-mirror-rest/utils.js @@ -781,18 +781,6 @@ const createTransactionId = (entityStr, validStartTimestamp) => { return `${entityStr}-${nsToSecNsWithHyphen(validStartTimestamp)}`; }; -/** - * Creates a transactionId from a protobuf TransactionID - * @param {TransactionID} protoTransactionId - * @returns {string} transactionId of format shard.realm.num-sssssssssss-nnnnnnnnn - */ -const createTransactionIdFromProto = (protoTransactionId) => { - const {accountID, transactionValidStart} = protoTransactionId; - const entityStr = EntityId.of(accountID.shardNum, accountID.realmNum, accountID.accountNum).toString(); - const timestampString = `${transactionValidStart.seconds}-${transactionValidStart.nanos.toString().padStart(9, '0')}`; - return `${entityStr}-${timestampString}`; -}; - /** * Builds the filters from HTTP request query, validates and parses the filters. * @@ -1109,7 +1097,6 @@ module.exports = { buildPgSqlObject, checkTimestampRange, createTransactionId, - createTransactionIdFromProto, convertMySqlStyleQueryToPostgres, encodeBase64, encodeBinary, diff --git a/hedera-mirror-rest/viewmodel/index.js b/hedera-mirror-rest/viewmodel/index.js index 801e40b972f..cfbdabb35a3 100644 --- a/hedera-mirror-rest/viewmodel/index.js +++ b/hedera-mirror-rest/viewmodel/index.js @@ -31,4 +31,5 @@ module.exports = { NftTransferViewModel: require('./nftTransferViewModel'), NftViewModel: require('./nftViewModel'), TopicMessageViewModel: require('./topicMessageViewModel'), + TransactionIdViewModel: require('./transactionIdViewModel'), }; diff --git a/hedera-mirror-rest/viewmodel/topicMessageViewModel.js b/hedera-mirror-rest/viewmodel/topicMessageViewModel.js index f238871a500..08b50c48639 100644 --- a/hedera-mirror-rest/viewmodel/topicMessageViewModel.js +++ b/hedera-mirror-rest/viewmodel/topicMessageViewModel.js @@ -24,7 +24,9 @@ const _ = require('lodash'); const EntityId = require('../entityId'); const utils = require('../utils'); +const TransactionId = require('../model/transactionId'); const {TransactionID} = require('@hashgraph/proto'); +const TransactionIdViewModel = require('./transactionIdViewModel'); /** * Topic message view model @@ -50,24 +52,19 @@ class TopicMessageViewModel { class ChunkInfoViewModel { constructor(topicMessage) { - let initialTransactionId, nonce, scheduled; + let initialTransactionId; if (!_.isNil(topicMessage.initialTransactionId)) { - const transactionId = TransactionID.decode(topicMessage.initialTransactionId); - initialTransactionId = utils.createTransactionIdFromProto(transactionId); - nonce = transactionId.nonce; - scheduled = transactionId.scheduled; + initialTransactionId = TransactionID.decode(topicMessage.initialTransactionId); } else { - initialTransactionId = utils.createTransactionId( - EntityId.parse(topicMessage.payerAccountId).toString(), - topicMessage.validStartTimestamp + initialTransactionId = new TransactionId( + topicMessage.payerAccountId, + topicMessage.validStartTimestamp, + null, + null ); - nonce = null; - scheduled = null; } - this.initial_transaction_id = initialTransactionId; - this.nonce = nonce; + this.initial_transaction_id = new TransactionIdViewModel(initialTransactionId); this.number = topicMessage.chunkNum; - this.scheduled = scheduled; this.total = topicMessage.chunkTotal; } } diff --git a/hedera-mirror-rest/viewmodel/transactionIdViewModel.js b/hedera-mirror-rest/viewmodel/transactionIdViewModel.js new file mode 100644 index 00000000000..18130bda431 --- /dev/null +++ b/hedera-mirror-rest/viewmodel/transactionIdViewModel.js @@ -0,0 +1,58 @@ +/*- + * ‌ + * Hedera Mirror Node + * ​ + * Copyright (C) 2019 - 2022 Hedera Hashgraph, LLC + * ​ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ‍ + */ + +'use strict'; + +const _ = require('lodash'); + +const EntityId = require('../entityId'); +const {TransactionID} = require('@hashgraph/proto'); +const utils = require('../utils'); + +/** + * TransactionId view model + */ +class TransactionIdViewModel { + /** + * Constructs transactionId view model from proto transaction id or TransactionId model + * + * @param {TransactionId|TransactionID} transactionId + */ + constructor(transactionId) { + if (transactionId instanceof TransactionID) { + // handle proto format + const {accountID, transactionValidStart, nonce, scheduled} = transactionId; + this.account_id = EntityId.of(accountID.shardNum, accountID.realmNum, accountID.accountNum).toString(); + this.nonce = Number(nonce); + this.scheduled = scheduled; + this.transaction_valid_start = `${transactionValidStart.seconds}.${transactionValidStart.nanos + .toString() + .padStart(9, '0')}`; + } else { + // handle db format. Handle nil case for nonce and scheduled + this.account_id = EntityId.parse(transactionId.payerAccountId).toString(); + this.nonce = _.isNil(transactionId.nonce) ? null : Number(transactionId.nonce); + this.scheduled = transactionId.scheduled; + this.transaction_valid_start = utils.nsToSecNs(transactionId.validStartTimestamp); + } + } +} + +module.exports = TransactionIdViewModel;