Skip to content

Commit

Permalink
refactor(cardano-services): handle CardanoTokenRegistry errors
Browse files Browse the repository at this point in the history
- lift up error handing into `DbSyncAssetProvider
- return `undefined` if there is an error while fetch from token metadata server
  • Loading branch information
Ivaylo Andonov committed Sep 12, 2022
1 parent 3b69ea4 commit 938bf9c
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 27 deletions.
10 changes: 9 additions & 1 deletion packages/cardano-services/src/Asset/CardanoTokenRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,15 @@ export class CardanoTokenRegistry implements TokenMetadataService {
}
}
} catch (error) {
throw toProviderError(error, 'while fetching metadata from token registry');
if (axios.isAxiosError(error)) {
throw new ProviderError(
ProviderFailure.ConnectionFailure,
error,
'CardanoTokenRegistry failed to fetch asset metatada from the token registry server'
);
}

throw error;
}

return tokenMetadata;
Expand Down
13 changes: 11 additions & 2 deletions packages/cardano-services/src/Asset/DbSyncAssetProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,17 @@ export class DbSyncAssetProvider extends DbSyncProvider implements AssetProvider
if (extraData?.history) await this.loadHistory(assetInfo);
if (extraData?.nftMetadata)
assetInfo.nftMetadata = await this.#dependencies.ntfMetadataService.getNftMetadata(assetInfo);
if (extraData?.tokenMetadata)
assetInfo.tokenMetadata = (await this.#dependencies.tokenMetadataService.getTokenMetadata([assetId]))[0];
if (extraData?.tokenMetadata) {
try {
assetInfo.tokenMetadata = (await this.#dependencies.tokenMetadataService.getTokenMetadata([assetId]))[0];
} catch (error) {
if (error instanceof ProviderError && error.reason === ProviderFailure.ConnectionFailure) {
assetInfo.tokenMetadata = undefined;
} else {
throw error;
}
}
}

return assetInfo;
}
Expand Down
4 changes: 3 additions & 1 deletion packages/cardano-services/src/Asset/openApi.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@
}
},
"GetAssetRequest": {
"required": ["assetId"],
"required": [
"assetId"
],
"type": "object",
"properties": {
"assetId": {
Expand Down
61 changes: 40 additions & 21 deletions packages/cardano-services/test/Asset/CardanoTokenRegistry.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Asset, Cardano, ProviderError } from '@cardano-sdk/core';
import { Cardano, ProviderError, ProviderFailure } from '@cardano-sdk/core';
import { CardanoTokenRegistry, toCoreTokenMetadata } from '../../src/Asset';
import { InMemoryCache, Key } from '../../src/InMemoryCache';
import { IncomingMessage, createServer } from 'http';
Expand Down Expand Up @@ -135,11 +135,11 @@ describe('CardanoTokenRegistry', () => {
});

it('returns metadata when subject exists', async () => {
const [metadata] = await tokenRegistry.getTokenMetadata([validAssetId]);
const metadata = await tokenRegistry.getTokenMetadata([validAssetId]);

expect(metadata).not.toBeNull();
expect(metadata![0]).not.toBeNull();

const result: Asset.TokenMetadata = {
const result = {
decimals: 8,
desc: 'SingularityNET',
icon: 'testLogo',
Expand All @@ -148,17 +148,17 @@ describe('CardanoTokenRegistry', () => {
url: 'https://singularitynet.io/'
};

expect(metadata).toEqual(result);
expect(metadata![0]).toEqual(result);
});

it('correctly returns null or metadata for request with good and bad assetIds', async () => {
const firstResult = await tokenRegistry.getTokenMetadata([invalidAssetId, validAssetId]);
const secondResult = await tokenRegistry.getTokenMetadata([validAssetId, invalidAssetId]);

expect(firstResult[0]).toBeNull();
expect(firstResult[1]).not.toBeNull();
expect(secondResult[0]).not.toBeNull();
expect(secondResult[1]).toBeNull();
expect(firstResult![0]).toBeNull();
expect(firstResult![1]).not.toBeNull();
expect(secondResult![0]).not.toBeNull();
expect(secondResult![1]).toBeNull();
});
});

Expand Down Expand Up @@ -193,12 +193,12 @@ describe('CardanoTokenRegistry', () => {
});

it('metadata are cached', async () => {
const [firstResult] = await tokenRegistry.getTokenMetadata([validAssetId]);
const [secondResult] = await tokenRegistry.getTokenMetadata([validAssetId]);
const firstResult = await tokenRegistry.getTokenMetadata([validAssetId]);
const secondResult = await tokenRegistry.getTokenMetadata([validAssetId]);

expect(gotValues[0]).toBeUndefined();
expect(gotValues[1]).toEqual(firstResult);
expect(firstResult).toEqual(secondResult);
expect(gotValues[1]).toEqual(firstResult![0]);
expect(firstResult![0]).toEqual(secondResult![0]);
});
});

Expand All @@ -214,18 +214,33 @@ describe('CardanoTokenRegistry', () => {
({ closeMock, tokenMetadataServerUrl } = await mockTokenRegistry(() => ({ body: { subjects: [null] } })));
const tokenRegistry = new CardanoTokenRegistry({ logger }, { tokenMetadataServerUrl });

await expect(tokenRegistry.getTokenMetadata([validAssetId])).rejects.toThrow(ProviderError);
await expect(tokenRegistry.getTokenMetadata([validAssetId])).rejects.toThrow(
new ProviderError(
ProviderFailure.Unknown,
undefined,
"Cannot destructure property 'subject' of 'record' as it is null. while evaluating metatada record null"
)
);
});

it('record without the subject property', async () => {
const record = { test: 'test' };
({ closeMock, tokenMetadataServerUrl } = await mockTokenRegistry(() => ({ body: { subjects: [record] } })));
const tokenRegistry = new CardanoTokenRegistry({ logger }, { tokenMetadataServerUrl });

await expect(tokenRegistry.getTokenMetadata([validAssetId])).rejects.toThrow(ProviderError);
await expect(tokenRegistry.getTokenMetadata([validAssetId])).rejects.toThrow(
new ProviderError(
ProviderFailure.InvalidResponse,
undefined,
'Missing \'subject\' property in metadata record {"test":"test"}'
)
);
});

it('internal server error', async () => {
const failedMetadata = null;
const succeededMetadata = { name: 'test' };

let alreadyCalled = false;
const record = () => {
if (alreadyCalled) return { body: {}, code: 500 };
Expand All @@ -243,12 +258,16 @@ describe('CardanoTokenRegistry', () => {

({ closeMock, tokenMetadataServerUrl } = await mockTokenRegistry(record));
const tokenRegistry = new CardanoTokenRegistry({ logger }, { tokenMetadataServerUrl });
const result = await tokenRegistry.getTokenMetadata([invalidAssetId, validAssetId]);

await expect(tokenRegistry.getTokenMetadata([invalidAssetId, validAssetId])).rejects.toThrow(ProviderError);

expect(result[0]).toBeNull();
expect(result[1]).toStrictEqual({ name: 'test' });
const firstSucceedResult = await tokenRegistry.getTokenMetadata([invalidAssetId, validAssetId]);
expect(firstSucceedResult).toEqual([failedMetadata, succeededMetadata]);

await expect(tokenRegistry.getTokenMetadata([invalidAssetId, validAssetId])).rejects.toThrow(
new ProviderError(
ProviderFailure.ConnectionFailure,
null,
'CardanoTokenRegistry failed to fetch asset metatada from the token registry server'
)
);
});
});
});
23 changes: 21 additions & 2 deletions packages/cardano-services/test/Asset/DbSyncAssetProvider.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Asset, Cardano, ProviderError } from '@cardano-sdk/core';
/* eslint-disable @typescript-eslint/no-shadow */
import { Asset, Cardano, ProviderError, ProviderFailure } from '@cardano-sdk/core';
import {
CardanoTokenRegistry,
DbSyncAssetProvider,
Expand Down Expand Up @@ -43,7 +44,9 @@ describe('DbSyncAssetProvider', () => {
});

it('rejects for not found assetId', async () => {
await expect(provider.getAsset({ assetId: notValidAssetId })).rejects.toThrow(ProviderError);
await expect(provider.getAsset({ assetId: notValidAssetId })).rejects.toThrow(
new ProviderError(ProviderFailure.NotFound, undefined, 'No entries found in multi_asset table')
);
});

it('returns an AssetInfo without extra data', async () => {
Expand Down Expand Up @@ -80,4 +83,20 @@ describe('DbSyncAssetProvider', () => {
name: 'macaron cake token'
});
});

it('returns undefined asset token metadata if the token registry throws a network error', async () => {
const { tokenMetadataServerUrl, closeMock } = await mockTokenRegistry(() => ({ body: {}, code: 500 }));
const tokenMetadataService = new CardanoTokenRegistry({ logger }, { tokenMetadataServerUrl });

provider = new DbSyncAssetProvider({ db, logger, ntfMetadataService, tokenMetadataService });

const asset = await provider.getAsset({
assetId: validAssetId,
extraData: { tokenMetadata: true }
});

expect(asset.tokenMetadata).toBeUndefined();
tokenMetadataService.shutdown();
await closeMock();
});
});

0 comments on commit 938bf9c

Please sign in to comment.