-
Notifications
You must be signed in to change notification settings - Fork 5k
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 #2426 from ethereum/issue/2266
CustomProvider
- Loading branch information
Showing
5 changed files
with
324 additions
and
14 deletions.
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
138 changes: 138 additions & 0 deletions
138
packages/web3-providers/src/providers/CustomProvider.js
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,138 @@ | ||
/* | ||
This file is part of web3.js. | ||
web3.js is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU Lesser General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
web3.js is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU Lesser General Public License for more details. | ||
You should have received a copy of the GNU Lesser General Public License | ||
along with web3.js. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
/** | ||
* @file CustomProvider.js | ||
* @author Samuel Furter <samuel@ethereum.org> | ||
* @date 2019 | ||
*/ | ||
import JsonRpcMapper from '../mappers/JsonRpcMapper'; | ||
import JsonRpcResponseValidator from '../validators/JsonRpcResponseValidator'; | ||
|
||
export default class CustomProvider { | ||
/** | ||
* @param {Object} connection | ||
* | ||
* @constructor | ||
*/ | ||
constructor(connection) { | ||
this.host = 'CustomProvider'; | ||
this.connection = connection; | ||
this.checkConnectionMethods(); | ||
} | ||
|
||
/** | ||
* Checks if the given custom provider does have the required methods | ||
* | ||
* @method checkConnectionMethods | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
checkConnectionMethods() { | ||
if (this.connection.send || this.connection.sendAsync) { | ||
return true; | ||
} | ||
|
||
throw new Error('Invalid provider injected!'); | ||
} | ||
|
||
/** | ||
* Added this method to have a better error message if someone is trying to create a subscription with this provider. | ||
*/ | ||
subscribe() { | ||
throw new Error('Subscriptions are not supported with the CustomProvider.'); | ||
} | ||
|
||
/** | ||
* Added this method to have a better error message if someone is trying to unsubscribe with this provider. | ||
*/ | ||
unsubscribe() { | ||
throw new Error('Subscriptions are not supported with the CustomProvider.'); | ||
} | ||
|
||
/** | ||
* Creates the JSON-RPC payload and sends it to the node. | ||
* | ||
* @method send | ||
* | ||
* @param {String} method | ||
* @param {Array} parameters | ||
* | ||
* @returns {Promise<any>} | ||
*/ | ||
send(method, parameters) { | ||
return this.sendPayload(JsonRpcMapper.toPayload(method, parameters)).then((response) => { | ||
const validationResult = JsonRpcResponseValidator.validate(response); | ||
|
||
if (validationResult instanceof Error) { | ||
throw validationResult; | ||
} | ||
|
||
return response.result; | ||
}); | ||
} | ||
|
||
/** | ||
* Creates the JSON-RPC batch payload and sends it to the node. | ||
* | ||
* @method sendBatch | ||
* | ||
* @param {AbstractMethod[]} methods | ||
* @param {AbstractWeb3Module} moduleInstance | ||
* | ||
* @returns Promise<Object[]> | ||
*/ | ||
sendBatch(methods, moduleInstance) { | ||
let payload = []; | ||
|
||
methods.forEach((method) => { | ||
method.beforeExecution(moduleInstance); | ||
payload.push(JsonRpcMapper.toPayload(method.rpcMethod, method.parameters)); | ||
}); | ||
|
||
return this.sendPayload(payload); | ||
} | ||
|
||
/** | ||
* Sends the JSON-RPC payload to the node. | ||
* | ||
* @method sendPayload | ||
* | ||
* @param {Object} payload | ||
* | ||
* @returns {Promise<any>} | ||
*/ | ||
sendPayload(payload) { | ||
return new Promise((resolve, reject) => { | ||
if (this.connection.sendAsync) { | ||
this.connection.sendAsync(payload, (error, response) => { | ||
if (!error) { | ||
resolve(response); | ||
} | ||
|
||
reject(error); | ||
}); | ||
|
||
return; | ||
} | ||
|
||
this.connection.send(payload, (error, response) => { | ||
if (!error) { | ||
resolve(response); | ||
} | ||
|
||
reject(error); | ||
}); | ||
}); | ||
} | ||
} |
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
137 changes: 137 additions & 0 deletions
137
packages/web3-providers/tests/src/providers/CustomProviderTest.js
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,137 @@ | ||
import JsonRpcMapper from '../../../src/mappers/JsonRpcMapper'; | ||
import JsonRpcResponseValidator from '../../../src/validators/JsonRpcResponseValidator'; | ||
import AbstractWeb3Module from '../../__mocks__/AbstractWeb3Module'; | ||
import AbstractMethod from '../../__mocks__/AbstractMethod'; | ||
import CustomProvider from '../../../src/providers/CustomProvider'; | ||
|
||
// Mocks | ||
jest.mock('../../../src/factories/ProvidersModuleFactory'); | ||
|
||
/** | ||
* CustomProvider test | ||
*/ | ||
describe('CustomProviderTest', () => { | ||
let customProvider, connectionMock; | ||
|
||
beforeEach(() => { | ||
connectionMock = {send: jest.fn(), sendAsync: jest.fn()}; | ||
|
||
customProvider = new CustomProvider(connectionMock); | ||
}); | ||
|
||
it('constructor check', () => { | ||
expect(customProvider.connection).toEqual(connectionMock); | ||
}); | ||
|
||
it('calls subscribe and throws error', () => { | ||
expect(() => { | ||
customProvider.subscribe(); | ||
}).toThrow('Subscriptions are not supported with the CustomProvider.'); | ||
}); | ||
|
||
it('calls unsubscribe and throws error', () => { | ||
expect(() => { | ||
customProvider.unsubscribe(); | ||
}).toThrow('Subscriptions are not supported with the CustomProvider.'); | ||
}); | ||
|
||
it('calls send and returns with a resolved promise', async () => { | ||
JsonRpcMapper.toPayload = jest.fn(); | ||
JsonRpcMapper.toPayload.mockReturnValueOnce({id: '0x0'}); | ||
|
||
JsonRpcResponseValidator.validate = jest.fn(); | ||
JsonRpcResponseValidator.validate.mockReturnValueOnce(true); | ||
|
||
connectionMock.sendAsync = jest.fn((payload, callback) => { | ||
expect(payload).toEqual({id: '0x0'}); | ||
|
||
callback(false, {result: true}); | ||
}); | ||
|
||
const response = await customProvider.send('rpc_method', []); | ||
|
||
expect(response).toEqual(true); | ||
|
||
expect(JsonRpcMapper.toPayload).toHaveBeenCalledWith('rpc_method', []); | ||
|
||
expect(JsonRpcResponseValidator.validate).toHaveBeenCalledWith({result: true}); | ||
|
||
expect(connectionMock.sendAsync).toHaveBeenCalled(); | ||
}); | ||
|
||
it('calls send without the sendAsync method and returns with a resolved promise', async () => { | ||
JsonRpcMapper.toPayload = jest.fn(); | ||
JsonRpcMapper.toPayload.mockReturnValueOnce({id: '0x0'}); | ||
|
||
JsonRpcResponseValidator.validate = jest.fn(); | ||
JsonRpcResponseValidator.validate.mockReturnValueOnce(true); | ||
|
||
connectionMock.sendAsync = false; | ||
connectionMock.send = jest.fn((payload, callback) => { | ||
expect(payload).toEqual({id: '0x0'}); | ||
|
||
callback(false, {result: true}); | ||
}); | ||
|
||
const response = await customProvider.send('rpc_method', []); | ||
|
||
expect(response).toEqual(true); | ||
|
||
expect(JsonRpcMapper.toPayload).toHaveBeenCalledWith('rpc_method', []); | ||
|
||
expect(JsonRpcResponseValidator.validate).toHaveBeenCalledWith({result: true}); | ||
|
||
expect(connectionMock.send).toHaveBeenCalled(); | ||
}); | ||
|
||
it('calls send and returns with a rejected promise because of an invalid JSON-RPC response', async () => { | ||
JsonRpcMapper.toPayload = jest.fn(); | ||
JsonRpcMapper.toPayload.mockReturnValueOnce({id: '0x0'}); | ||
|
||
JsonRpcResponseValidator.validate = jest.fn(); | ||
JsonRpcResponseValidator.validate.mockReturnValueOnce(new Error('invalid')); | ||
|
||
connectionMock.sendAsync = jest.fn((payload, callback) => { | ||
expect(payload).toEqual({id: '0x0'}); | ||
|
||
callback(false, true); | ||
}); | ||
|
||
await expect(customProvider.send('rpc_method', [])).rejects.toThrow('invalid'); | ||
|
||
expect(JsonRpcResponseValidator.validate).toHaveBeenCalledWith(true); | ||
|
||
expect(JsonRpcMapper.toPayload).toHaveBeenCalledWith('rpc_method', []); | ||
|
||
expect(connectionMock.sendAsync).toHaveBeenCalled(); | ||
}); | ||
|
||
it('calls sendBatch and returns with a resolved promise', async () => { | ||
const abstractMethodMock = new AbstractMethod(); | ||
|
||
const moduleInstanceMock = new AbstractWeb3Module(); | ||
|
||
abstractMethodMock.beforeExecution = jest.fn(); | ||
abstractMethodMock.rpcMethod = 'rpc_method'; | ||
abstractMethodMock.parameters = []; | ||
|
||
JsonRpcMapper.toPayload = jest.fn(); | ||
JsonRpcMapper.toPayload.mockReturnValueOnce({id: '0x0'}); | ||
|
||
connectionMock.sendAsync = jest.fn((payload, callback) => { | ||
expect(payload).toEqual([{id: '0x0'}]); | ||
|
||
callback(false, true); | ||
}); | ||
|
||
const response = await customProvider.sendBatch([abstractMethodMock], moduleInstanceMock); | ||
|
||
expect(response).toEqual(true); | ||
|
||
expect(JsonRpcMapper.toPayload).toHaveBeenCalledWith('rpc_method', []); | ||
|
||
expect(connectionMock.sendAsync).toHaveBeenCalled(); | ||
|
||
expect(abstractMethodMock.beforeExecution).toHaveBeenCalled(); | ||
}); | ||
}); |
Oops, something went wrong.