Skip to content

Commit

Permalink
optionally run ngrok on startup (#1273)
Browse files Browse the repository at this point in the history
Added option to run ngrok on startup
Added menu item to copy the ngrok url to the clipboard
  • Loading branch information
Justin Wilaby committed Jan 31, 2019
1 parent adc3de8 commit 860c708
Show file tree
Hide file tree
Showing 19 changed files with 664 additions and 6,934 deletions.
63 changes: 63 additions & 0 deletions packages/app/client/src/data/action/frameworkSettingsActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { FrameworkSettings } from '@bfemulator/app-shared';
import { Action } from 'redux';

export const FRAMEWORK_SETTINGS_CHANGED = 'FRAMEWORK_SETTINGS_CHANGED';
export const GET_FRAMEWORK_SETTINGS = 'GET_FRAMEWORK_SETTINGS';
export const SAVE_FRAMEWORK_SETTINGS = 'SAVE_FRAMEWORK_SETTINGS';

export interface FrameworkSettingsAction<T = void> extends Action {
payload: T;
}

export function frameworkSettingsChanged(payload: FrameworkSettings): FrameworkSettingsAction<FrameworkSettings> {
return {
type: FRAMEWORK_SETTINGS_CHANGED,
payload,
};
}

export function getFrameworkSettings(): FrameworkSettingsAction {
return {
type: GET_FRAMEWORK_SETTINGS,
payload: void 0,
};
}

export function saveFrameworkSettings(payload: FrameworkSettings): FrameworkSettingsAction<FrameworkSettings> {
return {
type: SAVE_FRAMEWORK_SETTINGS,
payload,
};
}
59 changes: 59 additions & 0 deletions packages/app/client/src/data/reducer/frameworkSettingsReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { FrameworkSettings as FS } from '@bfemulator/app-shared';

import { FRAMEWORK_SETTINGS_CHANGED, FrameworkSettingsAction } from '../action/frameworkSettingsActions';

const defaults: FS = {
autoUpdate: true,
bypassNgrokLocalhost: true,
runNgrokAtStartup: false,
collectUsageData: true,
locale: '',
localhost: '',
ngrokPath: '',
stateSizeLimit: 64,
use10Tokens: false,
useCodeValidation: false,
usePrereleases: false,
};

export function framework(state: FS = defaults, action: FrameworkSettingsAction<FS>): FS {
switch (action.type) {
case FRAMEWORK_SETTINGS_CHANGED:
return { ...state, ...action.payload };

default:
return state;
}
}
144 changes: 144 additions & 0 deletions packages/app/client/src/data/sagas/frameworkSettingsSagas.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { SharedConstants, newNotification } from '@bfemulator/app-shared';
import { applyMiddleware, combineReducers, createStore } from 'redux';
import sagaMiddlewareFactory from 'redux-saga';

import { CONTENT_TYPE_APP_SETTINGS, DOCUMENT_ID_APP_SETTINGS } from '../../constants';
import * as EditorActions from '../action/editorActions';
import {
getFrameworkSettings as getFrameworkSettingsAction,
saveFrameworkSettings as saveFrameworkSettingsAction,
} from '../action/frameworkSettingsActions';
import { CommandServiceImpl } from '../../platform/commands/commandServiceImpl';
import { beginAdd } from '../action/notificationActions';
import { editor } from '../reducer/editor';
import {
frameworkSettingsChanged,
GET_FRAMEWORK_SETTINGS,
SAVE_FRAMEWORK_SETTINGS,
} from '../action/frameworkSettingsActions';
import { framework } from '../reducer/frameworkSettingsReducer';

import {
activeDocumentSelector,
frameworkSettingsSagas,
getFrameworkSettings,
saveFrameworkSettings,
} from './frameworkSettingsSagas';

import { put, takeEvery, select } from 'redux-saga/effects';

jest.mock(
'../../ui/dialogs/',
() =>
new Proxy(
{},
{
get(): any {
return {};
},
}
)
);

const sagaMiddleWare = sagaMiddlewareFactory();
const mockStore = createStore(combineReducers({ framework, editor }), {}, applyMiddleware(sagaMiddleWare));
sagaMiddleWare.run(frameworkSettingsSagas);

jest.mock('../store', () => ({
get store() {
return mockStore;
},
}));

mockStore.dispatch(
EditorActions.open({
contentType: CONTENT_TYPE_APP_SETTINGS,
documentId: DOCUMENT_ID_APP_SETTINGS,
isGlobal: true,
meta: null,
})
);
describe('The frameworkSettingsSagas', () => {
it('should register the expected generators', () => {
const it = frameworkSettingsSagas();
expect(it.next().value).toEqual(takeEvery(GET_FRAMEWORK_SETTINGS, getFrameworkSettings));
expect(it.next().value).toEqual(takeEvery(SAVE_FRAMEWORK_SETTINGS, saveFrameworkSettings));
});

it('should get the framework settings when using the happy path', () => {
const it = getFrameworkSettings();
let next = it.next();
expect(next.value).toEqual(CommandServiceImpl.remoteCall(SharedConstants.Commands.Settings.LoadAppSettings));

next = it.next({});
expect(next.value).toEqual(put(frameworkSettingsChanged({})));
});

it('should send a notification when something goes wrong while getting the framework settings', () => {
const it = getFrameworkSettings();
it.next();
const errMsg = `Error while loading emulator settings: oh noes!`;
const notification = newNotification(errMsg);
notification.timestamp = jasmine.any(Number);
notification.id = jasmine.any(String);
expect(it.throw('oh noes!').value).toEqual(put(beginAdd(notification)));
});

it('should save the framework settings', () => {
const it = saveFrameworkSettings(saveFrameworkSettingsAction({}));
// remote call to save the settings
expect(it.next().value).toEqual(
CommandServiceImpl.remoteCall(SharedConstants.Commands.Settings.SaveAppSettings, {})
);
const selector = it.next().value;
// selector to get the active document from the state
expect(selector).toEqual(select(activeDocumentSelector));
const value = selector.SELECT.selector(mockStore.getState());
// put the dirty state to false
expect(it.next(value).value).toEqual(put(EditorActions.setDirtyFlag(value.documentId, false)));
// get the settings from the main side again
expect(it.next().value).toEqual(put(getFrameworkSettingsAction()));
});

it('should send a notification when saving the settings fails', () => {
const it = saveFrameworkSettings(saveFrameworkSettingsAction({}));
it.next();
const errMsg = `Error while saving emulator settings: oh noes!`;
const notification = newNotification(errMsg);
notification.timestamp = jasmine.any(Number);
notification.id = jasmine.any(String);
expect(it.throw('oh noes!').value).toEqual(put(beginAdd(notification)));
});
});
83 changes: 83 additions & 0 deletions packages/app/client/src/data/sagas/frameworkSettingsSagas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { FrameworkSettings, newNotification, SharedConstants } from '@bfemulator/app-shared';

import { CommandServiceImpl } from '../../platform/commands/commandServiceImpl';
import * as EditorActions from '../action/editorActions';
import {
FrameworkSettingsAction,
frameworkSettingsChanged,
GET_FRAMEWORK_SETTINGS,
getFrameworkSettings as getFrameworkSettingsAction,
SAVE_FRAMEWORK_SETTINGS,
} from '../action/frameworkSettingsActions';
import { beginAdd } from '../action/notificationActions';
import { Document } from '../reducer/editor';
import { RootState } from '../store';

import { ForkEffect, put, select, takeEvery } from 'redux-saga/effects';

export const activeDocumentSelector = (state: RootState) => {
const { editors, activeEditor } = state.editor;
const { activeDocumentId } = editors[activeEditor];
return editors[activeEditor].documents[activeDocumentId];
};

export function* getFrameworkSettings(): IterableIterator<any> {
try {
const framework = yield CommandServiceImpl.remoteCall(SharedConstants.Commands.Settings.LoadAppSettings);
yield put(frameworkSettingsChanged(framework));
} catch (e) {
const errMsg = `Error while loading emulator settings: ${e}`;
const notification = newNotification(errMsg);
yield put(beginAdd(notification));
}
}

export function* saveFrameworkSettings(action: FrameworkSettingsAction<FrameworkSettings>): IterableIterator<any> {
try {
yield CommandServiceImpl.remoteCall(SharedConstants.Commands.Settings.SaveAppSettings, action.payload);
const activeDoc: Document = yield select(activeDocumentSelector);
yield put(EditorActions.setDirtyFlag(activeDoc.documentId, false)); // mark as clean
yield put(getFrameworkSettingsAction()); // sync with main - do not assume main hasn't processed this in some way
} catch (e) {
const errMsg = `Error while saving emulator settings: ${e}`;
const notification = newNotification(errMsg);
yield put(beginAdd(notification));
}
}

export function* frameworkSettingsSagas(): IterableIterator<ForkEffect> {
yield takeEvery(GET_FRAMEWORK_SETTINGS, getFrameworkSettings);
yield takeEvery(SAVE_FRAMEWORK_SETTINGS, saveFrameworkSettings);
}
8 changes: 5 additions & 3 deletions packages/app/client/src/data/sagas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,22 @@ import { azureAuthSagas } from './azureAuthSaga';
import { botSagas } from './botSagas';
import { editorSagas } from './editorSagas';
import { endpointSagas } from './endpointSagas';
import { frameworkSettingsSagas } from './frameworkSettingsSagas';
import { navBarSagas } from './navBarSagas';
import { notificationSagas } from './notificationSagas';
import { resourceSagas } from './resourcesSagas';
import { servicesExplorerSagas } from './servicesExplorerSagas';
import { welcomePageSagas } from './welcomePageSagas';

export const applicationSagas = [
servicesExplorerSagas,
botSagas,
endpointSagas,
azureAuthSagas,
botSagas,
editorSagas,
endpointSagas,
frameworkSettingsSagas,
navBarSagas,
notificationSagas,
resourceSagas,
servicesExplorerSagas,
welcomePageSagas,
];
Loading

0 comments on commit 860c708

Please sign in to comment.