You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2017/08/10 13:15:21 UTC
[couchdb-fauxton] branch master updated: (#947) Refactor CORS addon
This is an automated email from the ASF dual-hosted git repository.
garren pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb-fauxton.git
The following commit(s) were added to refs/heads/master by this push:
new bc94e63 (#947) Refactor CORS addon
bc94e63 is described below
commit bc94e637fd8b50db6f1febbb7567451d41a41c26
Author: Antonio Maranhao <30...@users.noreply.github.com>
AuthorDate: Thu Aug 10 09:15:19 2017 -0400
(#947) Refactor CORS addon
* Moved components to individual files
* Fixed components. All tests pass
* Clean up old code
* Remove use of 'var' and changed name of action functions
* Use fetch instead of Backbone models
* Remove Backbone related files and code cleanup
* Jest tests updated
---
app/addons/config/layout.js | 2 +-
app/addons/config/routes.js | 2 -
app/addons/cors/__tests__/actions.test.js | 111 ++++-----
app/addons/cors/__tests__/components.test.js | 156 +++++++-----
app/addons/cors/__tests__/helpers.test.js | 53 ++++
app/addons/cors/__tests__/resources.test.js | 74 ------
app/addons/cors/__tests__/stores.test.js | 100 --------
app/addons/cors/actions.js | 234 ++++++------------
app/addons/cors/actiontypes.js | 7 +-
app/addons/cors/api.js | 120 +++++++++
app/addons/cors/base.js | 8 +-
app/addons/cors/components.js | 356 +--------------------------
app/addons/cors/components/CORSContainer.js | 46 ++++
app/addons/cors/components/CORSScreen.js | 150 +++++++++++
app/addons/cors/components/OriginInput.js | 60 +++++
app/addons/cors/components/OriginRow.js | 86 +++++++
app/addons/cors/components/OriginTable.js | 48 ++++
app/addons/cors/components/Origins.js | 45 ++++
app/addons/cors/{base.js => helpers.js} | 33 ++-
app/addons/cors/reducers.js | 67 +++++
app/addons/cors/resources.js | 110 ---------
app/addons/cors/stores.js | 190 --------------
22 files changed, 946 insertions(+), 1112 deletions(-)
diff --git a/app/addons/config/layout.js b/app/addons/config/layout.js
index bc8a504..1a75288 100644
--- a/app/addons/config/layout.js
+++ b/app/addons/config/layout.js
@@ -51,7 +51,7 @@ export const ConfigLayout = ({showCors, docURL, node, endpoint, crumbs}) => {
}
];
const selectedTab = showCors ? 'CORS' : 'Main config';
- const content = showCors ? <CORSComponents.CORSController/> : <ConfigComponents.ConfigTableController node={node} />;
+ const content = showCors ? <CORSComponents.CORSContainer node={node} url={endpoint}/> : <ConfigComponents.ConfigTableController node={node} />;
return (
<div id="dashboard" className="with-sidebar">
<ConfigHeader
diff --git a/app/addons/config/routes.js b/app/addons/config/routes.js
index 370d126..4b924d4 100644
--- a/app/addons/config/routes.js
+++ b/app/addons/config/routes.js
@@ -13,7 +13,6 @@
import React from 'react';
import FauxtonAPI from "../../core/api";
import Config from "./resources";
-import CORSActions from "../cors/actions";
import ClusterActions from "../cluster/cluster.actions";
import ConfigActions from "./actions";
import Layout from './layout';
@@ -67,7 +66,6 @@ var ConfigPerNodeRouteObject = FauxtonAPI.RouteObject.extend({
},
configCorsForNode: function (node) {
- CORSActions.fetchAndEditCors(node);
return <Layout
node={node}
docURL={this.configs.documentation}
diff --git a/app/addons/cors/__tests__/actions.test.js b/app/addons/cors/__tests__/actions.test.js
index 124845d..10f7a41 100644
--- a/app/addons/cors/__tests__/actions.test.js
+++ b/app/addons/cors/__tests__/actions.test.js
@@ -11,7 +11,8 @@
// the License.
import utils from "../../../../test/mocha/testUtils";
import FauxtonAPI from "../../../core/api";
-import Actions from "../actions";
+import * as Actions from "../actions";
+import * as CorsAPI from "../api";
import sinon from "sinon";
const assert = utils.assert;
@@ -21,103 +22,81 @@ describe('CORS actions', () => {
describe('save', () => {
- let localNode = 'node2@127.0.0.1';
+ const localNode = 'node2@127.0.0.1';
+ const baseURL = 'http://localhost:8000/#_config/couchdb@localhost/cors';
+ const dispatch = sinon.stub();
+ const spyUpdateEnableCorsToHttpd = sinon.stub(CorsAPI, 'updateEnableCorsToHttpd');
+ const spyUpdateCorsOrigins = sinon.stub(CorsAPI, 'updateCorsOrigins');
+ const spyUpdateCorsCredentials = sinon.stub(CorsAPI, 'updateCorsCredentials');
+ const spyUpdateCorsHeaders = sinon.stub(CorsAPI, 'updateCorsHeaders');
+ const spyUpdateCorsMethods = sinon.stub(CorsAPI, 'updateCorsMethods');
afterEach(() => {
- restore(Actions.saveCorsOrigins);
-
- restore(FauxtonAPI.when);
+ restore(FauxtonAPI.Promise.all);
restore(FauxtonAPI.addNotification);
- });
-
- it('should save cors enabled to httpd', () => {
- var spy = sinon.spy(Actions, 'saveEnableCorsToHttpd');
-
- Actions.saveCors({
- enableCors: false,
- node: localNode
- });
- assert.ok(spy.calledWith(false));
+ spyUpdateEnableCorsToHttpd.reset();
+ spyUpdateCorsOrigins.reset();
+ spyUpdateCorsCredentials.reset();
+ spyUpdateCorsHeaders.reset();
+ spyUpdateCorsMethods.reset();
});
- it('does not save cors origins if cors not enabled', () => {
- var spy = sinon.spy(Actions, 'saveCorsOrigins');
-
- Actions.saveCors({
- enableCors: false,
- origins: ['*'],
+ it('should save enable_cors to httpd', () => {
+ Actions.saveCors(baseURL, {
+ corsEnabled: false,
node: localNode
- });
+ })(dispatch);
- assert.notOk(spy.calledOnce);
+ assert.ok(spyUpdateEnableCorsToHttpd.calledWith(baseURL, localNode, false));
});
- it('saves cors origins', () => {
- var spy = sinon.spy(Actions, 'saveCorsOrigins');
-
- Actions.saveCors({
- enableCors: true,
+ it('does not save CORS origins if CORS is not enabled', () => {
+ Actions.saveCors(baseURL, {
+ corsEnabled: false,
origins: ['*'],
node: localNode
- });
+ })(dispatch);
- assert.ok(spy.calledWith('*'));
+ assert.notOk(spyUpdateCorsOrigins.called);
});
- it('saves cors allow credentials', () => {
- var spy = sinon.spy(Actions, 'saveCorsCredentials');
-
- Actions.saveCors({
- enableCors: true,
- origins: ['https://testdomain.com'],
- node: localNode
- });
-
- assert.ok(spy.calledOnce);
- });
-
- it('saves cors headers', () => {
- var spy = sinon.spy(Actions, 'saveCorsHeaders');
-
- Actions.saveCors({
- enableCors: true,
- origins: ['https://testdomain.com'],
+ it('saves CORS origins', () => {
+ Actions.saveCors(baseURL, {
+ corsEnabled: true,
+ origins: ['*'],
node: localNode
- });
+ })(dispatch);
- assert.ok(spy.calledOnce);
+ assert.ok(spyUpdateCorsOrigins.calledWith(baseURL, localNode, '*'));
});
- it('saves cors methods', () => {
- var spy = sinon.spy(Actions, 'saveCorsMethods');
-
- Actions.saveCors({
- enableCors: true,
+ it('saves CORS credentials, headers and methods', () => {
+ Actions.saveCors(baseURL, {
+ corsEnabled: true,
origins: ['https://testdomain.com'],
node: localNode
- });
-
- assert.ok(spy.calledOnce);
+ })(dispatch);
+ assert.ok(spyUpdateCorsCredentials.calledOnce);
+ assert.ok(spyUpdateCorsHeaders.calledOnce);
+ assert.ok(spyUpdateCorsMethods.calledOnce);
});
it('shows notification on successful save', () => {
- var stub = sinon.stub(FauxtonAPI, 'when');
- var spy = sinon.spy(FauxtonAPI, 'addNotification');
- var promise = FauxtonAPI.Deferred();
- promise.resolve();
+ const stub = sinon.stub(FauxtonAPI.Promise, 'all');
+ const spyAddNotification = sinon.spy(FauxtonAPI, 'addNotification');
+ const promise = FauxtonAPI.Promise.resolve();
stub.returns(promise);
- Actions.saveCors({
+ return Actions.saveCors(baseURL, {
enableCors: true,
origins: ['https://testdomain.com'],
node: localNode
+ })(dispatch).then(() => {
+ assert.ok(spyAddNotification.called);
});
-
- assert.ok(spy.calledOnce);
});
-
});
describe('Sanitize origins', () => {
diff --git a/app/addons/cors/__tests__/components.test.js b/app/addons/cors/__tests__/components.test.js
index d61f7e9..d264ed9 100644
--- a/app/addons/cors/__tests__/components.test.js
+++ b/app/addons/cors/__tests__/components.test.js
@@ -10,10 +10,8 @@
// License for the specific language governing permissions and limitations under
// the License.
import FauxtonAPI from "../../../core/api";
+import * as Helpers from "../helpers";
import Views from "../components";
-import Actions from "../actions";
-import Resources from "../resources";
-import Stores from "../stores";
import utils from "../../../../test/mocha/testUtils";
import React from "react";
import ReactDOM from "react-dom";
@@ -21,19 +19,11 @@ import sinon from "sinon";
import { shallow, mount } from 'enzyme';
FauxtonAPI.router = new FauxtonAPI.Router([]);
-const {assert, restore} = utils;
-const corsStore = Stores.corsStore;
+const { assert, restore } = utils;
describe('CORS Components', () => {
- describe('CorsController', () => {
-
- beforeEach(() => {
- corsStore._origins = ['http://hello.com'];
- corsStore._node = 'node2@127.0.0.1';
- corsStore._isEnabled = true;
- corsStore._configChanged = true;
- });
+ describe('CORSContainer tests', () => {
afterEach(() => {
restore(window.confirm);
@@ -43,7 +33,14 @@ describe('CORS Components', () => {
const spy = sinon.stub(window, 'confirm');
spy.returns(false);
- const wrapper = shallow(<Views.CORSController />);
+ const wrapper = shallow(<Views.CORSScreen
+ corsEnabled={true}
+ isAllOrigins={false}
+ origins={['https://localhost']}
+ saveCORS={sinon.stub()}
+ showDeleteDomainConfirmation={sinon.stub()}
+ />);
+
wrapper.find('.enable-disable .btn').simulate('click');
assert.ok(spy.calledOnce);
});
@@ -52,10 +49,13 @@ describe('CORS Components', () => {
const spy = sinon.stub(window, 'confirm');
spy.returns(false);
- // Set selected origins to empty
- corsStore._origins = [];
-
- const wrapper = shallow(<Views.CORSController />);
+ const wrapper = shallow(<Views.CORSScreen
+ corsEnabled={true}
+ isAllOrigins={true}
+ origins={[]}
+ saveCORS={sinon.stub()}
+ showDeleteDomainConfirmation={sinon.stub()}
+ />);
wrapper.find('.enable-disable .btn').simulate('click');
assert.ok(spy.notCalled);
});
@@ -64,8 +64,15 @@ describe('CORS Components', () => {
const spy = sinon.stub(window, 'confirm');
spy.returns(false);
- const wrapper = mount(<Views.CORSController />);
- wrapper.find('input').at(0).simulate('change', {target: {checked: true, value: 'all'}});
+ const wrapper = mount(<Views.CORSScreen
+ corsEnabled={true}
+ isAllOrigins={false}
+ origins={['http://localhost']}
+ saveCORS={sinon.stub()}
+ showDeleteDomainConfirmation={sinon.stub()}
+ fetchAndLoadCORSOptions={sinon.stub()}
+ />);
+ wrapper.find('input').at(0).simulate('change', { target: { checked: true, value: 'all' } });
assert.ok(spy.calledOnce);
});
@@ -73,23 +80,42 @@ describe('CORS Components', () => {
const spy = sinon.stub(window, 'confirm');
spy.returns(false);
- // Set selected origins to empty
- corsStore._origins = [];
-
- const wrapper = mount(<Views.CORSController />);
- wrapper.find('input').at(0).simulate('change', {target: {checked: true, value: 'all'}});
+ const wrapper = shallow(<Views.CORSScreen
+ corsEnabled={true}
+ isAllOrigins={false}
+ origins={[]}
+ saveCORS={sinon.stub()}
+ showDeleteDomainConfirmation={sinon.stub()}
+ />);
+ wrapper.find('input').at(0).simulate('change', { target: { checked: true, value: 'all' } });
assert.notOk(spy.calledOnce);
});
it('shows loading bars', () => {
- const wrapper = mount(<Views.CORSController />);
- Actions.toggleLoadingBarsToEnabled(true);
+ const wrapper = mount(<Views.CORSScreen
+ isLoading={true}
+ corsEnabled={true}
+ isAllOrigins={false}
+ origins={[]}
+ saveCORS={sinon.stub()}
+ showDeleteDomainConfirmation={sinon.stub()}
+ fetchAndLoadCORSOptions={sinon.stub()}
+ />);
+
assert.ok(wrapper.find('.loading-lines').exists());
});
it('hides loading bars', () => {
- const wrapper = mount(<Views.CORSController />);
- Actions.toggleLoadingBarsToEnabled(false);
+ const wrapper = mount(<Views.CORSScreen
+ isLoading={false}
+ corsEnabled={true}
+ isAllOrigins={false}
+ origins={[]}
+ saveCORS={sinon.stub()}
+ showDeleteDomainConfirmation={sinon.stub()}
+ fetchAndLoadCORSOptions={sinon.stub()}
+ />);
+
assert.notOk(wrapper.find('.loading-lines').exists());
});
});
@@ -98,30 +124,29 @@ describe('CORS Components', () => {
const newOrigin = 'http://new-site.com';
it('calls validates each domain', () => {
- const spyValidateCORSDomain = sinon.spy(Resources, 'validateCORSDomain');
- const addOriginStub = sinon.stub();
- const wrapper = shallow(<Views.OriginInput isVisible={true} addOrigin={addOriginStub}/>);
+ const spyValidateDomain = sinon.spy(Helpers, 'validateDomain');
+ const wrapper = shallow(<Views.OriginInput isVisible={true} addOrigin={sinon.stub()} />);
- wrapper.find('input').simulate('change', {target: {value: newOrigin}});
- wrapper.find('.btn').simulate('click', {preventDefault: sinon.stub()});
- assert.ok(spyValidateCORSDomain.calledWith(newOrigin));
+ wrapper.find('input').simulate('change', { target: { value: newOrigin } });
+ wrapper.find('.btn').simulate('click', { preventDefault: sinon.stub() });
+ assert.ok(spyValidateDomain.called);
});
it('calls addOrigin on add click with valid domain', () => {
const addOriginSpy = sinon.spy();
- const wrapper = shallow(<Views.OriginInput isVisible={true} addOrigin={addOriginSpy}/>);
+ const wrapper = mount(<Views.OriginInput isVisible={true} addOrigin={addOriginSpy} />);
- wrapper.find('input').simulate('change', {target: {value: newOrigin}});
- wrapper.find('.btn').simulate('click', {preventDefault: sinon.stub()});
+ wrapper.find('input').simulate('change', { target: { value: newOrigin } });
+ wrapper.find('.btn').simulate('click', { preventDefault: sinon.stub() });
assert.ok(addOriginSpy.calledWith(newOrigin));
});
it('shows notification if origin is not valid', () => {
const spyAddNotification = sinon.spy(FauxtonAPI, 'addNotification');
- const wrapper = shallow(<Views.OriginInput isVisible={true} addOrigin={sinon.stub()}/>);
+ const wrapper = shallow(<Views.OriginInput isVisible={true} addOrigin={sinon.stub()} />);
- wrapper.find('input').simulate('change', {target: {value: 'badOrigin'}});
- wrapper.find('.btn').simulate('click', {preventDefault: sinon.stub()});
+ wrapper.find('input').simulate('change', { target: { value: 'badOrigin' } });
+ wrapper.find('.btn').simulate('click', { preventDefault: sinon.stub() });
assert.ok(spyAddNotification.calledOnce);
});
});
@@ -134,22 +159,23 @@ describe('CORS Components', () => {
});
it('calls changeOrigin() when you switch from "Select List of Origins" to "Allow All Origins"', () => {
- const wrapper = shallow(<Views.Origins corsEnabled={true} isAllOrigins={false} originChange={spyChangeOrigin}/>);
+ const wrapper = shallow(<Views.Origins corsEnabled={true} isAllOrigins={false} originChange={spyChangeOrigin} />);
- wrapper.find('input[value="all"]').simulate('change', {target: {checked: true, value: 'all'}});
+ wrapper.find('input[value="all"]').simulate('change', { target: { checked: true, value: 'all' } });
assert.ok(spyChangeOrigin.calledWith(true));
});
it('calls changeOrigin() when you switch from "Allow All Origins" to "Select List of Origins"', () => {
- const wrapper = shallow(<Views.Origins corsEnabled={true} isAllOrigins={true} originChange={spyChangeOrigin}/>);
+ const wrapper = shallow(<Views.Origins corsEnabled={true} isAllOrigins={true} originChange={spyChangeOrigin} />);
- wrapper.find('input[value="selected"]').simulate('change', {target: {checked: true, value: 'selected'}});
+ wrapper.find('input[value="selected"]').simulate('change', { target: { checked: true, value: 'selected' } });
assert.ok(spyChangeOrigin.calledWith(false));
});
});
describe('OriginRow', () => {
const spyUpdateOrigin = sinon.spy();
+ const spyDeleteOrigin = sinon.spy();
let origin;
beforeEach(() => {
@@ -158,21 +184,35 @@ describe('CORS Components', () => {
afterEach(() => {
spyUpdateOrigin.reset();
+ spyDeleteOrigin.reset();
});
- it('should show confirm modal on delete', () => {
- const wrapper = mount(<Views.OriginTable updateOrigin={spyUpdateOrigin} isVisible={true} origins={[origin]}/>);
+ it('should call deleteOrigin on delete', () => {
+ const wrapper = mount(<Views.OriginTable
+ updateOrigin={spyUpdateOrigin}
+ deleteOrigin={spyDeleteOrigin}
+ isVisible={true}
+ origins={[origin]} />);
wrapper.find('.fonticon-trash').simulate('click', { preventDefault: sinon.stub() });
- assert.ok(corsStore.isDeleteDomainModalVisible());
+ assert.ok(spyDeleteOrigin.calledOnce);
});
it('does not throw error if origins is undefined', () => {
- mount(<Views.OriginTable updateOrigin={spyUpdateOrigin} isVisible={true} origins={false}/>);
+ mount(<Views.OriginTable
+ updateOrigin={spyUpdateOrigin}
+ deleteOrigin={spyDeleteOrigin}
+ isVisible={true}
+ origins={undefined} />);
});
it('should change origin to input on edit click, then hide input on 2nd click', () => {
- const wrapper = mount(<Views.OriginTable updateOrigin={spyUpdateOrigin} isVisible={true} origins={[origin]}/>);
+ const wrapper = mount(<Views.OriginTable
+ updateOrigin={spyUpdateOrigin}
+ deleteOrigin={spyDeleteOrigin}
+ isVisible={true}
+ origins={[origin]} />);
+
// Text input appears after clicking Edit
wrapper.find('.fonticon-pencil').simulate('click', { preventDefault: sinon.stub() });
assert.ok(wrapper.find('input').exists());
@@ -183,28 +223,32 @@ describe('CORS Components', () => {
});
it('should update origin on update clicked', () => {
- let updatedOrigin = 'https://updated-origin.com';
+ const updatedOrigin = 'https://updated-origin.com';
const wrapper = mount(
<Views.OriginTable
updateOrigin={spyUpdateOrigin}
- isVisible={true} origins={[origin]}/>
+ deleteOrigin={spyDeleteOrigin}
+ isVisible={true}
+ origins={[origin]} />
);
wrapper.find('.fonticon-pencil').simulate('click', { preventDefault: sinon.stub() });
- wrapper.find('input').simulate('change', {target: {value: updatedOrigin}});
+ wrapper.find('input').simulate('change', { target: { value: updatedOrigin } });
wrapper.find('.btn').at(0).simulate('click', { preventDefault: sinon.stub() });
assert.ok(spyUpdateOrigin.calledWith(updatedOrigin));
});
it('should not update origin on update clicked with bad origin', () => {
- let updatedOrigin = 'updated-origin';
+ const updatedOrigin = 'updated-origin';
const wrapper = mount(
<Views.OriginTable
updateOrigin={spyUpdateOrigin}
- isVisible={true} origins={[origin]}/>
+ deleteOrigin={spyDeleteOrigin}
+ isVisible={true}
+ origins={[origin]} />
);
wrapper.find('.fonticon-pencil').simulate('click', { preventDefault: sinon.stub() });
- wrapper.find('input').simulate('change', {target: {value: updatedOrigin}});
+ wrapper.find('input').simulate('change', { target: { value: updatedOrigin } });
wrapper.find('.btn').at(0).simulate('click', { preventDefault: sinon.stub() });
assert.notOk(spyUpdateOrigin.calledWith(updatedOrigin));
});
diff --git a/app/addons/cors/__tests__/helpers.test.js b/app/addons/cors/__tests__/helpers.test.js
new file mode 100644
index 0000000..d728f4d
--- /dev/null
+++ b/app/addons/cors/__tests__/helpers.test.js
@@ -0,0 +1,53 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+import testUtils from "../../../../test/mocha/testUtils";
+import * as Helpers from "../helpers";
+const assert = testUtils.assert;
+
+describe('CORS helper functions', () => {
+
+ it('allows valid domains', () => {
+ const urls = [
+ 'http://something.com',
+ 'https://a.ca',
+ 'https://something.com:8000',
+ 'https://www.some-valid-domain.com:80',
+ 'http://localhost',
+ 'https://localhost',
+ 'http://192.168.1.113',
+ 'http://192.168.1.113:1337'
+ ];
+
+ urls.forEach((url) => {
+ assert.isTrue(Helpers.validateCORSDomain(url));
+ });
+ });
+
+ it('fails on non http/https domains', () => {
+ const urls = [
+ 'whoahnellythisaintright',
+ 'ftp://site.com',
+ 'https://',
+ 'http://'
+ ];
+ _.each(urls, function (url) {
+ assert.isFalse(Helpers.validateCORSDomain(url));
+ });
+ });
+
+ it('normalizes common cases, like accidentally added subfolders', () => {
+ assert.equal('https://foo.com', Helpers.normalizeUrls('https://foo.com/blerg'));
+ assert.equal('https://192.168.1.113', Helpers.normalizeUrls('https://192.168.1.113/blerg'));
+ assert.equal('https://foo.com:1337', Helpers.normalizeUrls('https://foo.com:1337/blerg'));
+ assert.equal('https://foo.com', Helpers.normalizeUrls('https://foo.com'));
+ });
+});
diff --git a/app/addons/cors/__tests__/resources.test.js b/app/addons/cors/__tests__/resources.test.js
deleted file mode 100644
index 322b168..0000000
--- a/app/addons/cors/__tests__/resources.test.js
+++ /dev/null
@@ -1,74 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-import testUtils from "../../../../test/mocha/testUtils";
-import CORS from "../resources";
-const assert = testUtils.assert;
-
-describe('Cors Config Model', () => {
- let cors;
-
- beforeEach(() => {
- cors = new CORS.Config(null, { node: 'node2@127.0.0.1' });
- });
-
- it('Splits up origins into array', () => {
- const origins = ['http://hello.com', 'http://another.co.a'];
- cors.set(cors.parse({ origins: origins.join(',') }));
- assert.deepEqual(cors.get('origins'), origins);
- });
-
- it('returns empty array for undefined', () => {
- const origins = { origins: undefined };
- cors.set(cors.parse(origins));
- assert.deepEqual(cors.get('origins'), []);
- });
-
- it('does not return an empty string (empty origin), when "specific origins" is set, but there are no domains on that list', () => {
- const emptyOrigins = { origins: '' };
- cors.set(cors.parse(emptyOrigins));
- assert.deepEqual(cors.get('origins'), []);
- });
-
- it('allows valid domains', () => {
- const urls = [
- 'http://something.com',
- 'https://a.ca',
- 'https://something.com:8000',
- 'https://www.some-valid-domain.com:80',
- 'http://localhost',
- 'https://localhost',
- 'http://192.168.1.113',
- 'http://192.168.1.113:1337'
- ];
- _.each(urls, function (url) {
- assert.isTrue(CORS.validateCORSDomain(url));
- });
- });
-
- it('fails on non http/https domains', () => {
- const urls = [
- 'whoahnellythisaintright',
- 'ftp://site.com'
- ];
- _.each(urls, function (url) {
- assert.isFalse(CORS.validateCORSDomain(url));
- });
- });
-
- it('normalizes common cases, like accidentally added subfolders', () => {
- assert.equal('https://foo.com', CORS.normalizeUrls('https://foo.com/blerg'));
- assert.equal('https://192.168.1.113', CORS.normalizeUrls('https://192.168.1.113/blerg'));
- assert.equal('https://foo.com:1337', CORS.normalizeUrls('https://foo.com:1337/blerg'));
- assert.equal('https://foo.com', CORS.normalizeUrls('https://foo.com'));
- });
-
-});
diff --git a/app/addons/cors/__tests__/stores.test.js b/app/addons/cors/__tests__/stores.test.js
deleted file mode 100644
index e9be117..0000000
--- a/app/addons/cors/__tests__/stores.test.js
+++ /dev/null
@@ -1,100 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-import testUtils from "../../../../test/mocha/testUtils";
-import Stores from "../stores";
-const assert = testUtils.assert;
-const store = Stores.corsStore;
-
-describe('CORS store', () => {
-
- describe('isAllOrigins', () => {
-
- it('returns true for all origins', () => {
- store._origins = ['*'];
-
- assert.ok(store.isAllOrigins());
- });
-
- it('returns false for specific origins', () => {
- store._origins = ['https://hello.com', 'http://another.com'];
-
- assert.notOk(store.isAllOrigins());
- });
-
- it('returns false for empty array', () => {
- store._origins = [];
-
- assert.notOk(store.isAllOrigins());
- });
- });
-
- describe('addOrigin', () => {
-
- it('adds Origin to list', () => {
- const origin = 'http://hello.com';
- store._origins = [];
- store.addOrigin(origin);
-
- assert.ok(_.include(store.getOrigins(), origin));
- });
-
- });
-
- describe('originChange', () => {
-
- it('sets origins to * for true', () => {
- store.originChange(true);
-
- assert.deepEqual(store.getOrigins(), ['*']);
- });
-
- it('sets origins to [] for true', () => {
- store.originChange(false);
-
- assert.deepEqual(store.getOrigins(), []);
- });
-
- });
-
- describe('deleteOrigin', () => {
-
- it('removes origin', () => {
- store._origins = ['http://first.com', 'http://hello.com', 'http://second.com'];
- store.deleteOrigin('http://hello.com');
-
- assert.deepEqual(store.getOrigins(), ['http://first.com', 'http://second.com']);
-
- });
-
- });
-
- describe('update origin', () => {
-
- it('removes old origin', () => {
- store._origins = ['http://first.com', 'http://hello.com', 'http://second.com'];
- store.updateOrigin('http://hello123.com', 'http://hello.com');
-
- assert.notOk(_.include(store.getOrigins(), 'http://hello.com'));
-
- });
-
- it('adds new origin', () => {
- store._origins = ['http://first.com', 'http://hello.com', 'http://second.com'];
- store.updateOrigin('http://hello123.com', 'http://hello.com');
-
- assert.ok(_.include(store.getOrigins(), 'http://hello123.com'));
-
- });
-
- });
-
-});
diff --git a/app/addons/cors/actions.js b/app/addons/cors/actions.js
index 039cb67..3ba2e08 100644
--- a/app/addons/cors/actions.js
+++ b/app/addons/cors/actions.js
@@ -9,186 +9,102 @@
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
+
import FauxtonAPI from "../../core/api";
import ActionTypes from "./actiontypes";
-import Resources from "./resources";
-
-export default {
- fetchAndEditCors: function (node) {
- var cors = new Resources.Config({node: node});
- var httpd = new Resources.Httpd({node: node});
-
- FauxtonAPI.when([cors.fetch(), httpd.fetch()]).then(function () {
- this.editCors({
- origins: cors.get('origins'),
- isEnabled: httpd.corsEnabled(),
- node: node
- });
- }.bind(this));
- },
+import * as CorsAPI from "./api";
- editCors: function (options) {
- FauxtonAPI.dispatch({
- type: ActionTypes.EDIT_CORS,
- options: options
- });
- },
-
- toggleEnableCors: function () {
- FauxtonAPI.dispatch({
- type: ActionTypes.TOGGLE_ENABLE_CORS
- });
- },
-
- addOrigin: function (origin) {
- FauxtonAPI.dispatch({
- type: ActionTypes.CORS_ADD_ORIGIN,
- origin: origin
- });
- },
-
- originChange: function (isAllOrigins) {
- FauxtonAPI.dispatch({
- type: ActionTypes.CORS_IS_ALL_ORIGINS,
- isAllOrigins: isAllOrigins
- });
- },
-
- deleteOrigin: function (origin) {
- FauxtonAPI.dispatch({
- type: ActionTypes.CORS_DELETE_ORIGIN,
- origin: origin
- });
- },
+export const fetchAndLoadCORSOptions = (url, node) => (dispatch) => {
+ const fetchCors = CorsAPI.fetchCORSConfig(url);
+ const fetchHttp = CorsAPI.fetchHttpdConfig(url);
- updateOrigin: function (updatedOrigin, originalOrigin) {
- FauxtonAPI.dispatch({
- type: ActionTypes.CORS_UPDATE_ORIGIN,
- updatedOrigin: updatedOrigin,
- originalOrigin: originalOrigin
- });
- },
-
- methodChange: function (httpMethod) {
- FauxtonAPI.dispatch({
- type: ActionTypes.CORS_METHOD_CHANGE,
- httpMethod: httpMethod
- });
- },
-
- saveEnableCorsToHttpd: function (enableCors, node) {
- var enableOption = new Resources.ConfigModel({
- section: 'httpd',
- attribute: 'enable_cors',
- value: enableCors.toString(),
- node: node
- });
-
- return enableOption.save();
- },
-
- saveCorsOrigins: function (origins, node) {
- var allowOrigins = new Resources.ConfigModel({
- section: 'cors',
- attribute: 'origins',
- value: origins,
+ FauxtonAPI.Promise.join(fetchCors, fetchHttp, (corsConfig, httpdConfig) => {
+ const loadOptions = loadCORSOptions({
+ origins: corsConfig.origins,
+ corsEnabled: httpdConfig.enable_cors === 'true',
node: node
});
-
- return allowOrigins.save();
- },
-
- saveCorsCredentials: function (node) {
- var allowCredentials = new Resources.ConfigModel({
- section: 'cors',
- attribute: 'credentials',
- value: 'true',
- node: node
+ dispatch(loadOptions);
+ }).catch((error) => {
+ FauxtonAPI.addNotification({
+ msg: 'Could not load CORS settings. ' + errorReason(error),
+ type: 'error'
});
+ });
+};
- return allowCredentials.save();
- },
+export const showLoadingBars = () => {
+ return {
+ type: ActionTypes.CORS_SET_IS_LOADING,
+ isLoading: true
+ };
+};
- saveCorsHeaders: function (node) {
- var corsHeaders = new Resources.ConfigModel({
- section: 'cors',
- attribute: 'headers',
- value: 'accept, authorization, content-type, origin, referer',
- node: node
- });
+export const hideLoadingBars = () => {
+ return {
+ type: ActionTypes.CORS_SET_IS_LOADING,
+ isLoading: false
+ };
+};
- return corsHeaders.save();
- },
+export const loadCORSOptions = (options) => {
+ return {
+ type: ActionTypes.EDIT_CORS,
+ options: options,
+ isLoading: false
+ };
+};
- saveCorsMethods: function (node) {
- var corsMethods = new Resources.ConfigModel({
- section: 'cors',
- attribute: 'methods',
- value: 'GET, PUT, POST, HEAD, DELETE',
- node: node
- });
+export const showDomainDeleteConfirmation = (domain) => {
+ return {
+ type: ActionTypes.CORS_SHOW_DELETE_DOMAIN_MODAL,
+ domainToDelete: domain
+ };
+};
- return corsMethods.save();
- },
+export const hideDomainDeleteConfirmation = () => {
+ return {
+ type: ActionTypes.CORS_HIDE_DELETE_DOMAIN_MODAL
+ };
+};
- sanitizeOrigins: function (origins) {
- if (_.isEmpty(origins)) {
- return '';
- }
+export const saveCors = (url, options) => (dispatch) => {
+ const promises = [];
- return origins.join(',');
- },
+ promises.push(CorsAPI.updateEnableCorsToHttpd(url, options.node, options.corsEnabled));
+ if (options.corsEnabled) {
+ promises.push(CorsAPI.updateCorsOrigins(url, options.node, sanitizeOrigins(options.origins)));
+ promises.push(CorsAPI.updateCorsCredentials(url, options.node));
+ promises.push(CorsAPI.updateCorsHeaders(url, options.node));
+ promises.push(CorsAPI.updateCorsMethods(url, options.node));
+ }
- toggleLoadingBarsToEnabled: function (state) {
- FauxtonAPI.dispatch({
- type: ActionTypes.CORS_SET_IS_LOADING,
- isLoading: state
+ return FauxtonAPI.Promise.all(promises).then(() => {
+ FauxtonAPI.addNotification({
+ msg: 'CORS settings updated.',
+ type: 'success',
+ clear: true
});
- },
-
- saveCors: function (options) {
- this.toggleLoadingBarsToEnabled(true);
-
- var promises = [];
- promises.push(this.saveEnableCorsToHttpd(options.enableCors, options.node));
-
- if (options.enableCors) {
- promises.push(this.saveCorsOrigins(this.sanitizeOrigins(options.origins), options.node));
- promises.push(this.saveCorsCredentials(options.node));
- promises.push(this.saveCorsHeaders(options.node));
- promises.push(this.saveCorsMethods(options.node));
- }
-
- FauxtonAPI.when(promises).then(function () {
- FauxtonAPI.addNotification({
- msg: 'Cors settings updated.',
- type: 'success',
- clear: true
- });
-
- this.hideDeleteDomainModal(); // just in case it was already open
- this.toggleLoadingBarsToEnabled(false);
-
- }.bind(this), function () {
+ dispatch(loadCORSOptions(options));
+ }).catch((error) => {
FauxtonAPI.addNotification({
- msg: 'Error! Could not save your CORS settings. Please try again.',
+ msg: 'Error! Could not save your CORS settings. Please try again. ' + errorReason(error),
type: 'error',
clear: true
});
- this.toggleLoadingBarsToEnabled(false);
- }.bind(this));
- },
-
- showDeleteDomainModal: function (domain) {
- FauxtonAPI.dispatch({
- type: ActionTypes.CORS_SHOW_DELETE_DOMAIN_MODAL,
- options: {
- domain: domain
- }
+ dispatch(hideDomainDeleteConfirmation());
+ dispatch(hideLoadingBars());
});
- },
+};
- hideDeleteDomainModal: function () {
- FauxtonAPI.dispatch({ type: ActionTypes.CORS_HIDE_DELETE_DOMAIN_MODAL });
+const errorReason = (error) => {
+ return 'Reason: ' + ((error && error.message) || 'n/a');
+};
+
+export const sanitizeOrigins = (origins) => {
+ if (_.isEmpty(origins)) {
+ return '';
}
+
+ return origins.join(',');
};
diff --git a/app/addons/cors/actiontypes.js b/app/addons/cors/actiontypes.js
index 81ebd7f..63a58e4 100644
--- a/app/addons/cors/actiontypes.js
+++ b/app/addons/cors/actiontypes.js
@@ -9,14 +9,9 @@
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
+
export default {
- TOGGLE_ENABLE_CORS: 'TOGGLE_ENABLE_CORS',
EDIT_CORS: 'EDIT_CORS',
- CORS_ADD_ORIGIN: 'CORS_ADD_ORIGIN',
- CORS_IS_ALL_ORIGINS: 'CORS_IS_ALL_ORIGINS',
- CORS_DELETE_ORIGIN: 'CORS_DELETE_ORIGIN',
- CORS_UPDATE_ORIGIN: 'CORS_UPDATE_ORIGIN',
- CORS_METHOD_CHANGE: 'CORS_METHOD_CHANGE',
CORS_SET_IS_LOADING: 'CORS_SET_IS_LOADING',
CORS_SHOW_DELETE_DOMAIN_MODAL: 'CORS_SHOW_DELETE_DOMAIN_MODAL',
CORS_HIDE_DELETE_DOMAIN_MODAL: 'CORS_HIDE_DELETE_DOMAIN_MODAL'
diff --git a/app/addons/cors/api.js b/app/addons/cors/api.js
new file mode 100644
index 0000000..82dda38
--- /dev/null
+++ b/app/addons/cors/api.js
@@ -0,0 +1,120 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+import 'whatwg-fetch';
+
+export const fetchCORSConfig = (baseURL) => {
+ const configURL = baseURL + '/cors';
+ return fetch(configURL, {
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ credentials: 'include',
+ method: 'GET'
+ })
+ .then((res) => res.json())
+ .then((json) => {
+ if (json.error) {
+ throw new Error(json.reason);
+ }
+
+ const origins = !json.origins ? [] : json.origins.split(',');
+ return {
+ origins: origins,
+ methods: json.methods,
+ credentials: json.credentials,
+ headers: json.headers
+ };
+ });
+};
+
+export const fetchHttpdConfig = (baseURL) => {
+ const configURL = baseURL + '/httpd';
+ return fetch(configURL, {
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ credentials: 'include',
+ method: 'GET'
+ })
+ .then((res) => res.json())
+ .then((json) => {
+ if (json.error) {
+ throw new Error(json.reason);
+ }
+ return json;
+ });
+};
+
+export const updateEnableCorsToHttpd = (baseURL, node, enableCors) => {
+ if (!node) {
+ throw new Error('node not set');
+ }
+ const configURL = baseURL + '/httpd/enable_cors';
+ return fetch(configURL, {
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ credentials: 'include',
+ method: 'PUT',
+ body: JSON.stringify(enableCors.toString())
+ })
+ .then((res) => res.json())
+ .then((json) => {
+ if (json.error) {
+ throw new Error(json.reason);
+ }
+ return json;
+ });
+};
+
+export const updateCorsOrigins = (baseURL, node, origins) => {
+ return updateCorsProperty(baseURL, node, 'origins', origins);
+};
+
+export const updateCorsCredentials = (baseURL, node) => {
+ return updateCorsProperty(baseURL, node, 'credentials', 'true');
+};
+
+export const updateCorsHeaders = (baseURL, node) => {
+ return updateCorsProperty(baseURL, node, 'headers', 'accept, authorization, content-type, origin, referer');
+};
+
+export const updateCorsMethods = (baseURL, node) => {
+ return updateCorsProperty(baseURL, node, 'methods', 'GET, PUT, POST, HEAD, DELETE');
+};
+
+const updateCorsProperty = (baseURL, node, propName, propValue) => {
+ if (!node) {
+ throw new Error('node not set');
+ }
+ const configURL = baseURL + '/cors/' + propName;
+ return fetch(configURL, {
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ credentials: 'include',
+ method: 'PUT',
+ body: JSON.stringify(propValue)
+ })
+ .then((res) => res.json())
+ .then((json) => {
+ if (json.error) {
+ throw new Error(json.reason);
+ }
+ return json;
+ });
+};
diff --git a/app/addons/cors/base.js b/app/addons/cors/base.js
index 6f8d400..f9c475f 100644
--- a/app/addons/cors/base.js
+++ b/app/addons/cors/base.js
@@ -12,8 +12,14 @@
import FauxtonAPI from "../../core/api";
import "./assets/less/cors.less";
-var CORS = FauxtonAPI.addon();
+import reducers from "./reducers";
+
+const CORS = FauxtonAPI.addon();
CORS.initialize = function () {};
+FauxtonAPI.addReducers({
+ cors: reducers
+});
+
export default CORS;
diff --git a/app/addons/cors/components.js b/app/addons/cors/components.js
index a986df4..71d0bbc 100644
--- a/app/addons/cors/components.js
+++ b/app/addons/cors/components.js
@@ -10,358 +10,28 @@
// License for the specific language governing permissions and limitations under
// the License.
-import app from "../../app";
-import FauxtonAPI from "../../core/api";
-import React from "react";
-import Stores from "./stores";
-import Resources from "./resources";
-import Actions from "./actions";
-import ReactComponents from "../components/react-components";
-import FauxtonComponents from "../fauxton/components";
-var LoadLines = ReactComponents.LoadLines;
-var ConfirmationModal = FauxtonComponents.ConfirmationModal;
+import CORSContainer from "./components/CORSContainer";
+import CORSScreen from "./components/CORSScreen";
+import OriginInput from "./components/OriginInput";
+import Origins from "./components/Origins";
+import OriginTable from "./components/OriginTable";
+import OriginRow from "./components/OriginRow";
-var corsStore = Stores.corsStore;
-
-
-var validateOrigin = function (origin) {
- if (!Resources.validateCORSDomain(origin)) {
- FauxtonAPI.addNotification({
- msg: 'Please enter a valid domain, starting with http/https.',
- type: 'error',
- clear: true
- });
-
- return false;
- }
-
- return true;
+export default {
+ CORSContainer,
+ CORSScreen,
+ OriginInput,
+ Origins,
+ OriginTable,
+ OriginRow
};
-var OriginRow = React.createClass({
-
- getInitialState: function () {
- return {
- edit: false,
- updatedOrigin: this.props.origin
- };
- },
-
- editOrigin: function (e) {
- e.preventDefault();
- this.setState({ edit: !this.state.edit });
- },
-
- updateOrigin: function (e) {
- e.preventDefault();
- if (!validateOrigin(this.state.updatedOrigin)) {
- return;
- }
- this.props.updateOrigin(this.state.updatedOrigin, this.props.origin);
- this.setState({ edit: false });
- },
-
- deleteOrigin: function (e) {
- e.preventDefault();
- Actions.showDeleteDomainModal(this.props.origin);
- },
-
- onInputChange: function (event) {
- this.setState({ updatedOrigin: event.target.value });
- },
-
- onKeyUp: function (e) {
- if (e.keyCode === 13) { //enter key
- return this.updateOrigin(e);
- }
- },
-
- createOriginDisplay: function () {
- if (this.state.edit) {
- return (
- <div className="input-append edit-domain-section">
- <input type="text" name="update_origin_domain" onChange={this.onInputChange} onKeyUp={this.onKeyUp} value={this.state.updatedOrigin} />
- <button onClick={this.updateOrigin} className="btn btn-primary update-origin"> Update </button>
- </div>
- );
- }
- return <div className="js-url url-display">{this.props.origin}</div>;
- },
-
- render: function () {
- var display = this.createOriginDisplay();
- return (
- <tr>
- <td>
- {display}
- </td>
- <td width="30">
- <span>
- <a className="fonticon-pencil" onClick={this.editOrigin} title="Edit domain." />
- </span>
- </td>
- <td width="30">
- <span>
- <a href="#" data-bypass="true" className="fonticon-trash" onClick={this.deleteOrigin} title="Delete domain." />
- </span>
- </td>
- </tr>
- );
- }
-
-});
-
-var OriginTable = React.createClass({
-
- createRows: function () {
- return _.map(this.props.origins, function (origin, i) {
- return <OriginRow
- updateOrigin={this.props.updateOrigin}
- key={i} origin={origin} />;
- }, this);
- },
-
- render: function () {
- if (!this.props.origins) {
- return null;
- }
-
- if (!this.props.isVisible || this.props.origins.length === 0) {
- return null;
- }
-
- var origins = this.createRows();
-
- return (
- <table id="origin-domain-table" className="table table-striped">
- <tbody>
- {origins}
- </tbody>
- </table>
- );
- }
-
-});
-
-var OriginInput = React.createClass({
- getInitialState: function () {
- return {
- origin: ''
- };
- },
-
- onInputChange: function (e) {
- this.setState({origin: e.target.value});
- },
-
- addOrigin: function (event) {
- event.preventDefault();
- if (!validateOrigin(this.state.origin)) {
- return;
- }
-
- var url = Resources.normalizeUrls(this.state.origin);
-
- this.props.addOrigin(url);
- this.setState({origin: ''});
- },
-
- onKeyUp: function (e) {
- if (e.keyCode == 13) { //enter key
- return this.addOrigin(e);
- }
- },
- render: function () {
- if (!this.props.isVisible) {
- return null;
- }
- return (
- <div id="origin-domains-container">
- <div className="origin-domains">
- <div className="input-append">
- <input type="text" name="new_origin_domain" onChange={this.onInputChange} onKeyUp={this.onKeyUp} value={this.state.origin} placeholder="https://example.com"/>
- <button onClick={this.addOrigin} className="btn btn-secondary add-domain"><i className="icon fonticon-ok-circled"></i> Add Domain</button>
- </div>
- </div>
- </div>
- );
- }
-});
-var Origins = React.createClass({
- onOriginChange: function (event) {
- if (event.target.value === 'all' && this.props.isAllOrigins) {
- return; // do nothing if all origins is already selected
- }
- if (event.target.value === 'selected' && !this.props.isAllOrigins) {
- return; // do nothing if specific origins is already selected
- }
- this.props.originChange(event.target.value === 'all');
- },
- render: function () {
- if (!this.props.corsEnabled) {
- return null;
- }
-
- return (
- <div>
- <p><strong> Origin Domains </strong> </p>
- <p>Databases will accept requests from these domains: </p>
- <label className="radio">
- <input type="radio" checked={this.props.isAllOrigins} value="all" onChange={this.onOriginChange} name="all-domains"/> All domains ( * )
- </label>
- <label className="radio">
- <input type="radio" checked={!this.props.isAllOrigins} value="selected" onChange={this.onOriginChange} name="selected-domains"/> Restrict to specific domains
- </label>
- </div>
- );
- }
-});
-
-var CORSController = React.createClass({
-
- getStoreState: function () {
- return {
- corsEnabled: corsStore.isEnabled(),
- origins: corsStore.getOrigins(),
- isAllOrigins: corsStore.isAllOrigins(),
- configChanged: corsStore.hasConfigChanged(),
- shouldSaveChange: corsStore.shouldSaveChange(),
- node: corsStore.getNode(),
- isLoading: corsStore.getIsLoading(),
- deleteDomainModalVisible: corsStore.isDeleteDomainModalVisible(),
- domainToDelete: corsStore.getDomainToDelete()
- };
- },
-
- getInitialState: function () {
- return this.getStoreState();
- },
-
- componentDidMount: function () {
- corsStore.on('change', this.onChange, this);
- },
-
- componentWillUnmount: function () {
- corsStore.off('change', this.onChange);
- },
-
- componentDidUpdate: function () {
- if (this.state.shouldSaveChange) {
- this.save();
- }
- },
-
- onChange: function () {
- this.setState(this.getStoreState());
- },
-
- enableCorsChange: function () {
- if (this.state.corsEnabled && !_.isEmpty(this.state.origins)) {
- var result = window.confirm(app.i18n.en_US['cors-disable-cors-prompt']);
- if (!result) { return; }
- }
-
- Actions.toggleEnableCors();
- },
-
- save: function () {
- Actions.saveCors({
- enableCors: this.state.corsEnabled,
- origins: this.state.origins,
- node: this.state.node
- });
- },
-
- originChange: function (isAllOrigins) {
- if (isAllOrigins && !_.isEmpty(this.state.origins)) {
- var result = window.confirm('Are you sure? Switching to all origin domains will overwrite your specific origin domains.');
- if (!result) { return; }
- }
-
- Actions.originChange(isAllOrigins);
- },
-
- addOrigin: function (origin) {
- Actions.addOrigin(origin);
- },
-
- updateOrigin: function (updatedOrigin, originalOrigin) {
- Actions.updateOrigin(updatedOrigin, originalOrigin);
- },
-
- methodChange: function (httpMethod) {
- Actions.methodChange(httpMethod);
- },
-
- deleteOrigin: function () {
- Actions.deleteOrigin(this.state.domainToDelete);
- },
-
- render: function () {
- var isVisible = _.all([this.state.corsEnabled, !this.state.isAllOrigins]);
-
- var originSettings = (
- <div id={this.state.corsEnabled ? 'collapsing-container' : ''}>
- <Origins corsEnabled={this.state.corsEnabled} originChange={this.originChange} isAllOrigins={this.state.isAllOrigins}/>
- <OriginTable updateOrigin={this.updateOrigin} isVisible={isVisible} origins={this.state.origins} />
- <OriginInput addOrigin={this.addOrigin} isVisible={isVisible} />
- </div>
- );
-
- if (this.state.isLoading) {
- originSettings = (<LoadLines />);
- }
- var deleteMsg = <span>Are you sure you want to delete <code>{_.escape(this.state.domainToDelete)}</code>?</span>;
-
- return (
- <div className="cors-page flex-body">
- <header id="cors-header">
- <p>{app.i18n.en_US['cors-notice']}</p>
- </header>
-
- <form id="corsForm" onSubmit={this.save}>
- <div className="cors-enable">
- {this.state.corsEnabled ? 'Cors is currently enabled.' : 'Cors is currently disabled.'}
- <br />
- <button
- type="button"
- className="enable-disable btn btn-secondary"
- onClick={this.enableCorsChange}
- disabled={this.state.isLoading ? 'disabled' : null}
- >
- {this.state.corsEnabled ? 'Disable CORS' : 'Enable CORS'}
- </button>
- </div>
- {originSettings}
- </form>
-
- <ConfirmationModal
- title="Confirm Deletion"
- visible={this.state.deleteDomainModalVisible}
- text={deleteMsg}
- buttonClass="btn-danger"
- onClose={Actions.hideDeleteDomainModal}
- onSubmit={this.deleteOrigin}
- successButtonLabel="Delete Domain" />
- </div>
- );
- }
-});
-
-
-export default {
- CORSController: CORSController,
- OriginInput: OriginInput,
- Origins: Origins,
- OriginTable: OriginTable,
- OriginRow: OriginRow
-};
diff --git a/app/addons/cors/components/CORSContainer.js b/app/addons/cors/components/CORSContainer.js
new file mode 100644
index 0000000..eece5ee
--- /dev/null
+++ b/app/addons/cors/components/CORSContainer.js
@@ -0,0 +1,46 @@
+import { connect } from 'react-redux';
+import CORSScreen from './CORSScreen';
+import {
+ saveCors, showLoadingBars, fetchAndLoadCORSOptions,
+ showDomainDeleteConfirmation, hideDomainDeleteConfirmation
+} from '../actions';
+
+const mapStateToProps = ({ cors }) => {
+ return {
+ node: cors.node,
+ corsEnabled: cors.corsEnabled,
+ isAllOrigins: cors.isAllOrigins,
+ isLoading: cors.isLoading,
+ origins: cors.origins,
+ deleteDomainModalVisible: cors.deleteDomainModalVisible,
+ domainToDelete: cors.domainToDelete
+ };
+};
+
+const mapDispatchToProps = (dispatch, ownProps) => {
+ return {
+ saveCORS: (options) => {
+ dispatch(showLoadingBars());
+ dispatch(saveCors(ownProps.url, options));
+ },
+
+ fetchAndLoadCORSOptions: () => {
+ dispatch(fetchAndLoadCORSOptions(ownProps.url, ownProps.node));
+ },
+
+ showDeleteDomainConfirmation: (domain) => {
+ dispatch(showDomainDeleteConfirmation(domain));
+ },
+
+ hideDeleteDomainConfirmation: () => {
+ dispatch(hideDomainDeleteConfirmation());
+ }
+ };
+};
+
+const CORSContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(CORSScreen);
+
+export default CORSContainer;
diff --git a/app/addons/cors/components/CORSScreen.js b/app/addons/cors/components/CORSScreen.js
new file mode 100644
index 0000000..e5d76b8
--- /dev/null
+++ b/app/addons/cors/components/CORSScreen.js
@@ -0,0 +1,150 @@
+import React, { Component } from "react";
+import app from "../../../app";
+import ReactComponents from "../../components/react-components";
+import FauxtonComponents from "../../fauxton/components";
+import Origins from "./Origins";
+import OriginInput from "./OriginInput";
+import OriginTable from "./OriginTable";
+
+const LoadLines = ReactComponents.LoadLines;
+const ConfirmationModal = FauxtonComponents.ConfirmationModal;
+
+export default class CORSScreen extends Component {
+
+ constructor(props) {
+ super(props);
+ }
+
+ componentDidMount() {
+ this.props.fetchAndLoadCORSOptions();
+ }
+
+ enableCorsChange() {
+ const { corsEnabled, origins, node } = this.props;
+ if (corsEnabled && !_.isEmpty(origins)) {
+ const result = window.confirm(app.i18n.en_US['cors-disable-cors-prompt']);
+ if (!result) { return; }
+ }
+ this.props.saveCORS({
+ corsEnabled: !corsEnabled,
+ origins: origins,
+ node: node
+ });
+ }
+
+ save() {
+ this.props.saveCORS({
+ corsEnabled: this.props.corsEnabled,
+ origins: this.props.origins,
+ node: this.props.node
+ });
+ }
+
+ originChange(isAllOrigins) {
+ if (isAllOrigins && !_.isEmpty(this.props.origins)) {
+ const result = window.confirm('Are you sure? Switching to all origin domains will overwrite your specific origin domains.');
+ if (!result) { return; }
+ }
+ this.props.saveCORS({
+ corsEnabled: this.props.corsEnabled,
+ origins: isAllOrigins ? ['*'] : [],
+ node: this.props.node
+ });
+ }
+
+ addOrigin(origin) {
+ this.props.saveCORS({
+ corsEnabled: this.props.corsEnabled,
+ origins: this.props.origins.concat(origin),
+ node: this.props.node
+ });
+ }
+
+ updateOrigin(updatedOrigin, originalOrigin) {
+ const newOrigins = this.props.origins.slice();
+ const index = _.indexOf(newOrigins, originalOrigin);
+ if (index === -1) { return; }
+ newOrigins[index] = updatedOrigin;
+
+ this.props.saveCORS({
+ corsEnabled: this.props.corsEnabled,
+ origins: newOrigins,
+ node: this.props.node
+ });
+ }
+
+ deleteOrigin() {
+ const { corsEnabled, origins, node, domainToDelete } = this.props;
+ const index = this.props.origins.indexOf(domainToDelete);
+ if (index === -1) { return; }
+ const newOrigins = [
+ ...origins.slice(0, index),
+ ...origins.slice(index + 1)
+ ];
+
+ this.props.saveCORS({
+ corsEnabled: corsEnabled,
+ origins: newOrigins,
+ node: node
+ });
+ }
+
+ render() {
+ const isVisible = [this.props.corsEnabled, !this.props.isAllOrigins].every((elem) => elem === true);
+
+ let originSettings = (
+ <div id={this.props.corsEnabled ? 'collapsing-container' : ''}>
+ <Origins
+ corsEnabled={this.props.corsEnabled}
+ originChange={this.originChange.bind(this)}
+ isAllOrigins={this.props.isAllOrigins} />
+ <OriginTable
+ updateOrigin={this.updateOrigin.bind(this)}
+ deleteOrigin={this.props.showDeleteDomainConfirmation}
+ isVisible={isVisible}
+ origins={this.props.origins} />
+ <OriginInput
+ addOrigin={this.addOrigin.bind(this)}
+ isVisible={isVisible} />
+ </div>
+ );
+
+ if (this.props.isLoading) {
+ originSettings = (<LoadLines />);
+ }
+ const deleteMsg = <span>Are you sure you want to delete <code>{_.escape(this.props.domainToDelete)}</code>?</span>;
+
+ return (
+ <div className="cors-page flex-body">
+ <header id="cors-header">
+ <p>{app.i18n.en_US['cors-notice']}</p>
+ </header>
+
+ <form id="corsForm" onSubmit={this.save.bind(this)}>
+ <div className="cors-enable">
+ {this.props.corsEnabled ? 'CORS is currently enabled.' : 'CORS is currently disabled.'}
+ <br />
+ <button
+ type="button"
+ className="enable-disable btn btn-secondary"
+ onClick={this.enableCorsChange.bind(this)}
+ disabled={this.props.isLoading ? 'disabled' : null}
+ >
+ {this.props.corsEnabled ? 'Disable CORS' : 'Enable CORS'}
+ </button>
+ </div>
+ {originSettings}
+ </form>
+
+ <ConfirmationModal
+ title="Confirm Deletion"
+ visible={this.props.deleteDomainModalVisible}
+ text={deleteMsg}
+ buttonClass="btn-danger"
+ onClose={this.props.hideDeleteDomainConfirmation}
+ onSubmit={this.deleteOrigin.bind(this)}
+ successButtonLabel="Delete Domain" />
+ </div>
+ );
+ }
+}
diff --git a/app/addons/cors/components/OriginInput.js b/app/addons/cors/components/OriginInput.js
new file mode 100644
index 0000000..bcd8720
--- /dev/null
+++ b/app/addons/cors/components/OriginInput.js
@@ -0,0 +1,60 @@
+import React, { Component } from "react";
+import { validateDomain, normalizeUrls } from "../helpers";
+
+export default class OriginInput extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ origin: ''
+ };
+ }
+
+ onInputChange(e) {
+ this.setState({ origin: e.target.value });
+ }
+
+ addOrigin(event) {
+ event.preventDefault();
+ if (!validateDomain(this.state.origin)) {
+ return false;
+ }
+
+ const url = normalizeUrls(this.state.origin);
+
+ this.props.addOrigin(url);
+ this.setState({ origin: '' });
+ }
+
+ onKeyUp(e) {
+ if (e.keyCode == 13) { //enter key
+ return this.addOrigin(e);
+ }
+ }
+
+ render() {
+ if (!this.props.isVisible) {
+ return null;
+ }
+
+ return (
+ <div id="origin-domains-container">
+ <div className="origin-domains">
+ <div className="input-append">
+ <input type="text" name="new_origin_domain" placeholder="https://example.com"
+ onChange={this.onInputChange.bind(this)} value={this.state.origin} />
+ <button onClick={this.addOrigin.bind(this)} className="btn btn-secondary add-domain">
+ <i className="icon fonticon-ok-circled"></i> Add Domain
+ </button>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+};
+
+OriginInput.propTypes = {
+ isVisible: React.PropTypes.bool.isRequired,
+ addOrigin: React.PropTypes.func.isRequired
+};
diff --git a/app/addons/cors/components/OriginRow.js b/app/addons/cors/components/OriginRow.js
new file mode 100644
index 0000000..cbc73eb
--- /dev/null
+++ b/app/addons/cors/components/OriginRow.js
@@ -0,0 +1,86 @@
+import React, { Component } from "react";
+import { validateDomain } from "../helpers";
+
+
+export default class OriginRow extends Component {
+
+ constructor (props) {
+ super(props);
+ this.state = {
+ edit: false,
+ updatedOrigin: this.props.origin
+ };
+ }
+
+ editOrigin(e) {
+ e.preventDefault();
+ this.setState({ edit: !this.state.edit });
+ }
+
+ updateOrigin (e) {
+ e.preventDefault();
+ if (!validateDomain(this.state.updatedOrigin)) {
+ return;
+ }
+
+ if (this.state.updatedOrigin && this.props.origin !== this.state.updatedOrigin) {
+ this.props.updateOrigin(this.state.updatedOrigin, this.props.origin);
+ }
+ this.setState({ edit: false });
+ }
+
+ deleteOrigin (e) {
+ e.preventDefault();
+ this.props.deleteOrigin(this.props.origin);
+ }
+
+ onInputChange (event) {
+ this.setState({ updatedOrigin: event.target.value });
+ }
+
+ onKeyUp (e) {
+ if (e.keyCode === 13) { //enter key
+ return this.updateOrigin(e);
+ }
+ }
+
+ createOriginDisplay () {
+ if (this.state.edit) {
+ return (
+ <div className="input-append edit-domain-section">
+ <input type="text" name="update_origin_domain" onChange={ this.onInputChange.bind(this) } onKeyUp={ this.onKeyUp.bind(this) } value={this.state.updatedOrigin} />
+ <button onClick={ this.updateOrigin.bind(this) } className="btn btn-primary update-origin"> Update </button>
+ </div>
+ );
+ }
+ return <div className="js-url url-display">{this.props.origin}</div>;
+ }
+
+ render () {
+ const display = this.createOriginDisplay();
+ return (
+ <tr>
+ <td>
+ {display}
+ </td>
+ <td width="30">
+ <span>
+ <a className="fonticon-pencil" onClick={ this.editOrigin.bind(this) } title="Edit domain." />
+ </span>
+ </td>
+ <td width="30">
+ <span>
+ <a href="#" data-bypass="true" className="fonticon-trash" onClick={ this.deleteOrigin.bind(this) } title="Delete domain." />
+ </span>
+ </td>
+ </tr>
+ );
+ }
+
+};
+
+OriginRow.propTypes = {
+ origin: React.PropTypes.string.isRequired,
+ updateOrigin: React.PropTypes.func.isRequired,
+ deleteOrigin: React.PropTypes.func.isRequired
+};
diff --git a/app/addons/cors/components/OriginTable.js b/app/addons/cors/components/OriginTable.js
new file mode 100644
index 0000000..41e14fa
--- /dev/null
+++ b/app/addons/cors/components/OriginTable.js
@@ -0,0 +1,48 @@
+import React, { Component } from "react";
+import OriginRow from "./OriginRow";
+
+export default class OriginTable extends Component {
+
+ constructor (props) {
+ super(props);
+ }
+
+ createRows () {
+ return this.props.origins.map((origin, i) => {
+ return <OriginRow
+ updateOrigin={this.props.updateOrigin}
+ deleteOrigin={this.props.deleteOrigin}
+ key={i} origin={origin} />;
+ });
+ }
+
+ render () {
+ const {origins, isVisible} = this.props;
+
+ if (!origins) {
+ return null;
+ }
+
+ if (!isVisible || origins.length === 0) {
+ return null;
+ }
+
+ const originRows = this.createRows();
+
+ return (
+ <table id="origin-domain-table" className="table table-striped">
+ <tbody>
+ {originRows}
+ </tbody>
+ </table>
+ );
+ }
+
+};
+
+OriginTable.propTypes = {
+ isVisible: React.PropTypes.bool.isRequired,
+ origins: React.PropTypes.arrayOf(React.PropTypes.string),
+ updateOrigin: React.PropTypes.func.isRequired,
+ deleteOrigin: React.PropTypes.func.isRequired
+};
diff --git a/app/addons/cors/components/Origins.js b/app/addons/cors/components/Origins.js
new file mode 100644
index 0000000..103f8b5
--- /dev/null
+++ b/app/addons/cors/components/Origins.js
@@ -0,0 +1,45 @@
+import React, { Component } from "react";
+
+export default class Origins extends Component {
+
+ constructor (props) {
+ super(props);
+ }
+
+ onOriginChange (event) {
+ if (event.target.value === 'all' && this.props.isAllOrigins) {
+ return; // do nothing if all origins is already selected
+ }
+ if (event.target.value === 'selected' && !this.props.isAllOrigins) {
+ return; // do nothing if specific origins is already selected
+ }
+
+ this.props.originChange(event.target.value === 'all');
+ }
+
+ render () {
+
+ if (!this.props.corsEnabled) {
+ return null;
+ }
+
+ return (
+ <div>
+ <p><strong> Origin Domains </strong> </p>
+ <p>Databases will accept requests from these domains: </p>
+ <label className="radio">
+ <input type="radio" checked={this.props.isAllOrigins} value="all" onChange={ this.onOriginChange.bind(this) } name="all-domains"/> All domains ( * )
+ </label>
+ <label className="radio">
+ <input type="radio" checked={!this.props.isAllOrigins} value="selected" onChange={ this.onOriginChange.bind(this) } name="selected-domains"/> Restrict to specific domains
+ </label>
+ </div>
+ );
+ }
+};
+
+Origins.propTypes = {
+ corsEnabled: React.PropTypes.bool,
+ isAllOrigins: React.PropTypes.bool,
+ originChange: React.PropTypes.func.isRequired
+};
diff --git a/app/addons/cors/base.js b/app/addons/cors/helpers.js
similarity index 50%
copy from app/addons/cors/base.js
copy to app/addons/cors/helpers.js
index 6f8d400..3ecf4f0 100644
--- a/app/addons/cors/base.js
+++ b/app/addons/cors/helpers.js
@@ -11,9 +11,34 @@
// the License.
import FauxtonAPI from "../../core/api";
-import "./assets/less/cors.less";
-var CORS = FauxtonAPI.addon();
-CORS.initialize = function () {};
+export const validateDomain = (domain) => {
+ if (!validateCORSDomain(domain)) {
+ FauxtonAPI.addNotification({
+ msg: 'Please enter a valid domain, starting with http/https.',
+ type: 'error',
+ clear: true
+ });
+ return false;
+ }
+ return true;
+};
+
+export const validateCORSDomain = (domain) => {
+ return (/^https?:\/\/(.+)(:\d{2,5})?$/).test(domain);
+};
+
+export const normalizeUrls = (url) => {
+ const el = document.createElement('a');
+ el.href = url;
+
+ if (/:/.test(url)) {
+ return el.protocol + '//' + el.host;
+ }
+
+ return el.protocol + '//' + el.hostname;
+};
+
+
+
-export default CORS;
diff --git a/app/addons/cors/reducers.js b/app/addons/cors/reducers.js
new file mode 100644
index 0000000..9b8d3f2
--- /dev/null
+++ b/app/addons/cors/reducers.js
@@ -0,0 +1,67 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+import ActionTypes from "./actiontypes";
+
+const initialState = {
+ corsEnabled: false,
+ origins: [],
+ isAllOrigins: false,
+ configChanged: false,
+ shouldSaveChange: false,
+ node: '',
+ isLoading: true,
+ deleteDomainModalVisible: false,
+ domainToDelete: ''
+};
+
+export default function cors (state = initialState, action) {
+ switch (action.type) {
+
+ case ActionTypes.EDIT_CORS:
+ const corsOptions = action.options;
+ return {
+ ...state,
+ isLoading: false,
+ node: corsOptions.node,
+ corsEnabled: corsOptions.corsEnabled,
+ isAllOrigins: _.include(corsOptions.origins, '*'),
+ origins: corsOptions.origins,
+ deleteDomainModalVisible: false,
+ domainToDelete: ''
+ };
+
+ case ActionTypes.CORS_SHOW_DELETE_DOMAIN_MODAL:
+ return {
+ ...state,
+ deleteDomainModalVisible: true,
+ domainToDelete: action.domainToDelete
+ };
+
+ case ActionTypes.CORS_HIDE_DELETE_DOMAIN_MODAL:
+ return {
+ ...state,
+ deleteDomainModalVisible: false,
+ domainToDelete: ''
+ };
+
+ case ActionTypes.CORS_SET_IS_LOADING:
+ return {
+ ...state,
+ isLoading: action.isLoading
+ };
+
+ default:
+ return state;
+ }
+};
+
diff --git a/app/addons/cors/resources.js b/app/addons/cors/resources.js
deleted file mode 100644
index 6c9a1c3..0000000
--- a/app/addons/cors/resources.js
+++ /dev/null
@@ -1,110 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the 'License'); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-import app from "../../app";
-import FauxtonAPI from "../../core/api";
-var CORS = FauxtonAPI.addon();
-
-
-CORS.Config = FauxtonAPI.Model.extend({
- url: function () {
- if (!this.get('node')) {
- throw new Error('node not set');
- }
-
- return window.location.origin + '/_node/' + this.get('node') + '/_config/cors';
- },
-
- parse: function (resp) {
- var origins = !resp.origins ? [] : resp.origins.split(',');
-
- return {
- origins: origins,
- methods: resp.methods,
- credentials: resp.credentials,
- headers: resp.headers
- };
- }
-});
-
-CORS.Httpd = FauxtonAPI.Model.extend({
- url: function () {
- if (!this.get('node')) {
- throw new Error('node not set');
- }
-
- return window.location.origin + '/_node/' + this.get('node') + '/_config/httpd';
- },
-
- corsEnabled: function () {
- var enabledCors = this.get('enable_cors');
-
- if (_.isUndefined(enabledCors)) {
- return false;
- }
-
- return enabledCors === 'true';
- }
-
-});
-
-CORS.ConfigModel = Backbone.Model.extend({
- documentation: 'cors',
-
- url: function () {
- if (!this.get('node')) {
- throw new Error('node not set');
- }
-
- return app.host + '/_node/' + this.get('node') + '/_config/' +
- encodeURIComponent(this.get('section')) + '/' + encodeURIComponent(this.get('attribute'));
- },
-
- isNew: function () { return false; },
-
- sync: function (method, model) {
-
- var params = {
- url: model.url(),
- contentType: 'application/json',
- dataType: 'json',
- data: JSON.stringify(model.get('value'))
- };
-
- if (method === 'delete') {
- params.type = 'DELETE';
- } else {
- params.type = 'PUT';
- }
-
- return $.ajax(params);
- }
-
-});
-
-// simple helper function to validate the user entered a valid domain starting with http(s)
-CORS.validateCORSDomain = function (str) {
- return (/^https?:\/\/(.*)(:\d{2,5})?$/).test(str);
-};
-
-CORS.normalizeUrls = function (url) {
- var el = document.createElement('a');
- el.href = url;
-
- if (/:/.test(url)) {
- return el.protocol + '//' + el.host;
- }
-
- return el.protocol + '//' + el.hostname;
-};
-
-export default CORS;
diff --git a/app/addons/cors/stores.js b/app/addons/cors/stores.js
deleted file mode 100644
index e8fdd62..0000000
--- a/app/addons/cors/stores.js
+++ /dev/null
@@ -1,190 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-import FauxtonAPI from "../../core/api";
-import ActionTypes from "./actiontypes";
-
-var CorsStore = FauxtonAPI.Store.extend({
-
- initialize: function () {
- this.reset();
- },
-
- reset: function () {
- this._deleteDomainModalVisible = false;
- this._domainToDelete = '';
- },
-
- editCors: function (options) {
- this._isEnabled = options.isEnabled;
- this._origins = options.origins;
- this._configChanged = false;
- this._shouldSaveChange = false;
- this._node = options.node;
- this._isLoading = false;
- },
-
- shouldSaveChange: function () {
- return this._shouldSaveChange;
- },
-
- hasConfigChanged: function () {
- return this._configChanged;
- },
-
- setConfigChanged: function () {
- this._configChanged = true;
- },
-
- setConfigSaved: function () {
- this._configChanged = false;
- },
-
- setIsLoading: function (state) {
- this._isLoading = state;
- this._shouldSaveChange = false;
- },
-
- getIsLoading: function () {
- return this._isLoading;
- },
-
- isEnabled: function () {
- return this._isEnabled;
- },
-
- addOrigin: function (origin) {
- this._origins.push(origin);
- },
-
- deleteOrigin: function (origin) {
- var index = _.indexOf(this._origins, origin);
-
- if (index === -1) { return; }
-
- this._origins.splice(index, 1);
- },
-
- originChange: function (isAllOrigins) {
- if (isAllOrigins) {
- this._origins = ['*'];
- return;
- }
-
- this._origins = [];
- },
-
- getOrigins: function () {
- return this._origins;
- },
-
- getNode: function () {
- return this._node;
- },
-
- isAllOrigins: function () {
- var origins = this.getOrigins();
- return _.include(origins, '*');
- },
-
- toggleEnableCors: function () {
- this._isEnabled = !this._isEnabled;
- },
-
- updateOrigin: function (updatedOrigin, originalOrigin) {
- this.deleteOrigin(originalOrigin);
- this.addOrigin(updatedOrigin);
- },
-
- showDeleteDomainModal: function (domain) {
- this._domainToDelete = domain;
- this._deleteDomainModalVisible = true;
- this._shouldSaveChange = false;
- },
-
- hideDeleteDomainModal: function () {
- this._deleteDomainModalVisible = false;
- this._shouldSaveChange = false;
- },
-
- isDeleteDomainModalVisible: function () {
- return this._deleteDomainModalVisible;
- },
-
- getDomainToDelete: function () {
- return this._domainToDelete;
- },
-
- dispatch: function (action) {
- // it should save after any change is triggered except for EDIT_CORS which is just to update
- // cors after the first change
- this._shouldSaveChange = true;
-
- switch (action.type) {
- case ActionTypes.EDIT_CORS:
- this.editCors(action.options);
- break;
-
- case ActionTypes.TOGGLE_ENABLE_CORS:
- this.toggleEnableCors();
- this.setConfigChanged();
- break;
-
- case ActionTypes.CORS_ADD_ORIGIN:
- this.addOrigin(action.origin);
- this.setConfigChanged();
- break;
-
- case ActionTypes.CORS_IS_ALL_ORIGINS:
- this.originChange(action.isAllOrigins);
- this.setConfigChanged();
- break;
-
- case ActionTypes.CORS_DELETE_ORIGIN:
- this.deleteOrigin(action.origin);
- this.setConfigChanged();
- break;
-
- case ActionTypes.CORS_UPDATE_ORIGIN:
- this.updateOrigin(action.updatedOrigin, action.originalOrigin);
- this.setConfigChanged();
- break;
-
- case ActionTypes.CORS_SET_IS_LOADING:
- this.setIsLoading(action.isLoading);
- break;
-
- case ActionTypes.CORS_SHOW_DELETE_DOMAIN_MODAL:
- this.showDeleteDomainModal(action.options.domain);
- break;
-
- case ActionTypes.CORS_HIDE_DELETE_DOMAIN_MODAL:
- this.hideDeleteDomainModal();
- break;
-
- default:
- return;
- }
-
- this.triggerChange();
- }
-
-});
-
-
-var corsStore = new CorsStore();
-
-corsStore.dispatchToken = FauxtonAPI.dispatcher.register(corsStore.dispatch.bind(corsStore));
-
-export default {
- corsStore: corsStore
-};
--
To stop receiving notification emails like this one, please contact
['"commits@couchdb.apache.org" <co...@couchdb.apache.org>'].