Skip to content

Commit

Permalink
Merge pull request #73 from pshenmic/feat/identity-api-get
Browse files Browse the repository at this point in the history
Add get identity by identifier API
  • Loading branch information
pshenmic authored Oct 27, 2023
2 parents 6707ba4 + 59d4406 commit 10b6549
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 30 deletions.
4 changes: 3 additions & 1 deletion packages/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const MainController = require("./src/controllers/MainController");
const TransactionsController = require("./src/controllers/TransactionsController");
const BlocksController = require("./src/controllers/BlocksController");
const DocumentsController = require("./src/controllers/DocumentsController");
const IdentitiesController = require("./src/controllers/IdentitiesController");
const packageVersion = require('./package.json').version
const Worker = require('./src/worker/index')
const {BLOCK_TIME} = require("./src/constants");
Expand Down Expand Up @@ -74,8 +75,9 @@ const init = async () => {
const transactionsController = new TransactionsController(client, knex)
const dataContractsController = new DataContractsController(knex)
const documentsController = new DocumentsController(knex)
const identitiesController = new IdentitiesController(knex)

Routes({fastify, mainController, blocksController, transactionsController, dataContractsController, documentsController})
Routes({fastify, mainController, blocksController, transactionsController, dataContractsController, documentsController, identitiesController})

fastify.setErrorHandler(errorHandler)
fastify.listen({ host: "0.0.0.0", port: 3005, listenTextResolver: (address) => console.log(`Platform indexer API has started on the ${address}`)});
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/controllers/DocumentsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class DocumentsController {
response.status(404).send({message: 'not found'})
}

response.send(Document.fromRow(document));
response.send(document);
}

