Skip to content

Commit

Permalink
fix: correctly classify image errors vs metadata errors (#251)
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelcr authored Aug 27, 2024
1 parent c57d963 commit 7369554
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 25 deletions.
16 changes: 8 additions & 8 deletions src/token-processor/util/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,22 +95,22 @@ export class StacksNodeHttpError extends Error {

export function getUserErrorInvalidReason(error: UserError): DbJobInvalidReason {
switch (true) {
case error instanceof MetadataSizeExceededError:
return DbJobInvalidReason.metadataSizeExceeded;
case error instanceof ImageSizeExceededError:
return DbJobInvalidReason.imageSizeExceeded;
case error instanceof MetadataTimeoutError:
return DbJobInvalidReason.metadataTimeout;
case error instanceof MetadataSizeExceededError:
return DbJobInvalidReason.metadataSizeExceeded;
case error instanceof ImageTimeoutError:
return DbJobInvalidReason.imageTimeout;
case error instanceof MetadataParseError:
return DbJobInvalidReason.metadataParseFailed;
case error instanceof MetadataTimeoutError:
return DbJobInvalidReason.metadataTimeout;
case error instanceof ImageParseError:
return DbJobInvalidReason.imageParseFailed;
case error instanceof MetadataHttpError:
return DbJobInvalidReason.metadataHttpError;
case error instanceof MetadataParseError:
return DbJobInvalidReason.metadataParseFailed;
case error instanceof ImageHttpError:
return DbJobInvalidReason.imageHttpError;
case error instanceof MetadataHttpError:
return DbJobInvalidReason.metadataHttpError;
case error instanceof SmartContractClarityError:
return DbJobInvalidReason.tokenContractClarityError;
default:
Expand Down
27 changes: 22 additions & 5 deletions src/token-processor/util/metadata-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ export async function fetchAllMetadataLocalesFromBaseUri(
try {
const rawMetadataLocales: RawMetadataLocale[] = [];

const defaultMetadata = await getMetadataFromUri(tokenUri);
const defaultMetadata = await getMetadataFromUri(
tokenUri,
contract.principal,
token.token_number
);
rawMetadataLocales.push({
metadata: defaultMetadata,
default: true,
Expand All @@ -83,7 +87,11 @@ export async function fetchAllMetadataLocalesFromBaseUri(
continue;
}
const localeUri = getTokenSpecificUri(uri, token.token_number, locale);
const localeMetadata = await getMetadataFromUri(localeUri);
const localeMetadata = await getMetadataFromUri(
localeUri,
contract.principal,
token.token_number
);
rawMetadataLocales.push({
metadata: localeMetadata,
locale: locale,
Expand Down Expand Up @@ -234,9 +242,14 @@ async function parseMetadataForInsertion(
* @param httpUrl - URL to fetch
* @returns Response text
*/
export async function fetchMetadata(httpUrl: URL): Promise<string | undefined> {
export async function fetchMetadata(
httpUrl: URL,
contract_principal: string,
token_number: bigint
): Promise<string | undefined> {
const url = httpUrl.toString();
try {
logger.info(`MetadataFetch for ${contract_principal}#${token_number} from ${url}`);
const result = await request(url, {
method: 'GET',
throwOnError: true,
Expand Down Expand Up @@ -266,7 +279,11 @@ export async function fetchMetadata(httpUrl: URL): Promise<string | undefined> {
}
}

export async function getMetadataFromUri(token_uri: string): Promise<RawMetadata> {
export async function getMetadataFromUri(
token_uri: string,
contract_principal: string,
token_number: bigint
): Promise<RawMetadata> {
// Support JSON embedded in a Data URL
if (new URL(token_uri).protocol === 'data:') {
const dataUrl = parseDataUrl(token_uri);
Expand All @@ -292,7 +309,7 @@ export async function getMetadataFromUri(token_uri: string): Promise<RawMetadata
// Support HTTP/S URLs otherwise
const httpUrl = getFetchableDecentralizedStorageUrl(token_uri);
const urlStr = httpUrl.toString();
const content = await fetchMetadata(httpUrl);
const content = await fetchMetadata(httpUrl, contract_principal, token_number);
return parseJsonMetadata(urlStr, content);
}

Expand Down
8 changes: 4 additions & 4 deletions tests/token-queue/image-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { ENV } from '../../src/env';
import { processImageCache } from '../../src/token-processor/images/image-cache';
import { closeTestServer, startTestResponseServer, startTimeoutServer } from '../helpers';
import {
MetadataHttpError,
MetadataTimeoutError,
ImageHttpError,
ImageTimeoutError,
TooManyRequestsHttpError,
} from '../../src/token-processor/util/errors';

Expand All @@ -22,7 +22,7 @@ describe('Image cache', () => {
const server = await startTimeoutServer(100);
await expect(
processImageCache('http://127.0.0.1:9999/', contract, tokenNumber)
).rejects.toThrow(MetadataTimeoutError);
).rejects.toThrow(ImageTimeoutError);
await closeTestServer(server);
}, 10000);

Expand All @@ -38,7 +38,7 @@ describe('Image cache', () => {
const server = await startTestResponseServer('not found', 404);
await expect(
processImageCache('http://127.0.0.1:9999/', contract, tokenNumber)
).rejects.toThrow(MetadataHttpError);
).rejects.toThrow(ImageHttpError);
await closeTestServer(server);
}, 10000);
});
19 changes: 11 additions & 8 deletions tests/token-queue/metadata-helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
getTokenSpecificUri,
fetchMetadata,
} from '../../src/token-processor/util/metadata-helpers';
import { RetryableJobError } from '../../src/token-processor/queue/errors';

describe('Metadata Helpers', () => {
test('performs timed and limited request', async () => {
Expand All @@ -24,7 +23,7 @@ describe('Metadata Helpers', () => {
.reply(200, 'hello');
setGlobalDispatcher(agent);

const result = await fetchMetadata(url);
const result = await fetchMetadata(url, 'ABCD.test', 1n);
expect(result).toBe('hello');
});

Expand All @@ -40,7 +39,9 @@ describe('Metadata Helpers', () => {
.reply(200, '[{"test-bad-json": true}]');
setGlobalDispatcher(agent);

await expect(getMetadataFromUri('http://test.io/1.json')).rejects.toThrow(/JSON parse error/);
await expect(getMetadataFromUri('http://test.io/1.json', 'ABCD.test', 1n)).rejects.toThrow(
/JSON parse error/
);
});

test('throws metadata http errors', async () => {
Expand All @@ -56,7 +57,7 @@ describe('Metadata Helpers', () => {
.reply(500, { message: 'server error' });
setGlobalDispatcher(agent);

await expect(fetchMetadata(url)).rejects.toThrow(MetadataHttpError);
await expect(fetchMetadata(url, 'ABCD.test', 1n)).rejects.toThrow(MetadataHttpError);
});

test('does not throw on raw metadata with null or stringable values', async () => {
Expand Down Expand Up @@ -91,7 +92,9 @@ describe('Metadata Helpers', () => {
.reply(200, crashPunks1);
setGlobalDispatcher(agent);

await expect(getMetadataFromUri('http://test.io/1.json')).resolves.not.toThrow();
await expect(
getMetadataFromUri('http://test.io/1.json', 'ABCD.test', 1n)
).resolves.not.toThrow();
});

test('fetches typed raw metadata', async () => {
Expand Down Expand Up @@ -127,7 +130,7 @@ describe('Metadata Helpers', () => {
.reply(200, json);
setGlobalDispatcher(agent);

const metadata = await getMetadataFromUri('http://test.io/1.json');
const metadata = await getMetadataFromUri('http://test.io/1.json', 'ABCD.test', 1n);
expect(metadata.name).toBe('Mutant Monkeys #27');
expect(metadata.image).toBe(
'https://byzantion.mypinata.cloud/ipfs/QmbNC9qvcYZugaeGeReDhyYiNH7oPzrCX1cZUnQeszFz4P'
Expand All @@ -154,7 +157,7 @@ describe('Metadata Helpers', () => {
.reply(200, json);
setGlobalDispatcher(agent);

const metadata = await getMetadataFromUri('http://test.io/1.json');
const metadata = await getMetadataFromUri('http://test.io/1.json', 'ABCD.test', 1n);
expect(metadata.name).toBe('Boombox [4th Edition]');
expect(metadata.description).toBe(
'The first ever Boombox to exist IRL, this art was created by 3D printing a model and photographing it under some very Boomerific lighting. 💥'
Expand Down Expand Up @@ -228,6 +231,6 @@ describe('Metadata Helpers', () => {
.replyWithError(Object.assign(new TypeError(), { cause: new Error('read ECONNRESET') }));
setGlobalDispatcher(agent);

await expect(fetchMetadata(url)).rejects.toThrow(MetadataHttpError);
await expect(fetchMetadata(url, 'ABCD.test', 1n)).rejects.toThrow(MetadataHttpError);
});
});

0 comments on commit 7369554

Please sign in to comment.