feat(upload): Expanded IPFS Class for uploading files to the web

This commit is contained in:
Daniel Gonzalez
2020-07-29 01:18:33 -04:00
parent 18b8536d2b
commit b12110a617
2 changed files with 433 additions and 16 deletions
+176 -7
View File
@@ -53,8 +53,8 @@ class IPFS {
} }
/** /**
* @api IPFS.createFileModel() createFileModel() - Create a file model * @api IPFS.createFileModelServer() createFileModelServer() - Create a file model
* @apiName createFileModel() * @apiName createFileModelServer()
* @apiGroup IPFS * @apiGroup IPFS
* @apiDescription Creates a new model on the server. If successful, will * @apiDescription Creates a new model on the server. If successful, will
* return an object with file data. That object contains a BCH address, * return an object with file data. That object contains a BCH address,
@@ -64,7 +64,7 @@ class IPFS {
* (async () => { * (async () => {
* try { * try {
* const path = `${__dirname}/ipfs.js` * const path = `${__dirname}/ipfs.js`
* let fileData = await bchjs.IPFS.uploadFile(path); * let fileData = await bchjs.IPFS.createFileModelServer(path);
* console.log(fileData); * console.log(fileData);
* } catch(error) { * } catch(error) {
* console.error(error) * console.error(error)
@@ -90,7 +90,7 @@ class IPFS {
* "__v": 0 * "__v": 0
* } * }
*/ */
async createFileModel(path) { async createFileModelServer(path) {
try { try {
// Ensure the file exists. // Ensure the file exists.
if (!_this.fs.existsSync(path)) if (!_this.fs.existsSync(path))
@@ -131,7 +131,7 @@ class IPFS {
} }
/** /**
* @api IPFS.uploadFile() uploadFile() - Upload a file to IPFS * @api IPFS.uploadFileServer() uploadFileServer() - Upload a file to IPFS
* @apiName uploadFile() * @apiName uploadFile()
* @apiGroup IPFS * @apiGroup IPFS
* @apiDescription Upload a file to the FullStack.cash IPFS server. If * @apiDescription Upload a file to the FullStack.cash IPFS server. If
@@ -142,7 +142,7 @@ class IPFS {
* (async () => { * (async () => {
* try { * try {
* const path = `${__dirname}/ipfs.js` * const path = `${__dirname}/ipfs.js`
* let fileData = await bchjs.IPFS.uploadFile(path, "5ec562319bfacc745e8d8a52"); * let fileData = await bchjs.IPFS.uploadFileServer(path, "5ec562319bfacc745e8d8a52");
* console.log(fileData); * console.log(fileData);
* } catch(error) { * } catch(error) {
* console.error(error) * console.error(error)
@@ -157,7 +157,7 @@ class IPFS {
* "fileExtension": "js" * "fileExtension": "js"
* } * }
*/ */
async uploadFile(path, modelId) { async uploadFileServer(path, modelId) {
try { try {
// Ensure the file exists. // Ensure the file exists.
if (!_this.fs.existsSync(path)) if (!_this.fs.existsSync(path))
@@ -270,6 +270,175 @@ class IPFS {
throw err throw err
} }
} }
/**
* @api IPFS.createFileModelWeb() createFileModelWeb() - Create a file model
* @apiName createFileModelWeb()
* @apiGroup IPFS
* @apiDescription Creates a new model on the server. If successful, will
* return an object with file data. That object contains a BCH address,
* payment amount, and _id required to be able to upload a file.
*
* @apiExample Example usage:
* (async () => {
* try {
* const content = ['PSF']
* const name = 'psf.txt' // Content can be Array , ArrayBuffer , Blob
* const options = { type: "text/plain" }
* const file = new File(content,name,options)
* let fileData = await bchjs.IPFS.createFileModelWeb(file);
* console.log(fileData);
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* "success": true,
* "hostingCostBCH": 0.00004197,
* "hostingCostUSD": 0.01,
* "file": {
* "payloadLink": "",
* "hasBeenPaid": false,
* "_id": "5ec562319bfacc745e8d8a52",
* "schemaVersion": 1,
* "size": 4458,
* "fileName": "ipfs.js",
* "fileExtension": "js",
* "createdTimestamp": "1589994033.655",
* "hostingCost": 4196,
* "walletIndex": 49,
* "bchAddr": "bchtest:qzrpkevu7h2ayfa4rjx08r5elvpfu72dg567x3mh3c",
* "__v": 0
* }
*/
async createFileModelWeb(file) {
try {
if (!file) throw new Error(`File is required`)
const { name, size } = file
if (!name || typeof name !== "string")
throw new Error(`File should have the property 'name' of string type`)
if (!size || typeof size !== "number")
throw new Error(`File should have the property 'size' of number type`)
// Get the file extension.
const splitExt = name.split(".")
const fileExt = splitExt[splitExt.length - 1]
const fileObj = {
schemaVersion: 1,
size: size,
fileName: name,
fileExtension: fileExt
}
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
const fileData = await _this.axios.post(`${this.IPFS_API}/files`, {
file: fileObj
})
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
return fileData.data
} catch (err) {
console.error(`Error in createFileModelWeb()`)
throw err
}
}
/**
* @api IPFS.uploadFileWeb() uploadFileWeb() - Upload a file to IPFS
* @apiName uploadFileWeb()
* @apiGroup IPFS
* @apiDescription Upload a file to the FullStack.cash IPFS server. If
* successful, it will return an object with an ID, a BCH address, and an
* amount of BCH to pay.
*
* @apiExample Example usage:
* (async () => {
* try {
* * const content = ['PSF']
* const name = 'psf.txt' // Content can be Array , ArrayBuffer , Blob
* const options = { type: "text/plain" }
* const file = new File(content,name,options)
* let fileData = await bchjs.IPFS.uploadFile(file, "5ec562319bfacc745e8d8a52");
* console.log(fileData);
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* "schemaVersion": 1,
* "size": 2374,
* "fileId": "5ec562319bfacc745e8d8a52",
* "fileName": "ipfs.js",
* "fileExtension": "js"
* }
*/
async uploadFileWeb(file, modelId) {
try {
if (!file) throw new Error(`File is required`)
const { name, type, size } = file
if (!name || typeof name !== "string")
throw new Error(`File should have the property 'name' of string type`)
if (!type || typeof type !== "string")
throw new Error(`File should have the property 'type' of string type`)
if (!size || typeof size !== "number")
throw new Error(`File should have the property 'size' of number type`)
if (!modelId) {
throw new Error(
`Must include a file model ID in order to upload a file.`
)
}
// Prepare the upload object. Get a file ID from Uppy.
const id = _this.uppy.addFile({
name: name,
data: file,
source: "Local",
isRemote: false
})
// console.log(`id: ${JSON.stringify(id, null, 2)}`)
// Add the model ID, required to be allowed to upload the file.
_this.uppy.setFileMeta(id, { fileModelId: modelId })
// Upload the file to the server.
const upData = await _this.uppy.upload()
if (upData.failed.length)
throw new Error("The file could not be uploaded")
if (upData.successful.length) {
delete upData.successful[0].data
// console.log(`upData: ${JSON.stringify(upData, null, 2)}`)
const fileObj = {
schemaVersion: 1,
size: upData.successful[0].progress.bytesTotal,
fileId: upData.successful[0].meta.fileModelId,
fileName: upData.successful[0].name,
fileExtension: upData.successful[0].extension
}
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
return fileObj
// return true
}
return false
} catch (err) {
console.error(`Error in bch-js/src/ipfs.js/uploadFileWeb(): `)
throw err
}
}
} }
module.exports = IPFS module.exports = IPFS
+257 -9
View File
@@ -4,7 +4,6 @@
const assert = require("chai").assert const assert = require("chai").assert
const sinon = require("sinon") const sinon = require("sinon")
const BCHJS = require("../../src/bch-js") const BCHJS = require("../../src/bch-js")
let bchjs let bchjs
@@ -27,12 +26,12 @@ describe(`#IPFS`, () => {
}) })
}) })
describe("#createFileModel", () => { describe("#createFileModelServer", () => {
it("should throw an error if file does not exist", async () => { it("should throw an error if file does not exist", async () => {
try { try {
const path = "/non-existant-file" const path = "/non-existant-file"
await bchjs.IPFS.createFileModel(path) await bchjs.IPFS.createFileModelServer(path)
assert.equal(true, false, "Unexpected result") assert.equal(true, false, "Unexpected result")
} catch (err) { } catch (err) {
@@ -48,7 +47,7 @@ describe(`#IPFS`, () => {
.stub(bchjs.IPFS.axios, "post") .stub(bchjs.IPFS.axios, "post")
.resolves({ data: mockData.mockNewFileModel }) .resolves({ data: mockData.mockNewFileModel })
const result = await bchjs.IPFS.createFileModel(path) const result = await bchjs.IPFS.createFileModelServer(path)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, "success")
@@ -72,12 +71,12 @@ describe(`#IPFS`, () => {
}) })
}) })
describe("#uploadFile", () => { describe("#uploadFileServer", () => {
it("should throw an error if file does not exist", async () => { it("should throw an error if file does not exist", async () => {
try { try {
const path = "/non-existant-file" const path = "/non-existant-file"
await bchjs.IPFS.uploadFile(path) await bchjs.IPFS.uploadFileServer(path)
assert.equal(true, false, "Unexpected result") assert.equal(true, false, "Unexpected result")
} catch (err) { } catch (err) {
@@ -90,7 +89,7 @@ describe(`#IPFS`, () => {
try { try {
const path = `${__dirname}/ipfs.js` const path = `${__dirname}/ipfs.js`
await bchjs.IPFS.uploadFile(path) await bchjs.IPFS.uploadFileServer(path)
assert.equal(true, false, "Unexpected result") assert.equal(true, false, "Unexpected result")
} catch (err) { } catch (err) {
@@ -112,7 +111,7 @@ describe(`#IPFS`, () => {
sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mock) sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mock)
const path = `${__dirname}/ipfs.js` const path = `${__dirname}/ipfs.js`
await bchjs.IPFS.uploadFile(path, "5ec562319bfacc745e8d8a52") await bchjs.IPFS.uploadFileServer(path, "5ec562319bfacc745e8d8a52")
assert.equal(true, false, "Unexpected result") assert.equal(true, false, "Unexpected result")
} catch (err) { } catch (err) {
@@ -126,7 +125,7 @@ describe(`#IPFS`, () => {
sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mockData.uploadData) sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mockData.uploadData)
const path = `${__dirname}/ipfs.js` const path = `${__dirname}/ipfs.js`
const result = await bchjs.IPFS.uploadFile( const result = await bchjs.IPFS.uploadFileServer(
path, path,
"5ec562319bfacc745e8d8a52" "5ec562319bfacc745e8d8a52"
) )
@@ -192,4 +191,253 @@ describe(`#IPFS`, () => {
assert.property(result, "fileName") assert.property(result, "fileName")
}) })
}) })
describe("#createFileModelWeb", () => {
it("should throw an error if file is undefined", async () => {
try {
let file
await bchjs.IPFS.createFileModelWeb(file)
assert.equal(true, false, "Unexpected result")
} catch (err) {
//console.log(`err.message: ${err.message}`)
assert.include(err.message, `File is required`)
}
})
it("should throw an error if file is empty", async () => {
try {
const file = {}
await bchjs.IPFS.createFileModelWeb(file)
assert.equal(true, false, "Unexpected result")
} catch (err) {
//console.log(`err.message: ${err.message}`)
assert.include(
err.message,
`File should have the property 'name' of string type`
)
}
})
it("should throw an error if 'name' property is not included", async () => {
try {
const file = {
size: 5000
}
await bchjs.IPFS.createFileModelWeb(file)
assert.equal(true, false, "Unexpected result")
} catch (err) {
//console.log(`err.message: ${err.message}`)
assert.include(
err.message,
`File should have the property 'name' of string type`
)
}
})
it("should throw an error if 'size' property is not included", async () => {
try {
const file = {
name: "ipfs.js"
}
await bchjs.IPFS.createFileModelWeb(file)
assert.equal(true, false, "Unexpected result")
} catch (err) {
//console.log(`err.message: ${err.message}`)
assert.include(
err.message,
`File should have the property 'size' of number type`
)
}
})
it("should create a new file model", async () => {
const file = {
name: "ipfs.js",
size: 5000,
type: "text/plain"
}
sandbox
.stub(bchjs.IPFS.axios, "post")
.resolves({ data: mockData.mockNewFileModel })
const result = await bchjs.IPFS.createFileModelWeb(file)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "hostingCostBCH")
assert.property(result, "hostingCostUSD")
assert.property(result, "file")
assert.property(result.file, "payloadLink")
assert.property(result.file, "hasBeenPaid")
assert.property(result.file, "_id")
assert.property(result.file, "schemaVersion")
assert.property(result.file, "size")
assert.property(result.file, "fileName")
assert.property(result.file, "fileExtension")
assert.property(result.file, "createdTimestamp")
assert.property(result.file, "hostingCost")
assert.property(result.file, "walletIndex")
assert.property(result.file, "bchAddr")
})
})
describe("#uploadFileWeb", () => {
it("should throw an error if file is undefined", async () => {
try {
let file
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, "Unexpected result")
} catch (err) {
//console.log(`err.message: ${err.message}`)
assert.include(err.message, `File is required`)
}
})
it("should throw an error if file is empty", async () => {
try {
const file = {}
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, "Unexpected result")
} catch (err) {
//console.log(`err.message: ${err.message}`)
assert.include(
err.message,
`File should have the property 'name' of string type`
)
}
})
it("should throw an error if 'name' property is not included", async () => {
try {
const file = {
size: 5000,
type: "text/plain"
}
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, "Unexpected result")
} catch (err) {
//console.log(`err.message: ${err.message}`)
assert.include(
err.message,
`File should have the property 'name' of string type`
)
}
})
it("should throw an error if 'size' property is not included", async () => {
try {
const file = {
name: "ipfs.js",
type: "text/plain"
}
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, "Unexpected result")
} catch (err) {
//console.log(`err.message: ${err.message}`)
assert.include(
err.message,
`File should have the property 'size' of number type`
)
}
})
it("should throw an error if 'type' property is not included", async () => {
try {
const file = {
name: "ipfs.js",
size: 5000
}
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, "Unexpected result")
} catch (err) {
//console.log(`err.message: ${err.message}`)
assert.include(
err.message,
`File should have the property 'type' of string type`
)
}
})
it("should throw an error if modelId is not included", async () => {
try {
const file = {
name: "ipfs.js",
size: 5000,
type: "text/plain"
}
await bchjs.IPFS.uploadFileWeb(file)
assert.equal(true, false, "Unexpected result")
} catch (err) {
//console.log(`err.message: ${err.message}`)
assert.include(err.message, `Must include a file model ID`)
}
})
it("Should throw error if the file was not uploaded", async () => {
try {
const mock = {
successful: [],
failed: [
{
id: "file id"
}
]
}
sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mock)
const file = {
name: "ipfs.js",
size: 5000,
type: "text/plain"
}
await bchjs.IPFS.uploadFileWeb(file, "5ec562319bfacc745e8d8a52")
assert.equal(true, false, "Unexpected result")
} catch (err) {
//console.log(err)
assert.include(err.message, `The file could not be uploaded`)
}
})
it("should return file object if the file is uploaded", async () => {
try {
sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mockData.uploadData)
const file = {
name: "ipfs.js",
size: 5000,
type: "text/plain"
}
const result = await bchjs.IPFS.uploadFileWeb(
file,
"5ec562319bfacc745e8d8a52"
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "schemaVersion")
assert.property(result, "size")
assert.property(result, "fileId")
assert.property(result, "fileName")
assert.property(result, "fileExtension")
} catch (err) {
//console.log(err)
assert.equal(true, false, "Unexpected result")
}
})
})
}) })