getDocumentsByDataContract = async (request, response) => {
Expand Down
21 changes: 21 additions & 0 deletions packages/api/src/controllers/IdentitiesController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const IdentitiesDAO = require("../dao/IdentitiesDAO");

class IdentitiesController {
constructor(knex) {
this.identitiesDAO = new IdentitiesDAO(knex)
}

getIdentityByIdentifier = async (request, response) => {
const {identifier} = request.params;

const identity = await this.identitiesDAO.getIdentityByIdentifier(identifier)

if (!identity) {
return response.status(404).send({message: 'not found'})
}

response.send(identity)
}
}

module.exports = IdentitiesController
1 change: 0 additions & 1 deletion packages/api/src/controllers/TransactionsController.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const cache = require("../cache");
const Transaction = require("../models/Transaction");
const TransactionsDAO = require("../dao/TransactionsDAO");

class TransactionsController {
Expand Down
17 changes: 11 additions & 6 deletions packages/api/src/dao/DataContractsDAO.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ module.exports = class DataContractsDAO {

const subquery = this.knex('data_contracts')
.select('data_contracts.id as id', 'data_contracts.identifier as identifier',
'data_contracts.version as version')
'data_contracts.version as version', 'data_contracts.state_transition_hash as tx_hash')
.select(this.knex.raw(`rank() over (partition by identifier order by version desc) rank`))

const filteredContracts = this.knex.with('with_alias', subquery)
.select( 'id', 'identifier', 'version', 'rank',
.select( 'id', 'identifier', 'version', 'tx_hash', 'rank',
this.knex('with_alias').count('*').as('total_count').where('rank', '1'))
.select(this.knex.raw(`rank() over (order by id ${order}) row_number`))
.from('with_alias')
Expand All @@ -25,9 +25,11 @@ module.exports = class DataContractsDAO {
.as('filtered_data_contracts')

const rows = await this.knex(filteredContracts)
.select('total_count', 'id', 'identifier', 'version', 'row_number')
.select('total_count', 'identifier', 'version', 'row_number', 'filtered_data_contracts.tx_hash',
'blocks.timestamp as timestamp', 'blocks.hash as block_hash')
.join('state_transitions', 'state_transitions.hash', 'filtered_data_contracts.tx_hash')
.join('blocks', 'blocks.hash', 'state_transitions.block_hash')
.whereBetween('row_number', [fromRank, toRank])
.orderBy('id', order);

const totalCount = rows.length > 0 ? Number(rows[0].total_count) : 0;

Expand All @@ -38,9 +40,12 @@ module.exports = class DataContractsDAO {

getDataContractByIdentifier = async (identifier) => {
const rows = await this.knex('data_contracts')
.select('data_contracts.identifier as identifier', 'data_contracts.schema as schema', 'data_contracts.version as version')
.select('data_contracts.identifier as identifier', 'data_contracts.schema as schema',
'data_contracts.version as version', 'state_transitions.hash as tx_hash', 'blocks.timestamp as timestamp')
.join('state_transitions', 'data_contracts.state_transition_hash', 'state_transitions.hash')
.join('blocks', 'blocks.hash', 'state_transitions.block_hash')
.where('data_contracts.identifier', identifier)
.orderBy('id', 'desc')
.orderBy('data_contracts.id', 'desc')
.limit(1);

const [row] = rows
Expand Down
31 changes: 19 additions & 12 deletions packages/api/src/dao/DocumentsDAO.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@ module.exports = class DocumentsDAO {

getDocumentByIdentifier = async (identifier) => {
const subquery = this.knex('documents')
.select('documents.id as id', 'documents.identifier as identifier',
'data_contracts.identifier as data_contract_identifier', 'documents.data as data',
'documents.revision as revision', 'documents.state_transition_hash as state_transition_hash',
.select('documents.id', 'documents.identifier as identifier',
'data_contracts.identifier as data_contract_identifier', 'documents.data as data_contract_data',
'documents.revision as revision', 'documents.state_transition_hash as tx_hash',
'documents.deleted as deleted')
.select(this.knex.raw('rank() over (partition by documents.identifier order by documents.id desc) rank'))
.leftJoin('data_contracts', 'data_contracts.id', 'documents.data_contract_id')
.where('documents.identifier', '=', identifier)
.as('documents')

const rows = await this.knex(subquery)
.select('id', 'identifier', 'data_contract_identifier', 'data',
'revision', 'deleted', 'rank', 'state_transition_hash')
.select('identifier', 'data_contract_identifier', 'data_contract_data',
'revision', 'deleted', 'rank', 'tx_hash', 'blocks.timestamp as timestamp')
.join('state_transitions', 'state_transitions.hash', 'tx_hash')
.join('blocks', 'blocks.hash', 'state_transitions.block_hash')
.limit(1);

const [row] = rows
Expand All @@ -28,7 +30,10 @@ module.exports = class DocumentsDAO {
return null
}

return Document.fromRow(row);
return Document.fromRow({
...row,
data: row.data_contract_data
});
}

getDocumentsByDataContract = async (identifier, page, limit, order) => {
Expand All @@ -37,16 +42,16 @@ module.exports = class DocumentsDAO {

const subquery = this.knex('documents')
.select('documents.id as id', 'documents.identifier as identifier',
'data_contracts.identifier as data_contract_identifier', 'documents.data as data',
'documents.revision as revision', 'documents.state_transition_hash as state_transition_hash',
'data_contracts.identifier as data_contract_identifier', 'documents.data as document_data',
'documents.revision as revision', 'documents.state_transition_hash as tx_hash',
'documents.deleted as deleted')
.select(this.knex.raw('rank() over (partition by documents.identifier order by documents.id desc) rank'))
.leftJoin('data_contracts', 'data_contracts.id', 'documents.data_contract_id')
.where('data_contracts.identifier', identifier)

const filteredDocuments = this.knex.with('with_alias', subquery)
.select('id', 'identifier', 'rank', 'revision', 'data_contract_identifier',
'state_transition_hash', 'deleted', 'data',
'tx_hash', 'deleted', 'document_data',
this.knex('with_alias').count('*').as('total_count').where('rank', '1'))
.select(this.knex.raw(`rank() over (order by id ${order}) row_number`))
.from('with_alias')
Expand All @@ -55,13 +60,15 @@ module.exports = class DocumentsDAO {
.as('documents')

const rows = await this.knex(filteredDocuments)
.select('id', 'identifier', 'row_number', 'revision', 'data_contract_identifier', 'state_transition_hash', 'deleted', 'data', 'total_count')
.select('identifier', 'row_number', 'revision', 'data_contract_identifier',
'tx_hash', 'deleted', 'document_data', 'total_count', 'blocks.timestamp as timestamp')
.whereBetween('row_number', [fromRank, toRank])
.orderBy('id', order);
.join('state_transitions', 'tx_hash', 'state_transitions.hash')
.join('blocks', 'blocks.hash', 'state_transitions.block_hash')

const totalCount = rows.length > 0 ? Number(rows[0].total_count) : 0;

const resultSet = rows.map((row) => Document.fromRow(row));
const resultSet = rows.map((row) => Document.fromRow({...row, data: row.document_data}));

return new PaginatedResultSet(resultSet, page, limit, totalCount)
}
Expand Down
67 changes: 67 additions & 0 deletions packages/api/src/dao/IdentitiesDAO.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const Identity = require("../models/Identity");

module.exports = class IdentitiesDAO {
constructor(knex) {
this.knex = knex;
}

getIdentityByIdentifier = async (identifier) => {
const subquery = this.knex('identities')
.select('identities.id','identities.identifier as identifier', 'identities.state_transition_hash as tx_hash',
'identities.revision as revision', 'identities.state_transition_hash as state_transition_hash')
.select(this.knex.raw('rank() over (partition by identities.identifier order by identities.id desc) rank'))
.where('identities.identifier', '=', identifier)
.as('all_identities')

const lastRevisionIdentities = this.knex(subquery)
.select('identifier', 'revision', 'tx_hash',
'transfers.id as transfer_id', 'transfers.sender as sender',
'transfers.recipient as recipient', 'transfers.amount as amount')
.where('rank', 1)
.leftJoin('transfers', 'transfers.recipient', 'identifier')

const documentsSubQuery = this.knex('documents')
.select('documents.id', 'documents.state_transition_hash', 'state_transitions.owner as owner')
.select(this.knex.raw('rank() over (partition by documents.identifier order by documents.id desc) rank'))
.leftJoin('state_transitions', 'documents.state_transition_hash', 'state_transitions.hash')
.where('state_transitions.owner', identifier)
.as('as_documents')

const dataContractsSubQuery = this.knex('data_contracts')
.select('data_contracts.id', 'data_contracts.state_transition_hash', 'state_transitions.owner as owner')
.select(this.knex.raw('rank() over (partition by data_contracts.identifier order by data_contracts.id desc) rank'))
.leftJoin('state_transitions', 'data_contracts.state_transition_hash', 'state_transitions.hash')
.where('state_transitions.owner', identifier)
.as('as_data_contracts')

const transfersSubquery = this.knex('transfers')
.select('transfers.id as id', 'transfers.state_transition_hash as tx_hash')
.leftJoin('state_transitions', 'tx_hash', 'state_transitions.hash')
.where('state_transitions.owner', identifier)

const rows = await this.knex.with('with_alias', lastRevisionIdentities)
.select('identifier', 'revision',
'transfer_id', 'sender', 'tx_hash', 'blocks.timestamp as timestamp',
'recipient', 'amount')
.join('state_transitions', 'state_transitions.hash', 'tx_hash')
.join('blocks', 'state_transitions.block_hash', 'blocks.hash')
.select(this.knex('with_alias').sum('amount').where('recipient', identifier).as('total_received'))
.select(this.knex('with_alias').sum('amount').where('sender', identifier).as('total_sent'))
.select(this.knex('state_transitions').count('*').where('owner', identifier).as('total_txs'))
.select(this.knex(documentsSubQuery).count('*').where('rank', 1).as('total_documents'))
.select(this.knex(dataContractsSubQuery).count('*').where('rank', 1).as('total_data_contracts'))
.select(this.knex.with('with_transfers', transfersSubquery).count('*').as('total_transfers'))
.from('with_alias')
.limit(1)

if (!rows.length) {
return null
}

const [row] = rows

const balance = Number(row.total_received ?? 0) - Number(row.total_sent ?? 0)

return Identity.fromRow({...row, balance})
}
}
10 changes: 7 additions & 3 deletions packages/api/src/models/DataContract.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ module.exports = class DataContract {
identifier
schema
version
txHash
timestamp

constructor(identifier, schema, version) {
constructor(identifier, schema, version, txHash, timestamp) {
this.identifier = identifier;
this.schema = schema;
this.version = version;
this.txHash = txHash;
this.timestamp = timestamp;
}

static fromRow({identifier, schema, version}) {
return new DataContract(identifier, schema, version)
static fromRow({identifier, schema, version, tx_hash, timestamp}) {
return new DataContract(identifier, schema, version, tx_hash, timestamp)
}
}
13 changes: 8 additions & 5 deletions packages/api/src/models/Document.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ module.exports = class Document {
identifier
dataContractIdentifier
revision
stateTransitionHash
txHash
deleted
data
timestamp

constructor(identifier, dataContractIdentifier, revision, stateTransitionHash, deleted, data) {
constructor(identifier, dataContractIdentifier, revision, txHash, deleted, data, timestamp) {
this.identifier = identifier;
this.dataContractIdentifier = dataContractIdentifier;
this.revision = revision;
this.stateTransitionHash = stateTransitionHash;
this.deleted = deleted;
this.data = data;
this.txHash = txHash;
this.data = data;
this.timestamp = timestamp;
}

static fromRow({identifier, data_contract_identifier, revision, state_transition_hash, deleted, data}) {
return new Document(identifier, data_contract_identifier, revision, state_transition_hash, deleted, data)
static fromRow({identifier, data_contract_identifier, revision, tx_hash, deleted, data, timestamp}) {
return new Document(identifier, data_contract_identifier, revision, tx_hash, deleted, data, timestamp)
}
}
27 changes: 27 additions & 0 deletions packages/api/src/models/Identity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module.exports = class Identity {
identifier
revision
balance
timestamp
txHash
totalTxs
totalTransfers
totalDocuments
totalDataContracts

constructor(identifier, revision, balance, timestamp, totalTxs, totalDataContracts, totalDocuments, totalTransfers, txHash) {
this.identifier = identifier;
this.revision = revision;
this.balance = balance;
this.timestamp = timestamp;
this.totalTxs = totalTxs;
this.totalDocuments = totalDocuments;
this.totalDataContracts = totalDataContracts;
this.totalTransfers = totalTransfers;
this.txHash = txHash
}

static fromRow({identifier, revision, balance, timestamp, total_txs, total_data_contracts, total_documents, total_transfers, tx_hash}) {
return new Identity(identifier, revision, balance, timestamp, total_txs, total_data_contracts, total_documents, total_transfers, tx_hash)
}
}
7 changes: 6 additions & 1 deletion packages/api/src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @param blockController {BlocksController}
* @param transactionsController {TransactionsController}
*/
module.exports = ({fastify, mainController, blocksController, transactionsController, dataContractsController, documentsController}) => {
module.exports = ({fastify, mainController, blocksController, transactionsController, dataContractsController, documentsController, identitiesController}) => {
const routes = [
{
path: '/status',
Expand Down Expand Up @@ -52,6 +52,11 @@ module.exports = ({fastify, mainController, blocksController, transactionsContro
method: 'GET',
handler: documentsController.getDocumentByIdentifier
},
{
path: '/identities/:identifier',
method: 'GET',
handler: identitiesController.getIdentityByIdentifier
},
{
path: '/search',
method: 'GET',
Expand Down

0 comments on commit 10b6549

Please sign in to comment.