Skip to content

Commit

Permalink
refactor: review
Browse files Browse the repository at this point in the history
  • Loading branch information
phamphong9981 committed Sep 12, 2024
1 parent 0d96df8 commit bbb69a0
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 84 deletions.
153 changes: 77 additions & 76 deletions src/services/evm/erc721_media_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ import { S3Service } from '../../common/utils/s3';
const SUPPORT_DECODED_TOKEN_URI = {
BASE64: 'data:application/json;base64',
};
const TOKEN_URI_FORMAT = {
IPFS: 'IPFS',
BASE64: 'BASE64',
JSON: 'JSON',
};
export interface ITokenMediaInfo {
erc721_token_id: number;
address: string;
Expand Down Expand Up @@ -53,64 +48,80 @@ const {
// download image/animation from media_uri, then upload to S3
export async function uploadMediaToS3(media_uri?: string) {
if (media_uri) {
const fileName = parseFilename(media_uri);
if (!fileName) {
return null;
}
const uploadAttachmentToS3 = async (
type: string | undefined,
buffer: Buffer
) => {
const params = {
Key: fileName,
Body: buffer,
Bucket: BUCKET,
ContentType: type,
};
return s3Client
.upload(params)
.promise()
.then(
(response: { Location: string; Key: string }) => ({
linkS3: S3_GATEWAY + response.Key,
contentType: type,
key: response.Key,
}),
(err: string) => {
throw new Error(err);
const fileName = parseFilenameFromIPFS(media_uri); //
if (fileName) {
// case media uri is ipfs supported
try {
const s3Object = await s3Client
.headObject({
Bucket: BUCKET,
Key: fileName,
})
.promise();
return {
linkS3: S3_GATEWAY + fileName,
contentType: s3Object.ContentType,
key: fileName,
};
} catch (e) {
const error = e as AWSError;
if (error.statusCode === 404) {
const mediaBuffer = await downloadAttachment(parseIPFSUri(media_uri));
let type: string | undefined = (
await FileType.fileTypeFromBuffer(mediaBuffer)
)?.mime;
if (type === 'application/xml') {
type = 'image/svg+xml';
}
);
};
try {
const s3Object = await s3Client
.headObject({
Bucket: BUCKET,
Key: fileName,
})
.promise();
return {
linkS3: S3_GATEWAY + fileName,
contentType: s3Object.ContentType,
key: fileName,
};
} catch (e) {
const error = e as AWSError;
if (error.statusCode === 404) {
const mediaBuffer = await downloadAttachment(parseIPFSUri(media_uri));
let type: string | undefined = (
await FileType.fileTypeFromBuffer(mediaBuffer)
)?.mime;
if (type === 'application/xml') {
type = 'image/svg+xml';
return uploadAttachmentToS3(fileName, type, mediaBuffer);
}
return uploadAttachmentToS3(type, mediaBuffer);
throw e;
}
throw e;
} else {
// case media uri isnot ipfs supported or http/https
const mediaBuffer = await downloadAttachment(parseIPFSUri(media_uri));
let type: string | undefined = (
await FileType.fileTypeFromBuffer(mediaBuffer)
)?.mime;
if (type === 'application/xml') {
type = 'image/svg+xml';
}
return {
linkS3: media_uri,
contentType: type,
key: undefined,
};
}
}
return null;
}

async function uploadAttachmentToS3(
file: string,
type: string | undefined,
buffer: Buffer
) {
const params = {
Key: file,
Body: buffer,
Bucket: BUCKET,
ContentType: type,
};
return s3Client
.upload(params)
.promise()
.then(
(response: { Location: string; Key: string }) => ({
linkS3: S3_GATEWAY + response.Key,
contentType: type,
key: response.Key,
}),
(err: string) => {
throw new Error(err);
}
);
}

// update s3 media link
export async function updateMediaS3(
tokenMediaInfo: ITokenMediaInfo,
Expand Down Expand Up @@ -177,31 +188,22 @@ export async function getMetadata(token_uri: string): Promise<{
image?: string;
animation_url?: string;
}> {
const tokenUriType = detechTokenUriFormat(token_uri);
switch (tokenUriType) {
case TOKEN_URI_FORMAT.BASE64: {
let metadata = '{}';
try {
if (token_uri.split(',')[0] === SUPPORT_DECODED_TOKEN_URI.BASE64) {
const base64Metadata = token_uri.split(',')[1];
return JSON.parse(fromUtf8(fromBase64(base64Metadata)));
}
case TOKEN_URI_FORMAT.JSON:
return JSON.parse(token_uri);
default: {
const metadata = await downloadAttachment(parseIPFSUri(token_uri));
return JSON.parse(metadata.toString());
metadata = fromUtf8(fromBase64(base64Metadata));
}
}
}
function detechTokenUriFormat(token_uri: string) {
if (token_uri.split(',')[0] === SUPPORT_DECODED_TOKEN_URI.BASE64) {
return TOKEN_URI_FORMAT.BASE64;
} catch {
// not base64
}
try {
JSON.parse(token_uri);
return TOKEN_URI_FORMAT.JSON;
metadata = JSON.parse(token_uri);
} catch {
// do nothing
// not json
}
return TOKEN_URI_FORMAT.IPFS;
metadata = (await downloadAttachment(parseIPFSUri(token_uri))).toString();
return JSON.parse(metadata);
}

// dowload image/animation from url
Expand All @@ -221,7 +223,7 @@ export async function downloadAttachment(url: string) {
}

// parse filename which be stored in AWS S3
export function parseFilename(media_uri: string) {
export function parseFilenameFromIPFS(media_uri: string) {
const parsed = parse(media_uri);
if (parsed.protocol === IPFS_PREFIX) {
const cid = parsed.host;
Expand All @@ -238,7 +240,6 @@ export function parseFilename(media_uri: string) {
}
return parsed.path.substring(1); // http://ipfs.io/ipfs/QmWov9DpE1vYZtTH7JLKXb7b8bJycN91rEPJEmXRXdmh2G/nerd_access_pass.gif
}
return parsed.path.substring(1); // https://github.com/storyprotocol/protocol-core/blob/main/assets/license-image.gif
}
if (media_uri.startsWith('/ipfs/')) {
return media_uri.substring(1); // /ipfs/QmPAGifcMvxDBgYr1XmEz9gZiC3DEkfYeinFdVSe364uQp/689.png
Expand Down
18 changes: 10 additions & 8 deletions test/unit/services/erc721/erc721-media.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,29 +131,30 @@ export default class TestErc721MediaService {
expect(parsedIpfsPath).toEqual(`${IPFS_GATEWAY}${path}`);
}

@Test('Test parseFilename function')
public async testParseFilename() {
@Test('Test parseFilenameFromIPFS function')
public async testParseFilenameFromIPFS() {
const host = 'ipfs';
const path =
'Qme33YMXArHQzDdgRxQuL6m7JDJNDKeAUyJXDQU3wnL7sf/1000_F_260918513_EtP8xFDBIj4SvHIuXPGdFIyEXyBCmTEq.jpg';
const nativeUrl = `${host}://${path}`;
const ipfsPath = `/${host}/${path}`;
const httpPath = `http://ipfs.dev.aura.network:8080/ipfs/${path}`;
const parsedNativeUrl = Erc721MediaHanlder.parseFilename(nativeUrl);
const parsedIpfsPath = Erc721MediaHanlder.parseFilename(ipfsPath);
const parsedHttpPath = Erc721MediaHanlder.parseFilename(httpPath);
const parsedNativeUrl = Erc721MediaHanlder.parseFilenameFromIPFS(nativeUrl);
const parsedIpfsPath = Erc721MediaHanlder.parseFilenameFromIPFS(ipfsPath);
const parsedHttpPath = Erc721MediaHanlder.parseFilenameFromIPFS(httpPath);
expect(parsedNativeUrl).toEqual(`ipfs/${path}`);
expect(parsedIpfsPath).toEqual(`ipfs/${path}`);
expect(parsedHttpPath).toEqual(`ipfs/${path}`);
const subDomain =
'bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link';
const httpSubDomain = `https://${subDomain}`;
const parsedHttpSubDomain = Erc721MediaHanlder.parseFilename(httpSubDomain);
const parsedHttpSubDomain =
Erc721MediaHanlder.parseFilenameFromIPFS(httpSubDomain);
expect(parsedHttpSubDomain).toEqual(subDomain);
const file = '1.json';
const httpFullSubDomain = `https://${subDomain}/${file}`;
const parsedFullHttpSubDomain =
Erc721MediaHanlder.parseFilename(httpFullSubDomain);
Erc721MediaHanlder.parseFilenameFromIPFS(httpFullSubDomain);
expect(parsedFullHttpSubDomain).toEqual(`${subDomain}/${file}`);
}

Expand Down Expand Up @@ -188,7 +189,8 @@ export default class TestErc721MediaService {
const imageUrl =
'ipfs://QmPfi9CcTafv4C1sBZ5HxUs4PbvBi22nkjgqbypMhqaPLp/The_Immersion_Into_Aura_Odyssey.png';
// Case token_uri: ipfs format
const ipfsTokenUri = 'ipfs://QmPf5LawLS1ZVqFTSs7JhFD6yteKQLXxYMEYoc1PcKkhVj/109';
const ipfsTokenUri =
'ipfs://QmPf5LawLS1ZVqFTSs7JhFD6yteKQLXxYMEYoc1PcKkhVj/109';
jest.spyOn(axios, 'get').mockImplementation(async () =>
Buffer.from(`{
name: 'Immersion 109',
Expand Down

0 comments on commit bbb69a0

Please sign in to comment.