Skip to content

Commit

Permalink
Merge pull request #89 from ixofoundation/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Michael-Ixo authored Jun 18, 2023
2 parents 46f6921 + 0f53773 commit db2b396
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 78 deletions.
31 changes: 29 additions & 2 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,10 @@ app.get("/api/claims/collection/:id/claims", async (req, res, next) => {
const claims = await ClaimsHandler.getCollectionClaims(
req.params.id,
req.query.status as string,
req.query.type as string
req.query.type as string,
req.query.take as string,
req.query.cursor as string,
req.query.orderBy as any
);
res.json(claims);
} catch (error) {
Expand Down Expand Up @@ -346,6 +349,30 @@ app.get("/api/token/entity/:id", async (req, res, next) => {
}
});

app.get("/api/token/totalByAddress/:address", async (req, res, next) => {
try {
const tokens = await TokenHandler.getTokensTotalByAddress(
req.params.address,
(req.query?.name || "") as string
);
res.json(tokens);
} catch (error) {
next(error);
}
});

app.get("/api/token/totalForEntities/:address", async (req, res, next) => {
try {
const tokens = await TokenHandler.getTokensTotalForEntities(
req.params.address,
(req.query?.name || "") as string
);
res.json(tokens);
} catch (error) {
next(error);
}
});

app.get("/api/token/collection/:id", async (req, res, next) => {
try {
const tokens = await TokenHandler.getTokensByCollection(req.params.id);
Expand Down Expand Up @@ -641,7 +668,7 @@ app.get("/api/block/getLastSyncedBlock", async (req, res, next) => {
// CRON Jobs
// =================================
// Get antity type "asset/device" with no externalId and check if it has a deviceCredential
// Since ipfs rate limit is 200 per minute, we do 100 every 1 minutes to lesser strain
// Since ipfs rate limit is 200 per minute, we do 100 every 1 minutes to lessen strain
new CronJob(
"1 */1 * * * *",
function () {
Expand Down
191 changes: 121 additions & 70 deletions src/handlers/claims_handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { parseJson, prisma } from "../prisma/prisma_client";
import { customQueries } from "@ixo/impactxclient-sdk";
import { Claim } from "@prisma/client";

export const getCollectionById = async (id: string, includeClaims = false) => {
const collection = await prisma.claimCollection.findFirst({
Expand All @@ -16,89 +15,96 @@ export const getCollectionById = async (id: string, includeClaims = false) => {
return collection;
};

/**
* Will return empty list if not all claims schemaTypes have been fetched from cellnode
* if empty list, call getCollectionClaims again after 1 minute to avoid cellnode rate limit
*/
export const getCollectionClaims = async (
id: string,
status?: string,
type?: string
type?: string,
take?: string,
cursor?: string,
orderBy: "asc" | "desc" = "asc"
) => {
const cleanStatus = status ? parseInt(status) : null;
const claims = await prisma.claim.findMany({
where: {
AND: [
{
collectionId: id,
},
type
? {
OR: [{ schemaType: type }, { schemaType: null }],
}
: {},
!cleanStatus
? {}
: cleanStatus === 0
? { evaluation: null }
: { evaluation: { status: cleanStatus } },
],
},
take: 50000,
include: {
evaluation: true,
},
});
const cleanStatus = status ? parseInt(status) : undefined;

if (type) {
// Get Collection Entity to get Collection Cellnode Service URI
const collection = await getCollectionById(id);
const entity = await prisma.entity.findFirst({
const query = async (
take?: string,
status?: number,
type?: string | null,
cursor?: string,
includeTypeNull = false
) =>
await prisma.claim.findMany({
where: {
id: collection.entity,
AND: [
{
collectionId: id,
},
type === undefined
? {}
: {
OR: includeTypeNull
? [{ schemaType: type }, { schemaType: null }]
: [{ schemaType: type }],
},
status === null
? {}
: status === 0
? { evaluation: null }
: { evaluation: { status: status } },
],
},
take: take ? Number(take) : 1000,
...(cursor && {
cursor: { aid: Number(cursor) },
skip: 1,
}),
include: {
IID: {
include: {
service: true,
},
},
evaluation: true,
},
orderBy: { aid: orderBy },
});
const cellnodeUri = entity!.IID.service.find((s) =>
s.id.includes("cellnode")
);

if (!cellnodeUri) return [];

const correctClaims = claims.filter((c) => c.schemaType === type);
const promises = claims
.filter((c) => !c.schemaType)
.map(async (c) => {
const res = await customQueries.cellnode.getPublicDoc(
c.claimId,
cellnodeUri!.serviceEndpoint
);
if (!res) return null;
const type: string =
(res.type || []).find((t) => t.includes("claim:")) ||
(res.type || []).find((t) => t.includes("ixo:")) ||
(res.type || []).find((t) => !!t);
if (!type) return null;
const typeSplit = type.split(":");
const claim = await prisma.claim.update({
where: {
claimId: c.claimId,
},
data: {
schemaType: typeSplit[typeSplit.length - 1],
},
});
return claim;
});
const res = await Promise.all(promises);
const correctClaims2 = res.filter((c) => c?.schemaType === type) as any[];
// get claims with schemaType null and fetch schemaType from cellnode
let claims = await query("8000", cleanStatus, null);
await getClaimTypesFromCellnode(id, claims);

return correctClaims.concat(correctClaims2);
// if any more claims with schemaType null, return empty list
claims = await query("1", cleanStatus, null);
if (claims.length && !!type) {
return {
data: [],
metaData: {
cursor: null,
hasNextPage: false,
schemaTypesLoaded: false,
},
};
}

return claims;
claims = await query(take, cleanStatus, type, cursor);
if (claims.length == 0) {
return {
data: [],
metaData: {
cursor: null,
hasNextPage: false,
schemaTypesLoaded: true,
},
};
}
const nextCursor: any = claims[claims.length - 1].aid;
const nextPage = await query("1", cleanStatus, type, nextCursor);
return {
data: claims,
metaData: {
cursor: nextCursor,
hasNextPage: nextPage.length > 0,
schemaTypesLoaded: true,
},
};
};

export const getClaimById = async (id: string) => {
Expand All @@ -114,3 +120,48 @@ export const getClaimById = async (id: string) => {
claim!.paymentsStatus = parseJson(claim!.paymentsStatus);
return claim;
};

export const getClaimTypesFromCellnode = async (
collectionID: string,
claims: any[]
) => {
// Get Collection Entity to get Collection Cellnode Service URI
const collection = await getCollectionById(collectionID);
const entity = await prisma.entity.findFirst({
where: { id: collection.entity },
include: { IID: { include: { service: true } } },
});
const cellnodeUri = entity!.IID.service.find((s) =>
s.id.includes("cellnode")
);
if (!cellnodeUri) throw new Error("Cellnode service not found");

// fetch schemaType from cellnode
const promises = claims.map(async (c) => {
let type: string | null = null;
try {
const res = await customQueries.cellnode.getPublicDoc(
c.claimId,
cellnodeUri.serviceEndpoint
);
type =
(res.type || []).find((t) => t.includes("claim:")) ||
(res.type || []).find((t) => t.includes("ixo:")) ||
(res.type || []).find((t) => !!t);
if (!type) throw new Error("Claim type not found");
const typeSplit = type!.split(":");
type = typeSplit[typeSplit.length - 1];
} catch (error) {
// if error 404 then claim not on cellnode, type "unknown"
if (error.response?.status === 404) type = "unknown";
else console.error(error.message);
} finally {
if (type)
await prisma.claim.update({
where: { claimId: c.claimId },
data: { schemaType: type },
});
}
});
await Promise.all(promises);
};
61 changes: 59 additions & 2 deletions src/handlers/token_handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { prisma } from "../prisma/prisma_client";
import { parseJson, prisma } from "../prisma/prisma_client";

export const getTokenClassByContractAddress = async (
contractAddress: string
Expand Down Expand Up @@ -80,10 +80,59 @@ export const getRetiredTokens = async (id: string) => {
});
};

export const getAccountTokens = async (address: string, name: string) => {
export const getTokensTotalByAddress = async (
address: string,
name?: string
) => {
const tokens = await getAccountTokens(address, name);
Object.keys(tokens).forEach((key) => {
const newTokens = {};
Object.values(tokens[key].tokens).forEach((t: any) => {
if (!newTokens[t.collection]) {
newTokens[t.collection] = {
amount: t.amount,
minted: t.minted,
retired: t.retired,
};
} else {
newTokens[t.collection].amount += t.amount;
newTokens[t.collection].minted += t.minted;
newTokens[t.collection].retired += t.retired;
}
});
tokens[key].tokens = newTokens;
});
return tokens;
};

export const getTokensTotalForEntities = async (
address: string,
name?: string
) => {
const entities =
(await prisma.entity.findMany({
where: { owner: address, type: "asset/device" },
select: { id: true, accounts: true },
})) || [];

const tokens = entities.map(async (entity) => {
const entityTokens = await getTokensTotalByAddress(
parseJson(entity.accounts).find((a) => a.name === "admin")?.address,
name
);
return { entity: entity.id, tokens: entityTokens };
});

const tokensTotal = await Promise.all(tokens);

return tokensTotal.filter((t) => Object.keys(t.tokens).length > 0);
};

export const getAccountTokens = async (address: string, name?: string) => {
const tokenTransactions = await prisma.tokenTransaction.findMany({
where: {
OR: [{ from: address }, { to: address }],
...(name && { Token: { name: name } }),
},
include: {
Token: {
Expand Down Expand Up @@ -117,8 +166,15 @@ export const getAccountTokens = async (address: string, name: string) => {
collection: curr.Token.collection,
amount: -Number(curr.amount),
minted: 0,
retired: 0,
};
}
// if no to it means was retired from this address
if (!curr.to) {
tokens[curr.Token.name].tokens[curr.tokenId].retired += Number(
curr.amount
);
}
// if to address is same it means it is an incoming transaction in relation to the address
} else {
if (tokens[curr.Token.name].tokens[curr.tokenId]) {
Expand All @@ -130,6 +186,7 @@ export const getAccountTokens = async (address: string, name: string) => {
collection: curr.Token.collection,
amount: Number(curr.amount),
minted: 0,
retired: 0,
};
}
// if no from it means was minted to address
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ CREATE TABLE "ClaimCollection" (

-- CreateTable
CREATE TABLE "Claim" (
"aid" SERIAL NOT NULL,
"claimId" TEXT NOT NULL,
"agentDid" TEXT NOT NULL,
"agentAddress" TEXT NOT NULL,
Expand All @@ -139,7 +140,7 @@ CREATE TABLE "Claim" (
"collectionId" TEXT NOT NULL,
"schemaType" TEXT,

CONSTRAINT "Claim_pkey" PRIMARY KEY ("claimId")
CONSTRAINT "Claim_pkey" PRIMARY KEY ("aid")
);

-- CreateTable
Expand Down Expand Up @@ -458,6 +459,9 @@ CREATE TABLE "Ipfs" (
CONSTRAINT "Ipfs_pkey" PRIMARY KEY ("cid")
);

-- CreateIndex
CREATE UNIQUE INDEX "Claim_claimId_key" ON "Claim"("claimId");

-- CreateIndex
CREATE UNIQUE INDEX "Evaluation_claimId_key" ON "Evaluation"("claimId");

Expand Down
Loading

0 comments on commit db2b396

Please sign in to comment.