Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added pino as API logger #1498

Merged
merged 2 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"oraclisation",
"oraclise",
"outdir",
"pino",
"Polkadot",
"QGIS",
"roboto",
Expand Down
9 changes: 5 additions & 4 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"test": "vitest"
},
"dependencies": {
"@hono/zod-validator": "0.2.2",
"@sni/address-utils": "workspace:*",
"@sni/clients": "workspace:*",
"@sni/constants": "workspace:*",
Expand All @@ -23,14 +24,14 @@
"@unique-nft/utils": "0.3.19",
"hono": "4.4.10",
"lodash": "4.17.21",
"nanoid": "^5.0.7"
"nanoid": "5.0.7",
"pino": "9.2.0",
"zod": "3.23.8"
},
"devDependencies": {
"@cloudflare/workers-types": "4.20240620.0",
"@hono/zod-validator": "0.2.2",
"env-cmd": "10.1.0",
"vitest": "1.6.0",
"wrangler": "3.62.0",
"zod": "3.23.8"
"wrangler": "3.62.0"
}
}
4 changes: 2 additions & 2 deletions apps/api/src/routes/assets/assets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import app from '.';
describe('Assets API', () => {
it('should handle invalid DID', async () => {
const res = await app.request('http://localhost/invalidDID');
expect(res.status).toEqual(404);
expect(res.status).toEqual(400);
});

it('should handle invalid Asset', async () => {
it('should handle missing Asset', async () => {
const res = await app.request(
'http://localhost/did:asset:deep:polkadot.asset-hub:13:1000000'
);
Expand Down
23 changes: 17 additions & 6 deletions apps/api/src/routes/assets/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Hono } from 'hono';
import { env } from 'hono/adapter';
import { getAssetByDID } from '@sni/clients/assets-client';
import { AssetNotFoundError, getAssetByDID } from '@sni/clients/assets-client';
import { AddressParsingError } from '@sni/address-utils';
import { logger } from '../../utils/logger';

const app = new Hono();

const getAssetRoute = app.get('/:assetDid', async (c) => {
app.get('/:assetDid', async (c) => {
const { OPEN_SEA_API_KEY } = env<{ OPEN_SEA_API_KEY: string }>(c);
const { ALCHEMY_API_KEY } = env<{ ALCHEMY_API_KEY: string }>(c);

Expand All @@ -17,11 +19,20 @@ const getAssetRoute = app.get('/:assetDid', async (c) => {
});
return c.json(assetData);
} catch (e) {
//console.error(JSON.stringify(e)); //TODO: standardize error logging
return c.json({ error: true, message: 'Asset not found' }, 404);
logger.error(e);

if (e instanceof AddressParsingError) {
return c.json({ error: true, message: e.message }, 400);
}

if (e instanceof AssetNotFoundError) {
return c.json({ error: true, message: e.message }, 404);
}

if (e instanceof Error) {
return c.json({ error: true, message: e.message }, 500);
}
}
});

export type GetAssetRoute = typeof getAssetRoute;

export default app;
34 changes: 25 additions & 9 deletions apps/api/src/routes/claims/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { zValidator } from '@hono/zod-validator';
import { Hono } from 'hono';
import { decode, verify } from 'hono/jwt';
import { env } from 'hono/adapter';
import { logger } from '../../utils/logger';
import { CollectionConfig, collections } from './config';
import { ClaimBody, CrossmintResponse, JWTToken } from './schemas';
import { mintOptimismToken } from './providers/crossmint';
Expand Down Expand Up @@ -32,6 +33,7 @@ app.post(
try {
await verify(token, CLAIMS_SECRET);
} catch (e) {
logger.error(e);
return c.json({ error: true, message: 'Invalid token' }, 400);
}

Expand All @@ -48,7 +50,7 @@ app.post(
const mintResponse = await MINTING_KV.get(mintId);

if (mintResponse === null) {
console.log('First time minting token');
logger.info(`Minting ${mintId}`);

const pendingResponse = {
id: payload.id,
Expand All @@ -60,11 +62,24 @@ app.post(
actionId: payload.id,
};

await MINTING_KV.put(mintId, JSON.stringify(pendingResponse));
try {
CrossmintResponse.parse(pendingResponse);
} catch (e) {
logger.error(e);
return c.json(
{
error: true,
message: `Internal server error`,
},
500
);
}

console.log('Sending to queue');
logger.info(`Sending minting ${mintId} to queue`);
await MINTING_KV.put(mintId, JSON.stringify(pendingResponse));
await MINTING_QUEUE.send({ address, payload, collectionConfig });
console.log('Message sent to queue');

logger.debug(`Message queue received ${mintId}`);

return c.json(pendingResponse);
}
Expand Down Expand Up @@ -93,10 +108,10 @@ app.post(
}
);

interface Env {
type Env = {
WALLET_MNEMONIC: string;
MINTING_KV: KVNamespace;
}
};

export async function claimsQueue(batch: MessageBatch<MintRequest>, env: Env) {
for (const message of batch.messages) {
Expand All @@ -110,7 +125,7 @@ export async function claimsQueue(batch: MessageBatch<MintRequest>, env: Env) {
const mintId = mintRequest.payload.id;
try {
//TODO: Add proper logger
console.log('Minting unique token');
logger.info(`Minting ${mintId} on ${network} network`);

const successResponse = await mintUniqueToken(
mintRequest.address,
Expand All @@ -123,9 +138,10 @@ export async function claimsQueue(batch: MessageBatch<MintRequest>, env: Env) {

await env.MINTING_KV.put(mintId, JSON.stringify(parsedResponse));
} catch (e) {
console.log('Error minting token');
console.error(e);
logger.error(`Error minting token on ${network} network`);
logger.error(e);

//Not sure if cleaning is needed on queue retries
await env.MINTING_KV.delete(mintId);
}
}
Expand Down
4 changes: 4 additions & 0 deletions apps/api/src/utils/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import pino from 'pino';

export const logger = pino({ name: 'deep-api' });
// import { APP_ID, LOG_LEVEL } from "./Config";
6 changes: 4 additions & 2 deletions packages/address-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ function getChainName(chainNamespace: string, chainId: string): string {
}
}

export class AddressParsingError extends Error {}

//Parsing AssetDID to it's components
export function parseAssetDID(did: string) {
try {
Expand All @@ -105,7 +107,7 @@ export function parseAssetDID(did: string) {
const tokenId = asset.identifier;
return { network, contractAddress, tokenId };
} catch (e) {
// console.error(e); //TODO: Proper logging?
throw new Error(`Invalid DID address: ${did}`);
//TODO: Throw custom address parsing error
throw new AddressParsingError(`Invalid DID address: ${did}`);
}
}
8 changes: 8 additions & 0 deletions packages/clients/assets-client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,17 @@ type KeysConfig = {
alchemyAPIKey?: string;
};

export class AssetNotFoundError extends Error {
constructor() {
super('Asset not found');
this.name = 'AssetNotFoundError';
}
}

export async function getAssetByDID(did: string, keys?: KeysConfig) {
const parsedData = parseAssetDID(did);

//TODO: Add error handling here
return await getAsset(
parsedData.network,
parsedData.contractAddress,
Expand Down
54 changes: 43 additions & 11 deletions packages/clients/assets-client/targets/polkadot/client.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { GraphQLClient } from 'graphql-request';
import { DeepAsset } from '@sni/types';
import { AssetNotFoundError } from '../..';
import { kusamaApiUrl, polkadotApiUrl } from './config';
import { getNftById } from './queries';
import { PolkadotResponse } from './types';
import polkadotFormatter from './formatter';

const polkadotClient = new GraphQLClient(polkadotApiUrl, { fetch });
const kusamaClient = new GraphQLClient(kusamaApiUrl, { fetch });
Expand All @@ -12,20 +12,52 @@ export async function getPolkadotAsset(
collectionId: string,
tokenId: number
): Promise<DeepAsset> {
return polkadotFormatter(
await polkadotClient.request<PolkadotResponse>(getNftById, {
id: `${collectionId}-${tokenId}`,
})
);
const res = await polkadotClient.request<PolkadotResponse>(getNftById, {
id: `${collectionId}-${tokenId}`,
});

const nftEntity = res.nftEntity;
if (!nftEntity) {
throw new AssetNotFoundError();
}

return {
id: nftEntity.id,
tokenId: nftEntity.sn,
name: nftEntity.meta.name,
description: nftEntity.meta.description,
image: nftEntity.meta.image,
collection: {
id: nftEntity.collection.id,
name: nftEntity.collection.name,
},
address: '', //TODO: Add DID address to the response
};
}

export async function getKusamaAsset(
collectionId: string,
tokenId: number
): Promise<DeepAsset> {
return polkadotFormatter(
await kusamaClient.request<PolkadotResponse>(getNftById, {
id: `${collectionId}-${tokenId}`,
})
);
const res = await kusamaClient.request<PolkadotResponse>(getNftById, {
id: `${collectionId}-${tokenId}`,
});

const nftEntity = res.nftEntity;
if (!nftEntity) {
throw new AssetNotFoundError();
}

return {
id: nftEntity.id,
tokenId: nftEntity.sn,
name: nftEntity.meta.name,
description: nftEntity.meta.description,
image: nftEntity.meta.image,
collection: {
id: nftEntity.collection.id,
name: nftEntity.collection.name,
},
address: '', //TODO: Add DID address to the response
};
}
21 changes: 0 additions & 21 deletions packages/clients/assets-client/targets/polkadot/formatter.ts

This file was deleted.

Loading
Loading