Skip to content

Commit

Permalink
Merge pull request #109 from Patrik-Stas/feature/filter-seqno
Browse files Browse the repository at this point in the history
Feature/filter seqno
  • Loading branch information
Patrik-Stas authored Jul 18, 2021
2 parents 49d4fe4 + dba7346 commit bc99d0f
Show file tree
Hide file tree
Showing 19 changed files with 209 additions and 134 deletions.
2 changes: 1 addition & 1 deletion indypool-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "indyscan-daemon",
"version": "4.1.3",
"version": "4.2.0",
"author": "Patrik Staš",
"license": "ISC",
"description": "Application scanning Hyperledger Indy blockchain for fetching and processing transactions.",
Expand Down
25 changes: 25 additions & 0 deletions indyscan-api-client/demo/get-many-txs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const { getTxsV2 } = require('../src')

const API_URL = process.env.API_URL || 'https://indyscan.io'
const NETWORK_ID = process.env.NETWORK_ID || 'SOVRIN_STAGINGNET'

async function fetch_1000txs_in_39K_40K_seqNo_range () { // eslint-disable-line
return getTxsV2(
API_URL,
NETWORK_ID,
'domain',
0,
1000,
[],
39000,
40000,
'expansion',
undefined)
}

fetch_1000txs_in_39K_40K_seqNo_range() // eslint-disable-line
.then(txs => {
const seqNos = txs.map(tx => tx.imeta.seqNo)
console.log(`Returned ${seqNos.length} txs.`)
console.log(`Fetched transaction sequence numbers: ${JSON.stringify(seqNos)}`)
})
4 changes: 3 additions & 1 deletion indyscan-api-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "indyscan-api-client",
"version": "4.1.3",
"version": "4.2.0",
"author": "Patrik Staš",
"license": "ISC",
"description": "IndyScan HTTP API client.",
Expand All @@ -13,6 +13,8 @@
"istx": "node ./script/istx.js --url 'http://localhost:3000' --network SOVRIN_TESTNET -l domain -s 5",
"isseries": "node ./script/isseries.js --url 'http://localhost:3000' --network SOVRIN_TESTNET -l domain",
"test:integration": "ENVIRONMENT=localhost jest test/integration",
"test:integration:indyscanio": "ENVIRONMENT=indyscanio jest test/integration",
"demo": "node demo/get-many-txs.js",
"test:unit": "echo 'TODO: Implement unit tests.'"
},
"devDependencies": {
Expand Down
10 changes: 8 additions & 2 deletions indyscan-api-client/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,17 @@ async function getTxCount (baseUrl, network, ledger, filterTxNames = [], search)
return res.txCount
}

