feat: 🎸 expose simplified async IPC handler with error support

This commit is contained in:
Randall Morey committed 2020-12-03 16:59:45 -05:00
1 parent 77f21f696d
commit 4d7d642a68
3 files changed
+79 -27

No files matched your search

@@ -49,10 +49,10 @@ export default class ScopesScopeProjectsTargetsRoute extends Route {
try {
await this.ipc.invoke('connect', {
target_id: model.target.id,
auth_token: this.session.data.authenticated.token,
token: this.session.data.authenticated.token,
});
} catch(e) {
this.notify.error(e.toString(), { closeAfter: null });
this.notify.error(e.message, { closeAfter: null });
}
}
}
+42 -25
View File
@@ -1,21 +1,14 @@
const { ipcMain } = require('electron');
const isDev = require('electron-is-dev');
const log = (method, request) => {
if (isDev) console.log(`[ipc-main](${method}): `, request);
};
const handle = require('./ipc-handler');
const { lookpath } = require('lookpath');
const { exec } = require('child_process');
let origin = 'serve://boundary';
let origin = '';
/**
* Returns the current runtime origin, which is used by the main thread to
* rewrite the CSP to allow requests.
*/
ipcMain.handle('getOrigin', async (event, request) => {
log('getOrigin', request);
return origin;
});
handle('getOrigin', () => origin);
/**
* TODO: sets the main process origin to the specified value. This value will
@@ -25,29 +18,53 @@ ipcMain.handle('getOrigin', async (event, request) => {
*
* For now, we update the internal origin but do not yet rewrite the CSP.
*/
ipcMain.handle('setOrigin', async (event, request) => {
log('setOrigin', request);
origin = request?.data;
return origin;
});
handle('setOrigin', (requestOrigin) => origin = requestOrigin);
/**
* Establishes a boundary session and returns session details.
* TODO: Return session details.
*/
ipcMain.handle('connect', async (event, request) => {
log('connect payload', request);
handle('connect', async ({ target_id, token }) => {
const cliAvailable = await lookpath('boundary');
log('cli check', cliAvailable);
if(!cliAvailable) { throw new Error('CLI unavailable.'); }
const connectCmd = `boundary connect -target-id=${request.target_id} -token=${request.auth_token}`;
log('connect command', connectCmd);
const connectCmd = `boundary connect -target-id=${target_id} -token=${token}`;
exec(connectCmd, (output) => {
log('connect output', output);
}, (error) => {
log('connect error', error);
// TODO: this isn't the *exact* promise, because apparently exec doesn't
// call the success callback until the process exits. We need a way to
// resolve the promise if the process doesn't exit AND we receive valid
// JSON on stdout, since this indicates the connection was successful.
//
// TODO: if there is an existing promiserified exec tool, we might consider
// using it to help make this cleaner.
// You can throw exceptions, or allow them to occur, and this is supported.
// Exceptions thrown in this way will be returned to the UI as an
// IPC rejection. However in most cases, you probably want to return a
// specific exception object of your own design, since Error instances
// originating from the underlying system will be noisey and not very helpful
// to users.
//
//throw new Error('Random error! Should still work!');
//throw { message: 'Random POJO exception! Should still work!' };
return new Promise((resolve, reject) => {
exec(connectCmd, (error, stdout, stderr) => {
if (error) {
// For example, you could just reject(error) here, but this is the raw
// error from the underlying exec implementation. It's not a very
// helpful message for users, so it's recommended to craft nice
// POJO representation and throw it or promise->reject it.
//
// TODO: connection errors may now return JSON on stderr, which we
// would attempt to parse here to use a basis for rejection.
const { code } = error;
return reject({
code,
message: `OH NOSE! Command failed with code ${code}`
});
}
return resolve(stdout);
});
});
});
@@ -0,0 +1,35 @@
const { ipcMain } = require('electron');
const isDev = require('electron-is-dev');
const log = (method, request='', response='', type='log') => {
const requestString = typeof request === 'object'
? JSON.stringify(request)
: request;
// only log in dev mode
if (isDev) {
console[type](
`[ipc] ${method}(${requestString}): `,
type === 'error' ? 'ERROR' : '',
response
);
}
};
module.exports = function handle (command, handler) {
ipcMain.handle(command, async function (event, request) {
const requestPayload = request;
let result;
try {
result = await handler(requestPayload);
log(command, requestPayload, result);
} catch (e) {
// If e is an Error instance, we need to extract the message and wrap
// it in a POJO before stringifying it. If not an Error, e is assumed to
// be already a POJO.
const errorMessage = (e instanceof Error) ? { message: e.message } : e;
result = new Error(JSON.stringify(errorMessage));
log(command, requestPayload, e, 'error');
}
return result;
});
};