Skip to content

Commit

Permalink
Merge pull request #52 from rsksmart/feat/address-finds-deletes-inser…
Browse files Browse the repository at this point in the history
…ts-count-converters

Feat/address finds, deletes, inserts, count and converters
  • Loading branch information
IOVgomezdn authored Mar 22, 2023
2 parents 219a157 + 85461a4 commit 1d3a6dc
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 46 deletions.
7 changes: 7 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ model action {
}

model address {
id Int @default(autoincrement())
address String @id @db.VarChar(42)
block Int
lastBlockMined Int? @map("last_block_mined")
Expand All @@ -63,6 +64,8 @@ model address {
token_address_token_address_addressToaddress token_address[] @relation("token_address_addressToaddress")
transaction_transaction_fromToaddress transaction[] @relation("transaction_fromToaddress")
transaction_transaction_toToaddress transaction[] @relation("transaction_toToaddress")
@@index([id], map: "index_address_id")
}

model address_in_event {
Expand Down Expand Up @@ -190,6 +193,8 @@ model contract_interface {
contractAddress String @map("contract_address") @db.VarChar(42)
contract contract @relation(fields: [contractAddress], references: [address], onDelete: Cascade, onUpdate: NoAction, map: "fk_contract_interface_contract_address")
interface_ interface_ @relation(fields: [interfaceId], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "fk_contract_interface_interface_id")
@@unique([interfaceId, contractAddress], map: "unique_contract_interface_interface_id_contract_address")
}

model contract_method {
Expand All @@ -198,6 +203,8 @@ model contract_method {
contractAddress String @map("contract_address") @db.VarChar(42)
contract contract @relation(fields: [contractAddress], references: [address], onDelete: Cascade, onUpdate: NoAction, map: "fk_contract_method_contract_address")
method method @relation(fields: [methodId], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "fk_contract_method_method_id")
@@unique([methodId, contractAddress], map: "unique_contract_method_method_id_contract_address")
}

model contract_verification {
Expand Down
4 changes: 2 additions & 2 deletions src/api/modules/Address.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export class Address extends DataCollectorItem {
let query = {}
const lbMined = fields.LAST_BLOCK_MINED
let { fromBlock } = params
query[lbMined] = { $exists: true, $ne: null }
query[lbMined] = { $exists: true, $ne: null, cursorField: 'id' }
if (fromBlock) {
fromBlock = parseInt(fromBlock)
query[`${lbMined}.number`] = { $gt: fromBlock }
Expand Down Expand Up @@ -212,7 +212,7 @@ export class Address extends DataCollectorItem {
const result = await this.getOne({ address }, fields)
let { data } = result
if (!data) throw new Error('Unknown address')
const { createdByTx, code } = data
const { createdByTx, code } = data
if (!code) throw new Error('The address does not have code')
if (createdByTx) {
// is a transaction
Expand Down
76 changes: 74 additions & 2 deletions src/converters/address.converters.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { removeNullFields } from '../repositories/utils'

function rawAddressToEntity ({
address,
blockNumber,
Expand All @@ -9,7 +11,7 @@ function rawAddressToEntity ({
return {
address,
block: blockNumber,
lastBlockMined: lastBlockMined ? lastBlockMined.blockNumber : null,
lastBlockMined: lastBlockMined ? lastBlockMined.number : null,
balance,
isNative,
type
Expand Down Expand Up @@ -42,4 +44,74 @@ function rawContractToEntity ({
}
}

export {rawAddressToEntity, rawContractToEntity}
function addressEntityToRaw ({
address,
balance,
block: blockNumber,
isNative,
block_address_last_block_minedToblock: lastBlockMined,
contract_contract_addressToaddress: contract,
name,
type
}) {
const addressToReturn = {
address,
balance,
blockNumber,
isNative,
name,
type
}

if (lastBlockMined) {
delete lastBlockMined.id
addressToReturn.lastBlockMined = lastBlockMined
}

if (contract) {
Object.assign(addressToReturn, contractEntityToRaw(contract))
}

return removeNullFields(addressToReturn, ['name'])
}

function contractEntityToRaw ({
address,
name,
createdByTx,
createdByInternalTx,
code,
codeStoredAtBlock,
deployedCode,
symbol,
totalSupply,
decimals,
contract_method: methods,
contract_interface: interfaces
}) {
const contractToReturn = {
address,
name,
createdByTx,
createdByInternalTx,
code,
codeStoredAtBlock,
deployedCode,
symbol,
totalSupply,
decimals
}

if (methods) {
const contractMethods = methods.map(({method: {method}}) => method)
contractToReturn.contractMethods = contractMethods
}

if (interfaces) {
const contractInterfaces = interfaces.map(interface_ => interface_.interface_.interface)
contractToReturn.contractInterfaces = contractInterfaces
}

return removeNullFields(contractToReturn)
}
export {rawAddressToEntity, rawContractToEntity, addressEntityToRaw, contractEntityToRaw}
103 changes: 73 additions & 30 deletions src/repositories/address.repository.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,59 @@
import {prismaClient} from '../lib/Setup'
import {rawAddressToEntity, rawContractToEntity} from '../converters/address.converters'
import {
rawAddressToEntity,
rawContractToEntity,
addressEntityToRaw
} from '../converters/address.converters'
import {
createPrismaSelect,
mongoQueryToPrisma,
createPrismaOrderBy
} from './utils'

const addressRelatedTables = {
block_address_last_block_minedToblock: true,
contract_contract_addressToaddress: {
include: {
contract_method: {include: {method: {select: {method: true}}}},
contract_interface: {include: {interface_: {select: {interface: true}}}}
}
}
}

export const addressRepository = {
findOne (query = {}, project = {}, collection) {
return collection.findOne(query, project)
async findOne (query = {}, project = {}, collection) {
const address = await prismaClient.address.findFirst({
where: mongoQueryToPrisma(query),
include: addressRelatedTables,
select: createPrismaSelect(project)
})

return address ? addressEntityToRaw(address) : null
},
find (query = {}, project = {}, collection, sort = {}, limit = 0, isArray = true) {
if (isArray) {
return collection
.find(query, project)
.sort(sort)
.limit(limit)
.toArray()
} else {
return collection
.find(query, project)
.sort(sort)
.limit(limit)
}
async find (query = {}, project = {}, collection, sort = {}, limit = 0, isArray = true) {
const addresses = await prismaClient.address.findMany({
where: mongoQueryToPrisma(query),
include: addressRelatedTables,
select: createPrismaSelect(project),
orderBy: createPrismaOrderBy(sort),
take: limit
})

return addresses.map(addressEntityToRaw)
},
countDocuments (query = {}, collection) {
return collection.countDocuments(query)
async countDocuments (query = {}, collection) {
const count = await prismaClient.address.count({where: mongoQueryToPrisma(query)})

return count
},
aggregate (aggregate, collection) {
return collection.aggregate(aggregate).toArray()
},
async updateOne (filter, update, options = {}, collection) {
const {$set: data} = update
const addressToSave = rawAddressToEntity(data)
await prismaClient.address.upsert({ where: filter, update: addressToSave, create: addressToSave })

const savedAddress = await prismaClient.address.upsert({ where: filter, update: addressToSave, create: addressToSave })

if (data.type === 'contract') {
const {createdByTx, createdByInternalTx, contractMethods, contractInterfaces} = data
Expand All @@ -50,27 +75,45 @@ export const addressRepository = {

if (contractMethods) {
for (const method of contractMethods) {
const savedMethod = await prismaClient.method.upsert({where: {method}, create: {method}, update: {method}})
await prismaClient.contract_method.create({data: {methodId: savedMethod.id, contractAddress: savedContract.address}})
const savedMethod = await prismaClient.method.upsert({where: {method}, create: {method}, update: {}})
const contractMethodToSave = {methodId: savedMethod.id, contractAddress: savedContract.address}
await prismaClient.contract_method.upsert({
where: {
methodId_contractAddress: contractMethodToSave
},
create: contractMethodToSave,
update: {}})
}
}

if (contractInterfaces) {
for (const interf of contractInterfaces) {
const savedInterface = await prismaClient.interface_.upsert({where: {interface: interf}, create: {interface: interf}, update: {interface: interf}})
await prismaClient.contract_interface.create({data: {interfaceId: savedInterface.id, contractAddress: savedContract.address}})
const savedInterface = await prismaClient.interface_.upsert({where: {interface: interf}, create: {interface: interf}, update: {}})
const contractInterfaceToSave = {interfaceId: savedInterface.id, contractAddress: savedContract.address}
await prismaClient.contract_interface.upsert({
where: {
interfaceId_contractAddress: contractInterfaceToSave
},
create: contractInterfaceToSave,
update: {}})
}
}
}

const mongoRes = await collection.updateOne(filter, update, options)
return mongoRes
await collection.updateOne(filter, update, options)

return savedAddress
},
async deleteMany (filter, collection) {
const mongoRes = await collection.deleteMany(filter)
return mongoRes
},
insertOne (data, collection) {
return collection.insertOne(data)
const blockHash = filter['createdByTx.blockHash']
const txs = await prismaClient.transaction.findMany({where: {blockHash}})

const deleted = await prismaClient.address.deleteMany({
where: {
contract_contract_addressToaddress: {createdByTx: {in: txs.map(tx => tx.hash)}}
}
})

return deleted
}
}
42 changes: 30 additions & 12 deletions src/repositories/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ function mongoQueryToPrisma (query) {
$and: 'AND',
$lt: 'lt',
$gt: 'gt',
$eq: 'equals'
$eq: 'equals',
$in: 'in',
$ne: 'not',
}

for (const key in query) {
Expand All @@ -56,7 +58,9 @@ function mongoQueryToPrisma (query) {
} else if (!(typeof value === 'string') && Object.keys(value).length > 0) {
return {[mongoOperatorToPrisma[key] || key]: mongoQueryToPrisma(value)}
} else {
if (key.includes('.')) {
if (key === '$exists') {
return value ? {not: null} : {equals: null}
} else if (key.includes('.')) {
return formatRelationQuery({[key]: value})
} else {
return {[mongoOperatorToPrisma[key] || key]: value}
Expand All @@ -67,21 +71,35 @@ function mongoQueryToPrisma (query) {
return {}
}

function formatRelationQuery (query) {
const [relation, attribute] = Object.keys(query)[0].split('.')
function formatExistsQuery (query) {
const [key] = Object.keys(query)

return {
[relation]: {
[attribute]: query[`${relation}.${attribute}`]
}
[key]: {not: null}
}
}

function removeNullFields (obj) {
for (const key in obj) {
if (Array.isArray(obj[key])) obj[key].forEach(arrValue => removeNullFields(arrValue))
if (typeof (obj[key]) === 'object') removeNullFields(obj[key])
if (obj[key] === null) delete obj[key]
function removeNullFields (obj, nullableFields = []) {
if (typeof obj === 'string') {
return obj
} else {
return Object.entries(obj).reduce((filtered, [key, value]) => {
if (value !== undefined) {
if (value !== null) {
if (value.constructor.name === 'Array') {
filtered[key] = value.map(v => removeNullFields(v))
} else if (value.constructor.name === 'Object') {
filtered[key] = removeNullFields(value)
} else {
filtered[key] = value
}
}
} else if (nullableFields.includes(key)) {
filtered[key] = null
}

return filtered
}, {})
}
}

Expand Down

0 comments on commit 1d3a6dc

Please sign in to comment.