diff --git a/CHANGELOG.md b/CHANGELOG.md index a056094a62..69ccb1a391 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [4.1.14] - 2023.10.23 +### Added +- Added IPFS get file data method + ## [4.1.13] - 2023.10.20 ### Changed - Fixed CONTRACT_ADDRESS_LOG_EVENT data in getAll() Notification method diff --git a/README.md b/README.md index 95d75f1bf6..5bf9a6262d 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,15 @@ Explore the world of fungible tokens, manage their properties, and track your as | [Get metadata of a fungible token](https://docs.tatum.io/docs/fungible-tokens/get-metadata-of-a-fungible-token) | | [Create a fungible token](https://docs.tatum.io/docs/fungible-tokens/create-a-fungible-token) | +### 📁 IPFS + +Enables you as a developer to use IPFS to store and retrieve your media. + +| Documentation | +|--------------------------------------------------------------------| +| [Upload file to IPFS](https://docs.tatum.io/docs/ipfs/upload-file) | + + ### ⛽ Fee Estimation Stay updated with real-time fee insights and ensure smooth transactions without overpaying. diff --git a/package.json b/package.json index cd2c686434..1de92c6d97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tatumio/tatum", - "version": "4.1.13", + "version": "4.1.14", "description": "Tatum JS SDK", "author": "Tatum", "repository": "https://github.com/tatumio/tatum-js", diff --git a/src/connector/connector.dto.ts b/src/connector/connector.dto.ts index 3ae451c561..996e2cf956 100644 --- a/src/connector/connector.dto.ts +++ b/src/connector/connector.dto.ts @@ -5,6 +5,7 @@ export interface GetUrl { path?: string basePath?: string params?: PARAMS + isDownload?: boolean } export interface SdkRequest extends GetUrl { diff --git a/src/connector/tatum.connector.ts b/src/connector/tatum.connector.ts index 48373f9c7a..7e3fbbe1f1 100644 --- a/src/connector/tatum.connector.ts +++ b/src/connector/tatum.connector.ts @@ -14,21 +14,24 @@ export class TatumConnector { constructor(private readonly id: string) {} public async get(request: GetUrl) { - return this.request({ ...request, method: 'GET' }) + return this.request({ ...request, method: 'GET' }) as Promise } public async rpcCall(url: string, body: JsonRpcCall | JsonRpcCall[]) { - return this.request({ body, method: 'POST' }, 0, url) + return this.request({ body, method: 'POST' }, 0, url) as Promise } public async post( request: SdkRequest, ) { - return this.request({ ...request, method: 'POST' }) + return this.request({ + ...request, + method: 'POST', + }) as Promise } public async delete(request: GetUrl) { - return this.request({ ...request, method: 'DELETE' }) + return this.request({ ...request, method: 'DELETE' }) as Promise } public async uploadFile(request: FileUploadRequest) { @@ -37,7 +40,13 @@ export class TatumConnector { return this.request( { ...request, method: 'POST', body: formData, basePath: Constant.TATUM_API_URL.V3 }, 0, - ) + ) as Promise + } + + public async getFile( + request: GetUrl, + ) { + return this.request({ ...request, method: 'GET', isDownload: true }) as Promise } private async request< @@ -45,10 +54,10 @@ export class TatumConnector { PARAMS extends DefaultParamsType = DefaultParamsType, BODY extends DefaultBodyType = DefaultBodyType, >( - { path, params, body, method, basePath }: SdkRequest, + { path, params, body, method, basePath, isDownload }: SdkRequest, retry = 0, externalUrl?: string, - ): Promise { + ): Promise { const url = externalUrl || this.getUrl({ path, params, basePath }) const isUpload = body && body instanceof FormData const headers = isUpload ? Utils.getBasicHeaders(this.id) : Utils.getHeaders(this.id) @@ -71,7 +80,7 @@ export class TatumConnector { try { const res = await fetch(url, request) const end = Date.now() - start - const responseBody = await res.clone().text() + const responseBody = isDownload ? `Binary data` : await res.clone().text() // Structure your log entry here Utils.log({ @@ -93,7 +102,13 @@ export class TatumConnector { }) if (res.ok) { - return responseBody ? await res.json() : undefined + if (!responseBody) { + return undefined + } + if (isDownload) { + return await res.blob() + } + return await res.json() } // Retry only in case of 5xx error @@ -101,7 +116,7 @@ export class TatumConnector { return await this.retry(url, request, res, retry) } - return await Promise.reject(responseBody) + throw responseBody } catch (error) { const end = Date.now() - start Utils.log({ @@ -151,7 +166,7 @@ export class TatumConnector { request: RequestInit, response: Response, retry: number, - ): Promise { + ): Promise { const { retryDelay, retryCount } = Container.of(this.id).get(CONFIG) if (!retryCount) { Utils.log({ diff --git a/src/service/ipfs/ipfs.dto.ts b/src/service/ipfs/ipfs.dto.ts index 629ad6d59d..d418f7ef6a 100644 --- a/src/service/ipfs/ipfs.dto.ts +++ b/src/service/ipfs/ipfs.dto.ts @@ -4,3 +4,10 @@ export interface UploadFile { */ file: BlobPart } + +export interface GetFile { + /** + * File id to be retrieved + */ + id: string +} diff --git a/src/service/ipfs/ipfs.ts b/src/service/ipfs/ipfs.ts index ff91277ccb..55fa0dcc70 100644 --- a/src/service/ipfs/ipfs.ts +++ b/src/service/ipfs/ipfs.ts @@ -2,7 +2,7 @@ import { Container, Service } from 'typedi' import { TatumConnector } from '../../connector/tatum.connector' import { CONFIG, ErrorUtils, ResponseDto } from '../../util' import { TatumConfig } from '../tatum' -import { UploadFile } from './ipfs.dto' +import { GetFile, UploadFile } from './ipfs.dto' @Service({ factory: (data: { id: string }) => { @@ -32,4 +32,18 @@ export class Ipfs { }), ) } + + /** + * Get file binary data from the IPFS storage. + * @param body Body of the request with file to be uploaded. + * @returns Blob IPFS file binary data. + * @returns ResponseDto is error occurred. + */ + async getFile(body: GetFile): Promise> { + return ErrorUtils.tryFailBlob(() => + this.connector.getFile({ + path: `ipfs/${body.id}`, + }), + ) + } } diff --git a/src/util/error.ts b/src/util/error.ts index c5048bcda7..fd8a27b239 100644 --- a/src/util/error.ts +++ b/src/util/error.ts @@ -56,6 +56,17 @@ export const ErrorUtils = { } } }, + tryFailBlob: async (f: (() => Promise) | (() => Blob)): Promise> => { + try { + return await f() + } catch (e) { + return { + data: null, + status: Status.ERROR, + error: ErrorUtils.toErrorWithMessage(e), + } + } + }, formatErrorMsg: (message: string) => { return message.replace('attr.', '') },