diff --git a/client/index.html b/client/index.html
index 5dda3a41..48a0c775 100644
--- a/client/index.html
+++ b/client/index.html
@@ -25,7 +25,8 @@
YTENV_DOCKER_REGISTRY="docker.io";
YTENV_SERVICE_URL_TLD="com";
YTENV_RESOURCE_WHITELIST="npiccolotto";
- YTENV_APPLICATION_WHITELIST="npiccolotto"
+ YTENV_APPLICATION_WHITELIST="npiccolotto";
+ YTENV_MINT_BUCKET_TEMPLATE="default-bucket-${id}-eu-west-1";
diff --git a/client/lib/application/src/access-form/access-form.jsx b/client/lib/application/src/access-form/access-form.jsx
index 2c30fd34..c2042409 100644
--- a/client/lib/application/src/access-form/access-form.jsx
+++ b/client/lib/application/src/access-form/access-form.jsx
@@ -7,6 +7,12 @@ import ScopeList from 'application/src/scope-list.jsx';
import EditableList from 'application/src/editable-list.jsx';
import {constructLocalUrl} from 'common/src/data/services';
import 'common/asset/less/application/access-form.less';
+import MINT_BUCKET_TEMPLATE from 'MINT_BUCKET_TEMPLATE';
+
+function getDefaultBucket(account) {
+ return MINT_BUCKET_TEMPLATE
+ .replace('${id}', account.id);
+}
class AccessForm extends React.Component {
constructor(props) {
@@ -30,6 +36,12 @@ class AccessForm extends React.Component {
});
}
+ addBucket(bucket) {
+ this.setState({
+ s3_buckets: this.state.s3_buckets.concat([bucket])
+ });
+ }
+
updateBuckets(s3_buckets) {
this.setState({
s3_buckets: s3_buckets
@@ -82,6 +94,7 @@ class AccessForm extends React.Component {
{kio, user, mint, essentials} = this.stores,
allAppScopes = essentials.getAllScopes().filter(s => !s.is_resource_owner_scope),
application = kio.getApplication(applicationId),
+ defaultAccount = user.getUserCloudAccounts().filter(a => a.name === application.team_id)[0],
isOwnApplication = user.getUserCloudAccounts().some(t => t.name === application.team_id),
oauth = mint.getOAuthConfig(applicationId);
@@ -116,13 +129,25 @@ class AccessForm extends React.Component {
Activate credential distribution into these S3 buckets (Naming Conventions). A *
indicates unsaved changes.
+ { this.state.s3_buckets.length === 0 && defaultAccount ?
+
+ Psst, your mint bucket is probably:
+
+ {getDefaultBucket(defaultAccount)}
+
+
+ :
+ null}
diff --git a/client/lib/application/src/editable-list.jsx b/client/lib/application/src/editable-list.jsx
index e4c7b381..c2e57f5e 100644
--- a/client/lib/application/src/editable-list.jsx
+++ b/client/lib/application/src/editable-list.jsx
@@ -18,6 +18,14 @@ class EditableList extends React.Component {
});
}
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.items.length !== this.props.items.length) {
+ this.setState({
+ items: nextProps.items
+ });
+ }
+ }
+
addItem(evt) {
evt.preventDefault();
let regex = new RegExp(this.props.pattern);
diff --git a/client/lib/application/test/access-form.test.js b/client/lib/application/test/access-form.test.js
index 6abab2c4..ccf7c17c 100644
--- a/client/lib/application/test/access-form.test.js
+++ b/client/lib/application/test/access-form.test.js
@@ -10,7 +10,7 @@ import UserStore from 'common/src/data/user/user-store';
import UserActions from 'common/src/data/user/user-actions';
import AccessForm from 'application/src/access-form/access-form.jsx';
-const MOCK_KIO = {
+const OAUTH_KIO = {
id: 'kio',
username: 'kio-robot',
last_password_rotation: '2015-01-01T12:42:41Z',
@@ -20,13 +20,21 @@ const MOCK_KIO = {
has_problems: false,
redirect_url: 'http://example.com/oauth',
s3_buckets: [
- 'kio-stups-bucket'
],
scopes: [{
resource_type_id: 'customer',
scope_id: 'read_all'
}]
-};
+},
+APP_KIO = {
+ id: 'kio',
+ team_id: 'stups',
+ active: true
+},
+ACCOUNTS = [{
+ id: '123',
+ name: 'stups'
+}];
class MockFlux extends Flummox {
constructor() {
@@ -58,10 +66,13 @@ describe('The access control form view', () => {
flux.getStore('essentials').receiveScopes(['customer', [{
id: 'read_all'
}]]);
- flux.getStore('mint').receiveOAuthConfig(['kio', MOCK_KIO]);
+ flux.getStore('mint').receiveOAuthConfig(['kio', OAUTH_KIO]);
+ flux.getStore('kio').receiveApplication(APP_KIO);
+ flux.getStore('user').receiveAccounts(ACCOUNTS);
actionSpy = sinon.stub(flux.getActions('mint'), 'saveOAuthConfig', () => {
return Promise.resolve();
});
+
props = {
flux: flux,
applicationId: 'kio'
@@ -74,4 +85,25 @@ describe('The access control form view', () => {
TestUtils.Simulate.submit(f);
expect(actionSpy.calledOnce).to.be.true;
});
+
+ it('should suggest a mint bucket', () => {
+ TestUtils.findRenderedDOMComponentWithAttributeValue(form, 'data-block', 'mint-bucket-suggestion');
+ });
+
+ it('should add suggested bucket to list', () => {
+ expect(() => {
+ TestUtils.findRenderedDOMComponentWithAttributeValue(form, 'data-block', 'editable-list-item');
+ }).to.throw;
+ let btn = TestUtils.findRenderedDOMComponentWithAttributeValue(form, 'data-block', 'mint-bucket-add-suggestion');
+ TestUtils.Simulate.click(btn);
+ TestUtils.findRenderedDOMComponentWithAttributeValue(form, 'data-block', 'editable-list-item');
+ });
+
+ it('should not suggest after adding', () => {
+ let btn = TestUtils.findRenderedDOMComponentWithAttributeValue(form, 'data-block', 'mint-bucket-add-suggestion');
+ TestUtils.Simulate.click(btn);
+ expect(() => {
+ TestUtils.findRenderedDOMComponentWithAttributeValue(form, 'data-block', 'mint-bucket-suggestion');
+ }).to.throw;
+ });
});
\ No newline at end of file
diff --git a/client/lib/common/asset/less/button.less b/client/lib/common/asset/less/button.less
index 203ed6a3..87980274 100644
--- a/client/lib/common/asset/less/button.less
+++ b/client/lib/common/asset/less/button.less
@@ -51,6 +51,12 @@
line-height: 1;
}
+ &.btn-smaller {
+ padding: 0 @padding-tiniest;
+ line-height: 1;
+ font-size: @small-font-size;
+ }
+
&.btn-primary {
.transition(background, border-color);
background: @orange;
diff --git a/client/lib/common/asset/less/form.less b/client/lib/common/asset/less/form.less
index 4a0bda23..69a33008 100644
--- a/client/lib/common/asset/less/form.less
+++ b/client/lib/common/asset/less/form.less
@@ -48,6 +48,9 @@ input[type="search"] {
.form-group {
small {
color: @gray;
+ + small {
+ display: block;
+ }
}
label {
font-weight: @weight-regular;
diff --git a/client/mocha-globals.js b/client/mocha-globals.js
index 6a1a5736..0dd9d58f 100644
--- a/client/mocha-globals.js
+++ b/client/mocha-globals.js
@@ -116,3 +116,4 @@ global.YTENV_SERVICE_URL_TLD = '';
global.YTENV_DOCKER_REGISTRY = '';
global.YTENV_RESOURCE_WHITELIST = '';
global.YTENV_APPLICATION_WHITELIST = '';
+global.YTENV_MINT_BUCKET_TEMPLATE = '';
diff --git a/client/webpack.config.js b/client/webpack.config.js
index 1e60832c..25f9d115 100644
--- a/client/webpack.config.js
+++ b/client/webpack.config.js
@@ -50,7 +50,8 @@ module.exports = {
DOCKER_REGISTRY: 'YTENV_DOCKER_REGISTRY',
SERVICE_URL_TLD: 'YTENV_SERVICE_URL_TLD',
RESOURCE_WHITELIST: 'YTENV_RESOURCE_WHITELIST',
- APPLICATION_WHITELIST: 'YTENV_APPLICATION_WHITELIST'
+ APPLICATION_WHITELIST: 'YTENV_APPLICATION_WHITELIST',
+ MINT_BUCKET_TEMPLATE: 'YTENV_MINT_BUCKET_TEMPLATE'
},
module: {
loaders: [
diff --git a/client/webpack.production.config.js b/client/webpack.production.config.js
index 16b7cfc8..da724e19 100644
--- a/client/webpack.production.config.js
+++ b/client/webpack.production.config.js
@@ -61,7 +61,8 @@ module.exports = {
DOCKER_REGISTRY: 'YTENV_DOCKER_REGISTRY',
SERVICE_URL_TLD: 'YTENV_SERVICE_URL_TLD',
RESOURCE_WHITELIST: 'YTENV_RESOURCE_WHITELIST',
- APPLICATION_WHITELIST: 'YTENV_APPLICATION_WHITELIST'
+ APPLICATION_WHITELIST: 'YTENV_APPLICATION_WHITELIST',
+ MINT_BUCKET_TEMPLATE: 'YTENV_MINT_BUCKET_TEMPLATE'
},
eslint: {
configFile: './.eslintrc',
diff --git a/client/webpack.test.config.js b/client/webpack.test.config.js
index 84c8d18d..ffad0369 100644
--- a/client/webpack.test.config.js
+++ b/client/webpack.test.config.js
@@ -56,6 +56,7 @@ module.exports = {
SERVICE_URL_TLD: 'YTENV_SERVICE_URL_TLD',
RESOURCE_WHITELIST: 'YTENV_RESOURCE_WHITELIST',
APPLICATION_WHITELIST: 'YTENV_APPLICATION_WHITELIST',
+ MINT_BUCKET_TEMPLATE: 'YTENV_MINT_BUCKET_TEMPLATE',
// needed because otherwise two react instances
// are running in tests and they trip each other up
react: 'var React'