-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #125 from mastercodercat/cascade-uploader
Add umi-uploader-cascade plugin
- Loading branch information
Showing
13 changed files
with
381 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@metaplex-foundation/umi-uploader-cascade": patch | ||
--- | ||
|
||
Add umi-uploader-cascade plugin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# @metaplex-foundation/umi-uploader-cascade | ||
|
||
## 0.0.1 | ||
|
||
### Patch Changes | ||
|
||
- [`e84c9e`](https://github.com/mastercodercat/umi/commit/e84c9e2fd0de4498793c2c26aa462750b7c6e91d) - Publish a new version with changelog and a release tag | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# umi-uploader-cascade | ||
|
||
An uploader implementation relying on Cascade Protocol. | ||
|
||
## Installation | ||
|
||
```sh | ||
npm install @metaplex-foundation/umi-uploader-cascade | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "../../babel.config.json" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
{ | ||
"name": "@metaplex-foundation/umi-uploader-cascade", | ||
"version": "0.1.1", | ||
"description": "An uploader implementation relying on Cascade", | ||
"license": "MIT", | ||
"sideEffects": false, | ||
"module": "dist/esm/index.mjs", | ||
"main": "dist/cjs/index.cjs", | ||
"types": "dist/types/index.d.ts", | ||
"exports": { | ||
".": { | ||
"types": "./dist/types/index.d.ts", | ||
"import": "./dist/esm/index.mjs", | ||
"require": "./dist/cjs/index.cjs" | ||
} | ||
}, | ||
"files": [ | ||
"/dist/cjs", | ||
"/dist/esm", | ||
"/dist/types", | ||
"/src" | ||
], | ||
"scripts": { | ||
"lint": "eslint --ext js,ts,tsx src", | ||
"lint:fix": "eslint --fix --ext js,ts,tsx src", | ||
"clean": "rimraf dist", | ||
"build": "pnpm clean && tsc && tsc -p test/tsconfig.json && rollup -c", | ||
"test": "ava" | ||
}, | ||
"dependencies": { | ||
"node-fetch": "^2.6.7", | ||
"form-data": "^3.0.0" | ||
}, | ||
"peerDependencies": { | ||
"@metaplex-foundation/umi": "workspace:^" | ||
}, | ||
"devDependencies": { | ||
"@ava/typescript": "^3.0.1", | ||
"@metaplex-foundation/umi": "workspace:^", | ||
"@metaplex-foundation/umi-downloader-http": "workspace:^", | ||
"@metaplex-foundation/umi-eddsa-web3js": "workspace:^", | ||
"@metaplex-foundation/umi-http-fetch": "workspace:^", | ||
"@metaplex-foundation/umi-rpc-web3js": "workspace:^", | ||
"ava": "^5.1.0", | ||
"typescript": "^4.5.4", | ||
"@types/node-fetch": "^2.6.2" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"author": "Metaplex Maintainers <contact@metaplex.com>", | ||
"homepage": "https://metaplex.com", | ||
"repository": { | ||
"url": "https://github.com/metaplex-foundation/umi.git" | ||
}, | ||
"typedoc": { | ||
"entryPoint": "./src/index.ts", | ||
"readmeFile": "./README.md", | ||
"displayName": "umi-uploader-cascade" | ||
}, | ||
"ava": { | ||
"typescript": { | ||
"compile": false, | ||
"rewritePaths": { | ||
"src/": "dist/test/src/", | ||
"test/": "dist/test/test/" | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { createConfigs } from '../../rollup.config'; | ||
import pkg from './package.json'; | ||
|
||
export default createConfigs({ | ||
pkg, | ||
builds: [ | ||
{ | ||
dir: 'dist/esm', | ||
format: 'es', | ||
}, | ||
{ | ||
dir: 'dist/cjs', | ||
format: 'cjs', | ||
}, | ||
], | ||
}); |
130 changes: 130 additions & 0 deletions
130
packages/umi-uploader-cascade/src/createCascadeUploader.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* eslint-disable no-await-in-loop */ | ||
import { | ||
Context, | ||
createGenericFileFromJson, | ||
GenericFile, | ||
lamports, | ||
SolAmount, | ||
UploaderInterface, | ||
UploaderUploadOptions, | ||
} from '@metaplex-foundation/umi'; | ||
import fetch from 'node-fetch'; | ||
|
||
const FormData = require('form-data'); | ||
|
||
const CASCADE_API_URL = 'https://gateway-api.pastel.network/'; | ||
|
||
export type CascadeUploaderOptions = { | ||
apiKey: string; | ||
}; | ||
|
||
export type CascadeUploadedItem = { | ||
result_id: string; | ||
result_status: string; | ||
registration_ticket_txid: string | undefined; | ||
original_file_ipfs_link: string | undefined; | ||
error: string | undefined; | ||
}; | ||
|
||
export type CascadeUploadResponse = { | ||
request_id: string; | ||
request_status: string; | ||
results: CascadeUploadedItem[]; | ||
}; | ||
|
||
export function createCascadeUploader( | ||
context: Pick<Context, 'rpc' | 'payer'>, | ||
options: CascadeUploaderOptions = { apiKey: '' } | ||
): UploaderInterface & { | ||
upload2: ( | ||
files: GenericFile[], | ||
options?: UploaderUploadOptions | ||
) => Promise<CascadeUploadedItem[]>; | ||
} { | ||
const { apiKey } = options; | ||
|
||
if (!apiKey) { | ||
throw new Error('Cascade Gateway API key is required'); | ||
} | ||
|
||
const getUploadPrice = async (): Promise<SolAmount> => lamports(0); | ||
|
||
const upload = async (files: GenericFile[]): Promise<string[]> => { | ||
const uris: string[] = []; | ||
|
||
const body = new FormData(); | ||
|
||
files.forEach((file) => { | ||
body.append('files', Buffer.from(file.buffer), file.fileName); | ||
}); | ||
|
||
try { | ||
const res = await fetch( | ||
`${CASCADE_API_URL}/api/v1/cascade?make_publicly_accessible=true`, | ||
{ | ||
headers: { | ||
Api_key: apiKey, | ||
}, | ||
method: 'POST', | ||
body, | ||
} | ||
); | ||
|
||
const data: CascadeUploadResponse = await res.json(); | ||
data.results.forEach((item) => { | ||
if (item.original_file_ipfs_link) | ||
uris.push(item.original_file_ipfs_link); | ||
else { | ||
uris.push(''); | ||
} | ||
}); | ||
} catch (e) { | ||
return []; | ||
} | ||
// eslint-disable-next-line no-console | ||
console.log(uris); | ||
return uris; | ||
}; | ||
|
||
const upload2 = async ( | ||
files: GenericFile[] | ||
): Promise<CascadeUploadedItem[]> => { | ||
const body = new FormData(); | ||
|
||
files.forEach((file) => { | ||
body.append('files', Buffer.from(file.buffer), file.fileName); | ||
}); | ||
|
||
try { | ||
const res = await fetch( | ||
`${CASCADE_API_URL}/api/v1/cascade?make_publicly_accessible=true`, | ||
{ | ||
headers: { | ||
Api_key: apiKey, | ||
}, | ||
method: 'POST', | ||
body, | ||
} | ||
); | ||
|
||
const data: CascadeUploadResponse = await res.json(); | ||
|
||
return data.results; | ||
} catch (e) { | ||
return []; | ||
} | ||
}; | ||
|
||
const uploadJson = async <T>(json: T): Promise<string> => { | ||
const file = createGenericFileFromJson(json); | ||
const uris = await upload([file]); | ||
return uris[0]; | ||
}; | ||
|
||
return { | ||
getUploadPrice, | ||
upload, | ||
uploadJson, | ||
upload2, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './createCascadeUploader'; | ||
export * from './plugin'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import type { UmiPlugin } from '@metaplex-foundation/umi'; | ||
import { | ||
createCascadeUploader, | ||
CascadeUploaderOptions, | ||
} from './createCascadeUploader'; | ||
|
||
export const cascadeUploader = ( | ||
options?: CascadeUploaderOptions | ||
): UmiPlugin => ({ | ||
install(umi) { | ||
umi.uploader = createCascadeUploader(umi, options); | ||
}, | ||
}); |
69 changes: 69 additions & 0 deletions
69
packages/umi-uploader-cascade/test/CascadeUploader.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { | ||
Context, | ||
createGenericFile, | ||
createUmi, | ||
generatedSignerIdentity, | ||
utf8, | ||
} from '@metaplex-foundation/umi'; | ||
import { httpDownloader } from '@metaplex-foundation/umi-downloader-http'; | ||
import { web3JsEddsa } from '@metaplex-foundation/umi-eddsa-web3js'; | ||
import { fetchHttp } from '@metaplex-foundation/umi-http-fetch'; | ||
import { web3JsRpc } from '@metaplex-foundation/umi-rpc-web3js'; | ||
import test from 'ava'; | ||
import { cascadeUploader, CascadeUploaderOptions } from '../src'; | ||
|
||
test('example test', async (t) => { | ||
t.is(typeof cascadeUploader, 'function'); | ||
}); | ||
|
||
// TODO(loris): Unskip these tests when we can mock the Cascade API. | ||
|
||
const getContext = (options?: CascadeUploaderOptions): Context => | ||
createUmi().use({ | ||
install(umi) { | ||
umi.use(web3JsRpc('https://api.devnet.solana.com')); | ||
umi.use(web3JsEddsa()); | ||
umi.use(fetchHttp()); | ||
umi.use(httpDownloader()); | ||
umi.use(generatedSignerIdentity()); | ||
umi.use(cascadeUploader(options)); | ||
}, | ||
}); | ||
// Use a dummy apiKey since the tests are skipped currently. | ||
const apiKey = 'testKey'; | ||
|
||
test.skip('it can upload one file', async (t) => { | ||
// Given a Context using Cascade.Storage. | ||
const context = getContext({ apiKey }); | ||
|
||
// When we upload some asset. | ||
const [uri] = await context.uploader.upload([ | ||
createGenericFile('some-image', 'some-image.jpg'), | ||
]); | ||
|
||
// Then the URI should be a valid IPFS URI. | ||
t.truthy(uri); | ||
t.true(uri.startsWith('https://ipfs.io/')); | ||
|
||
// and it should point to the uploaded asset. | ||
const [asset] = await context.downloader.download([uri]); | ||
t.is(utf8.deserialize(asset.buffer)[0], 'some-image'); | ||
}); | ||
|
||
test.skip('it can upload multiple files in batch', async (t) => { | ||
// Given a Context using Cascade with a batch size of 1. | ||
const context = getContext({ apiKey }); | ||
|
||
// When we upload two assets. | ||
const uris = await context.uploader.upload([ | ||
createGenericFile('some-image-A', 'some-image-A.jpg'), | ||
createGenericFile('some-image-B', 'some-image-B.jpg'), | ||
]); | ||
|
||
// Then the URIs should point to the uploaded assets in the right order. | ||
t.is(uris.length, 2); | ||
const [assetA] = await context.downloader.download([uris[0]]); | ||
t.is(utf8.deserialize(assetA.buffer)[0], 'some-image-A'); | ||
const [assetB] = await context.downloader.download([uris[1]]); | ||
t.is(utf8.deserialize(assetB.buffer)[0], 'some-image-B'); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"extends": "../../../tsconfig.json", | ||
"include": ["./**/*"], | ||
"compilerOptions": { | ||
"module": "commonjs", | ||
"outDir": "../dist/test", | ||
"declarationDir": null, | ||
"declaration": false, | ||
"emitDeclarationOnly": false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"extends": "../../tsconfig.json", | ||
"include": ["src"], | ||
"compilerOptions": { | ||
"outDir": "dist/esm", | ||
"declarationDir": "dist/types" | ||
} | ||
} |
Oops, something went wrong.