Skip to content

Commit cfe8bba

Browse files
authored
Add resolveScheme method to storage (#156)
1 parent 18dc8d3 commit cfe8bba

File tree

7 files changed

+59
-61
lines changed

7 files changed

+59
-61
lines changed

.changeset/lucky-penguins-cover.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@thirdweb-dev/storage": patch
3+
---
4+
5+
Add resolveScheme method to storage

packages/storage/src/core/downloaders/storage-downloader.ts

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,20 @@ import fetch from "cross-fetch";
1111
* // Can instantiate the downloader with the default gateway URLs
1212
* const downloader = new StorageDownloader();
1313
* const storage = new ThirdwebStorage(undefined, downloader);
14-
*
15-
* // Or optionally, can specify your own mapping of URLs
16-
* const gatewayUrls = {
17-
* // We define a mapping of schemes to gateway URLs
18-
* "ipfs://": [
19-
* "https://gateway.ipfscdn.io/ipfs/",
20-
* "https://cloudflare-ipfs.com/ipfs/",
21-
* "https://ipfs.io/ipfs/",
22-
* ],
23-
* };
24-
* const downloader = new StorageDownloader(gatewayUrls);
25-
* const storage = new ThirdwebStorage(undefined, downloader);
2614
* ```
2715
*
2816
* @public
2917
*/
3018
export class StorageDownloader implements IStorageDownloader {
31-
public gatewayUrls: GatewayUrls;
32-
33-
constructor(gatewayUrls?: GatewayUrls) {
34-
this.gatewayUrls = prepareGatewayUrls(gatewayUrls);
35-
}
36-
37-
async download(uri: string, attempts = 0): Promise<Response> {
19+
async download(
20+
uri: string,
21+
gatewayUrls: GatewayUrls,
22+
attempts = 0,
23+
): Promise<Response> {
3824
// Replace recognized scheme with the highest priority gateway URL that hasn't already been attempted
3925
let resolvedUri;
4026
try {
41-
resolvedUri = replaceSchemeWithGatewayUrl(
42-
uri,
43-
this.gatewayUrls,
44-
attempts,
45-
);
27+
resolvedUri = replaceSchemeWithGatewayUrl(uri, gatewayUrls, attempts);
4628
} catch (err: any) {
4729
// If every gateway URL we know about for the designated scheme has been tried (via recursion) and failed, throw an error
4830
if (err.includes("[GATEWAY_URL_ERROR]")) {
@@ -61,7 +43,7 @@ export class StorageDownloader implements IStorageDownloader {
6143
console.warn(
6244
`Request to ${resolvedUri} failed with status ${res.status} - ${res.statusText}`,
6345
);
64-
return this.download(uri, attempts + 1);
46+
return this.download(uri, gatewayUrls, attempts + 1);
6547
}
6648

6749
return res;

packages/storage/src/core/storage.ts

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1+
import { prepareGatewayUrls } from "../common";
12
import {
23
extractObjectFiles,
34
replaceObjectFilesWithUris,
45
replaceObjectGatewayUrlsWithSchemes,
56
replaceObjectSchemesWithGatewayUrls,
7+
replaceSchemeWithGatewayUrl,
68
} from "../common/utils";
79
import {
810
FileOrBuffer,
911
FileOrBufferOrStringArraySchema,
12+
GatewayUrls,
1013
IpfsUploadBatchOptions,
1114
IStorageDownloader,
1215
IStorageUploader,
@@ -28,24 +31,52 @@ import { IpfsUploader } from "./uploaders/ipfs-uploader";
2831
* const uri = await storage.upload(data);
2932
* const result = await storage.download(uri);
3033
*
31-
* // Or configure a custom uploader and downloader
34+
* // Or configure a custom uploader, downloader, and gateway URLs
35+
* const gatewayUrls = {
36+
* // We define a mapping of schemes to gateway URLs
37+
* "ipfs://": [
38+
* "https://gateway.ipfscdn.io/ipfs/",
39+
* "https://cloudflare-ipfs.com/ipfs/",
40+
* "https://ipfs.io/ipfs/",
41+
* ],
42+
* };
3243
* const downloader = new StorageDownloader();
3344
* const uploader = new IpfsUploader();
34-
* const storage = new ThirdwebStorage(uploader, downloader)
45+
* const storage = new ThirdwebStorage(uploader, downloader, gatewayUrls)
3546
* ```
3647
*
3748
* @public
3849
*/
3950
export class ThirdwebStorage<T extends UploadOptions = IpfsUploadBatchOptions> {
4051
private uploader: IStorageUploader<T>;
4152
private downloader: IStorageDownloader;
53+
public gatewayUrls: GatewayUrls;
4254

4355
constructor(
4456
uploader: IStorageUploader<T> = new IpfsUploader(),
4557
downloader: IStorageDownloader = new StorageDownloader(),
58+
gatewayUrls?: GatewayUrls,
4659
) {
4760
this.uploader = uploader;
4861
this.downloader = downloader;
62+
this.gatewayUrls = prepareGatewayUrls(gatewayUrls);
63+
}
64+
65+
/**
66+
* Resolve any scheme on a URL to get a retrievable URL for the data
67+
*
68+
* @param url - The URL to resolve the scheme of
69+
* @returns The URL with its scheme resolved
70+
*
71+
* @example
72+
* ```jsx
73+
* const uri = "ipfs://example";
74+
* const url = storage.resolveScheme(uri);
75+
* console.log(url);
76+
* ```
77+
*/
78+
resolveScheme(url: string): string {
79+
return replaceSchemeWithGatewayUrl(url, this.gatewayUrls);
4980
}
5081

5182
/**
@@ -61,7 +92,7 @@ export class ThirdwebStorage<T extends UploadOptions = IpfsUploadBatchOptions> {
6192
* ```
6293
*/
6394
async download(url: string): Promise<Response> {
64-
return this.downloader.download(url);
95+
return this.downloader.download(url, this.gatewayUrls);
6596
}
6697

6798
/**
@@ -73,7 +104,7 @@ export class ThirdwebStorage<T extends UploadOptions = IpfsUploadBatchOptions> {
73104
*
74105
* @example
75106
* ```jsx
76-
* const uri = "ipfs://example"
107+
* const uri = "ipfs://example";
77108
* const json = await storage.downloadJSON(uri);
78109
* ```
79110
*/
@@ -82,10 +113,7 @@ export class ThirdwebStorage<T extends UploadOptions = IpfsUploadBatchOptions> {
82113

83114
// If we get a JSON object, recursively replace any schemes with gatewayUrls
84115
const json = await res.json();
85-
return replaceObjectSchemesWithGatewayUrls(
86-
json,
87-
this.downloader.gatewayUrls,
88-
) as TJSON;
116+
return replaceObjectSchemesWithGatewayUrls(json, this.gatewayUrls) as TJSON;
89117
}
90118

91119
/**
@@ -175,19 +203,19 @@ export class ThirdwebStorage<T extends UploadOptions = IpfsUploadBatchOptions> {
175203
): Promise<Json[]> {
176204
let cleaned = data;
177205
// TODO: Gateway URLs should probably be top-level since both uploader and downloader need them
178-
if (this.uploader.gatewayUrls || this.downloader.gatewayUrls) {
206+
if (this.gatewayUrls) {
179207
// Replace any gateway URLs with their hashes
180208
cleaned = replaceObjectGatewayUrlsWithSchemes(
181209
cleaned,
182-
this.uploader.gatewayUrls || this.downloader.gatewayUrls,
210+
this.gatewayUrls,
183211
) as Json[];
184212

185213
if (options?.uploadWithGatewayUrl || this.uploader.uploadWithGatewayUrl) {
186214
// If flag is set, replace all schemes with their preferred gateway URL
187215
// Ex: used for Solana, where services don't resolve schemes for you, so URLs must be useable by default
188216
cleaned = replaceObjectSchemesWithGatewayUrls(
189217
cleaned,
190-
this.uploader.gatewayUrls || this.downloader.gatewayUrls,
218+
this.gatewayUrls,
191219
) as Json[];
192220
}
193221
}

packages/storage/src/core/uploaders/ipfs-uploader.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,7 @@ import FormData from "form-data";
2828
* const storage = new ThirdwebStorage(uploader);
2929
*
3030
* // Or optionally, can pass configuration
31-
* const gatewayUrls = {
32-
* // We define a mapping of schemes to gateway URLs
33-
* "ipfs://": [
34-
* "https://gateway.ipfscdn.io/ipfs/",
35-
* "https://cloudflare-ipfs.com/ipfs/",
36-
* "https://ipfs.io/ipfs/",
37-
* ],
38-
* };
3931
* const options = {
40-
* // Define cutom gateway URLs
41-
* gatewayUrls,
4232
* // Upload objects with resolvable URLs
4333
* uploadWithGatewayUrl: true,
4434
* }
@@ -48,11 +38,9 @@ import FormData from "form-data";
4838
* @public
4939
*/
5040
export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
51-
public gatewayUrls: GatewayUrls;
5241
public uploadWithGatewayUrl: boolean;
5342

5443
constructor(options?: IpfsUploaderOptions) {
55-
this.gatewayUrls = prepareGatewayUrls(options?.gatewayUrls);
5644
this.uploadWithGatewayUrl = options?.uploadWithGatewayUrl || false;
5745
}
5846

packages/storage/src/types/download.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,14 @@
22
* @public
33
*/
44
export interface IStorageDownloader {
5-
/**
6-
* Gateway URLs used to replace schemes on download
7-
*/
8-
gatewayUrls: GatewayUrls;
95
/**
106
* Download arbitrary data from any URL scheme
117
*
128
* @param url - The URL to download data from
9+
* @param gatewayUrls - The gateway URLs to use for this download
1310
* @returns The response object of the fetch
1411
*/
15-
download(url: string): Promise<Response>;
12+
download(url: string, gatewayUrls?: GatewayUrls): Promise<Response>;
1613
}
1714

1815
/**

packages/storage/src/types/upload.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ export type UploadOptions = { [key: string]: any };
1010
* @public
1111
*/
1212
export interface IStorageUploader<T extends UploadOptions> {
13-
/**
14-
* If specified, will be used to replace any gateway URLs with schemes on upload
15-
*/
16-
gatewayUrls?: GatewayUrls;
1713
/**
1814
* If specified, will upload objects with gateway URLs instead of schemes
1915
*/
@@ -47,10 +43,6 @@ export type UploadProgressEvent = {
4743
* @public
4844
*/
4945
export type IpfsUploaderOptions = {
50-
/**
51-
* Mapping of URL schemes to gateway URLs to resolve to
52-
*/
53-
gatewayUrls?: GatewayUrls;
5446
/**
5547
* Whether or not to replace any URLs with schemes with resolved URLs before upload
5648
*/

packages/storage/test/ipfs.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ import { readFileSync } from "fs";
77
describe("IPFS", async () => {
88
const storage = new ThirdwebStorage();
99

10+
it("Should resolve scheme with gateway URL", async () => {
11+
const uri = `ipfs://example`;
12+
const url = storage.resolveScheme(uri);
13+
expect(url).to.equal(`${DEFAULT_GATEWAY_URLS["ipfs://"][0]}example`);
14+
});
15+
1016
it("Should upload buffer with file number", async () => {
1117
const uri = await storage.upload(readFileSync("test/files/0.jpg"));
1218

0 commit comments

Comments
 (0)