async function getTxs (baseUrl, network, ledger, skip, size, filterTxNames = [], format = 'original', search, sortFromRecent) {
async function getTxs (baseUrl, network, ledger, skip, size, filterTxNames = [], format = 'serialized', search, sortFromRecent) {
const query = qs.stringify({ sortFromRecent, skip, size, format, filterTxNames: JSON.stringify(filterTxNames), search })
return getRequest(`${baseUrl}/api/networks/${network}/ledgers/${ledger}/txs?${query}`)
}

async function getTx (baseUrl, network, ledger, seqNo, format = 'original') {
async function getTxsV2 (baseUrl, network, ledger, skip, size, filterTxNames = [], seqNoGte, seqNoLt, format = 'serialized', search, sortFromRecent) {
const query = qs.stringify({ sortFromRecent, skip, size, format, filterTxNames: JSON.stringify(filterTxNames), seqNoGte, seqNoLt, search })
return getRequest(`${baseUrl}/api/networks/${network}/ledgers/${ledger}/txs?${query}`)
}

async function getTx (baseUrl, network, ledger, seqNo, format = 'serialized') {
const query = qs.stringify({ format })
const axiosCall = async () => {
return getRequest(`${baseUrl}/api/networks/${network}/ledgers/${ledger}/txs/${seqNo}?${query}`)
Expand All @@ -55,5 +60,6 @@ module.exports.getNetworks = getNetworks
module.exports.getNetwork = getNetwork
module.exports.getDefaultNetwork = getDefaultNetwork
module.exports.getTx = getTx
module.exports.getTxsV2 = getTxsV2
module.exports.getTxs = getTxs
module.exports.getTxCount = getTxCount
2 changes: 2 additions & 0 deletions indyscan-api-client/test/config/indyscanio.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
API_URL='https://indyscan.io'
NETWORK_ID='SOVRIN_STAGINGNET'
3 changes: 2 additions & 1 deletion indyscan-api-client/test/config/localhost.env
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
API_URL='http://localhost:3709'
API_URL='http://localhost:3708'
NETWORK_ID='SOVRIN_STAGINGNET'
177 changes: 93 additions & 84 deletions indyscan-api-client/test/integration/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-env jest */
require('jest')
const { getTxsV2 } = require('../../src')
const { getTxs } = require('../../src')
const { getTxCount } = require('../../src')
const { getNetwork } = require('../../src')
Expand Down Expand Up @@ -29,22 +30,34 @@ describe('basic api test suite', () => {
}

function basicOriginalTxValidation (tx) {
expect(tx.rootHash).toBeDefined()
expect(tx.auditPath).toBeDefined()
expect(tx.txn).toBeDefined()
expect(tx.txn.type).toBeDefined()
expect(tx.txnMetadata).toBeDefined()
expect(tx.imeta).toBeDefined()
expect(tx.imeta.subledger).toBeDefined()
expect(tx.imeta.seqNo).toBeDefined()
expect(tx.idata).toBeDefined()
}

function serializedTxValidation (tx) {
expect(tx.imeta).toBeDefined()
expect(tx.imeta.subledger).toBeDefined()
expect(tx.imeta.seqNo).toBeDefined()
expect(tx.idata).toBeDefined()
expect(tx.idata.json).toBeDefined()
const ledgerTx = JSON.parse(tx.idata.json)
expect(ledgerTx.reqSignature).toBeDefined()
expect(ledgerTx.rootHash).toBeDefined()
expect(ledgerTx.txn).toBeDefined()
expect(ledgerTx.txnMetadata).toBeDefined()
}

function basicIndyscanTxValidation (tx) {
expect(tx.rootHash).toBeDefined()
expect(tx.auditPath).toBeDefined()
expect(tx.txn).toBeDefined()
expect(tx.txn.type).toBeDefined()
expect(tx.txn.typeName).toBeDefined()
expect(tx.txnMetadata).toBeDefined()
expect(tx.subledger).toBeDefined()
expect(tx.meta).toBeDefined()
expect(tx.imeta).toBeDefined()
expect(tx.imeta.subledger).toBeDefined()
expect(tx.imeta.seqNo).toBeDefined()

expect(tx.idata.txn).toBeDefined()
expect(tx.idata.txn.type).toBeDefined()
expect(tx.idata.txn.typeName).toBeDefined()
expect(tx.idata.txnMetadata).toBeDefined()
}

it('should get default network', async () => {
Expand All @@ -59,13 +72,6 @@ describe('basic api test suite', () => {
basicNetworkValidation(networks[0])
})

it('should get all networks', async () => {
const networks = await getNetworks(process.env.API_URL)
expect(Array.isArray(networks)).toBeTruthy()
expect(networks.length).toBeGreaterThanOrEqual(1)
basicNetworkValidation(networks[0])
})

it('should get network by id', async () => {
const networks = await getNetworks(process.env.API_URL)
expect(Array.isArray(networks)).toBeTruthy()
Expand All @@ -87,126 +93,129 @@ describe('basic api test suite', () => {

it('should get config transaction as was found on ledger', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const tx = await getTx(process.env.API_URL, firstNetworkId, 'CONFIG', 1, 'original')
const networkId = process.env.NETWORK_ID || networks[0].id
const tx = await getTx(process.env.API_URL, networkId, 'config', 1, 'serialized')
basicOriginalTxValidation(tx)
})

it('should be case insensitive on subledger name', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const tx = await getTx(process.env.API_URL, firstNetworkId, 'conFIG', 1, 'original')
basicOriginalTxValidation(tx)
const networkId = process.env.NETWORK_ID || networks[0].id
const tx = await getTx(process.env.API_URL, networkId, 'config', 1, 'serialized')
serializedTxValidation(tx)
})

it('should get config transaction in indyscan format', async () => {
it('should get config transaction in full format', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const tx = await getTx(process.env.API_URL, firstNetworkId, 'config', 1, 'full')
expect(tx.original).toBeDefined()
const originalTxParsed = JSON.parse(tx.original)
basicOriginalTxValidation(originalTxParsed)
expect(tx.indyscan).toBeDefined()
basicIndyscanTxValidation(tx.indyscan)
const networkId = process.env.NETWORK_ID || networks[0].id
const tx = await getTx(process.env.API_URL, networkId, 'config', 1, 'full')
expect(tx.idata.serialized).toBeDefined()
expect(tx.idata.expansion).toBeDefined()
serializedTxValidation(tx.idata.serialized)
basicIndyscanTxValidation(tx.idata.expansion)
})

it('should get domain transaction as was found on ledger', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const tx = await getTx(process.env.API_URL, firstNetworkId, 'domain', 1, 'original')
basicOriginalTxValidation(tx)
const networkId = process.env.NETWORK_ID || networks[0].id
const tx = await getTx(process.env.API_URL, networkId, 'domain', 1, 'serialized')
serializedTxValidation(tx)
})

it('should get domain transaction in indyscan format', async () => {
it('should get domain transaction in expansion format', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const tx = await getTx(process.env.API_URL, firstNetworkId, 'domain', 1, 'full')
expect(tx.original).toBeDefined()
const originalTxParsed = JSON.parse(tx.original)
basicOriginalTxValidation(originalTxParsed)
expect(tx.indyscan).toBeDefined()
basicIndyscanTxValidation(tx.indyscan)
const networkId = process.env.NETWORK_ID || networks[0].id
const tx = await getTx(process.env.API_URL, networkId, 'domain', 1, 'expansion')
basicIndyscanTxValidation(tx)
})

it('should get transaction in ledger format by default', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const tx = await getTx(process.env.API_URL, firstNetworkId, 'pool', 1)
basicOriginalTxValidation(tx)
const networkId = process.env.NETWORK_ID || networks[0].id
const tx = await getTx(process.env.API_URL, networkId, 'pool', 1)
serializedTxValidation(tx)
})

it('should get transaction in ledger format', async () => {
it('should get pool transaction in full format', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const tx = await getTx(process.env.API_URL, firstNetworkId, 'pool', 1, 'original')
basicOriginalTxValidation(tx)
})

it('should get pool transaction in indyscan format', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const tx = await getTx(process.env.API_URL, firstNetworkId, 'pool', 1, 'full')
expect(tx.original).toBeDefined()
const originalTxParsed = JSON.parse(tx.original)
basicOriginalTxValidation(originalTxParsed)
expect(tx.indyscan).toBeDefined()
basicIndyscanTxValidation(tx.indyscan)
const networkId = process.env.NETWORK_ID || networks[0].id
const tx = await getTx(process.env.API_URL, networkId, 'pool', 1, 'full')
expect(tx.idata.serialized).toBeDefined()
expect(tx.idata.expansion).toBeDefined()
serializedTxValidation(tx.idata.serialized)
basicIndyscanTxValidation(tx.idata.expansion)
})

it('should get transaction count', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const count = await getTxCount(process.env.API_URL, firstNetworkId, 'domain')
const networkId = process.env.NETWORK_ID || networks[0].id
const count = await getTxCount(process.env.API_URL, networkId, 'domain')
expect(count).toBeGreaterThan(5)
})

it('should have higher total tx count than count of NYM transactions', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const countAll = await getTxCount(process.env.API_URL, firstNetworkId, 'domain')
const countNym = await getTxCount(process.env.API_URL, firstNetworkId, 'domain', ['NYM'])
const networkId = process.env.NETWORK_ID || networks[0].id
const countAll = await getTxCount(process.env.API_URL, networkId, 'domain')
const countNym = await getTxCount(process.env.API_URL, networkId, 'domain', ['NYM'])
expect(countAll).toBeGreaterThan(countNym)
})

it('should have higher total tx count than count of NYM transactions', async () => {
it('should get 10 domain transactions', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const txs = await getTxs(process.env.API_URL, firstNetworkId, 'domain', 0, 10)
const networkId = process.env.NETWORK_ID || networks[0].id
const txs = await getTxs(process.env.API_URL, networkId, 'domain', 0, 10)
expect(Array.isArray(txs)).toBeTruthy()
expect(txs.length).toBe(10)
})

it('should search NYM transactions containing DID', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const txs = await getTxs(process.env.API_URL, firstNetworkId, 'domain', 0, 100, ['NYM'], 'full', 'J4N1K1SEB8uY2muwmecY5q')
const networkId = process.env.NETWORK_ID || networks[0].id
const txs = await getTxs(process.env.API_URL, networkId, 'domain', 0, 100, ['NYM'], 'full', 'J4N1K1SEB8uY2muwmecY5q')
expect(Array.isArray(txs)).toBeTruthy()
for (const tx of txs) {
expect(JSON.stringify(tx)).toEqual(expect.stringMatching(/J4N1K1SEB8uY2muwmecY5q/))
expect(tx.indyscan.txn.typeName).toBe('NYM')
}
})

it('should find 2 NYM transactions containing DID', async () => {
it('should search ATTRIB transactions containing DID', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const txs = await getTxs(process.env.API_URL, firstNetworkId, 'domain', 0, 2, ['NYM'], 'full', 'J4N1K1SEB8uY2muwmecY5q')
const networkId = process.env.NETWORK_ID || networks[0].id
const txs = await getTxs(process.env.API_URL, networkId, 'domain', 0, 100, ['ATTRIB'], 'full', 'J4N1K1SEB8uY2muwmecY5q')
expect(Array.isArray(txs)).toBeTruthy()
expect(txs.length).toBe(2)
for (const tx of txs) {
expect(JSON.stringify(tx)).toEqual(expect.stringMatching(/J4N1K1SEB8uY2muwmecY5q/))
expect(tx.indyscan.txn.typeName).toBe('NYM')
expect(tx.indyscan.txn.typeName).toBe('ATTRIB')
}
})

it('should search ATTRIB transactions containing DID', async () => {
it('should search transaction capped by seqNo range', async () => {
const networks = await getNetworks(process.env.API_URL)
const firstNetworkId = networks[0].id
const txs = await getTxs(process.env.API_URL, firstNetworkId, 'domain', 0, 100, ['ATTRIB'], 'full', 'J4N1K1SEB8uY2muwmecY5q')
const networkId = process.env.NETWORK_ID || networks[0].id
const txs = await getTxsV2(process.env.API_URL, networkId, 'domain', 0, 100, [], 10, 20, 'full')
expect(Array.isArray(txs)).toBeTruthy()
for (const tx of txs) {
expect(JSON.stringify(tx)).toEqual(expect.stringMatching(/J4N1K1SEB8uY2muwmecY5q/))
expect(tx.indyscan.txn.typeName).toBe('ATTRIB')
}
expect(txs.length).toBe(10)
expect(txs.find(tx => tx.idata.expansion.imeta.seqNo === 10)).toBeDefined()
expect(txs.find(tx => tx.idata.expansion.imeta.seqNo === 19)).toBeDefined()
expect(txs.find(tx => tx.idata.expansion.imeta.seqNo === 20)).toBeUndefined()
expect(txs.find(tx => tx.idata.expansion.imeta.seqNo === 9)).toBeUndefined()
})

it('should return no transactions if seqNo filter is set as "lt > gte"', async () => {
const networks = await getNetworks(process.env.API_URL)
const networkId = process.env.NETWORK_ID || networks[0].id
const txs = await getTxsV2(process.env.API_URL, networkId, 'domain', 0, 100, [], 20, 10, 'full')
expect(Array.isArray(txs)).toBeTruthy()
expect(txs.length).toBe(0)
})

it('should return less than filtered seqNo range if "size" parameter is smaller than the range', async () => {
const networks = await getNetworks(process.env.API_URL)
const networkId = process.env.NETWORK_ID || networks[0].id
const txs = await getTxsV2(process.env.API_URL, networkId, 'domain', 0, 2, [], 10, 20, 'full')
expect(Array.isArray(txs)).toBeTruthy()
expect(txs.length).toBe(2)
})
})
2 changes: 1 addition & 1 deletion indyscan-api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "indyscan-api",
"version": "4.1.3",
"version": "4.2.0",
"description": "Web application to browse Hyperledger Indy blockchain transactions.",
"main": "index.js",
"scripts": {
Expand Down
6 changes: 5 additions & 1 deletion indyscan-api/src/api/txs.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ function initTxsApi (app, networkManager, serviceTxs) {
fromRecentTx: Joi.number(),
toRecentTx: Joi.number(),
filterTxNames: Joi.array().items(Joi.string()),
seqNoGte: Joi.number(),
seqNoLt: Joi.number(),
search: Joi.string(),
format: Joi.string().valid(['serialized', 'full', 'expansion'])
}
Expand All @@ -28,13 +30,15 @@ function initTxsApi (app, networkManager, serviceTxs) {
const networkId = getNetworkId(req, res)
const { ledger } = req.params
console.log(JSON.stringify(req.query))
const { skip, size, filterTxNames, search, format, sortFromRecent } = req.query
const { skip, size, filterTxNames, search, format, sortFromRecent, seqNoGte, seqNoLt } = req.query
const txs = await serviceTxs.getTxs(
networkId,
ledger,
skip || 0,
size || 50,
filterTxNames,
seqNoGte,
seqNoLt,
search,
format,
(sortFromRecent === undefined || sortFromRecent === null) ? true : (sortFromRecent === 'true')
Expand Down
Loading

0 comments on commit bc99d0f

Please sign in to comment.