288 lines
7.9 KiB
JavaScript
288 lines
7.9 KiB
JavaScript
const _ = require('underscore');
|
||
const Backbone = require('backbone');
|
||
const CoreView = require('backbone/core-view');
|
||
require('dashboard/components/form-components/index');
|
||
const checkAndBuildOpts = require('builder/helpers/required-opts');
|
||
const ApiKeyModel = require('dashboard/data/api-key-model');
|
||
const TableGrantsView = require('dashboard/components/table-grants/table-grants-view');
|
||
const TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||
const template = require('./api-keys-form.tpl');
|
||
const ApiKeysCollection = require('dashboard/data/api-keys-collection');
|
||
|
||
const REQUIRED_OPTS = [
|
||
'stackLayoutModel',
|
||
'userTablesModel',
|
||
'userModel'
|
||
];
|
||
|
||
const API_TYPES = {
|
||
maps: 'maps',
|
||
sql: 'sql'
|
||
};
|
||
|
||
const DATASET_SCOPE_TYPES = {
|
||
create: 'create',
|
||
listing: 'listing'
|
||
};
|
||
|
||
module.exports = CoreView.extend({
|
||
className: 'ApiKeysForm',
|
||
|
||
events: {
|
||
'click .js-back': '_onClickBack',
|
||
'click .js-submit': '_onFormSubmit',
|
||
'change input#create': '_onChangeCreateCheckbox',
|
||
'change input#sql': '_onChangeSqlCheckbox'
|
||
|
||
},
|
||
|
||
initialize: function (options) {
|
||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||
|
||
this._apiKeysCollection = new ApiKeysCollection(null, { userModel: this._userModel });
|
||
this._apiKeyModel = options.apiKeyModel || new ApiKeyModel(null, { userModel: this._userModel });
|
||
this._formView = this._generateForm();
|
||
|
||
this.listenTo(this._formView, 'change', this._onFormChanged);
|
||
this.listenTo(this._apiKeyModel, 'change:tables', this._onFormChanged);
|
||
},
|
||
|
||
render: function () {
|
||
this.$el.empty();
|
||
|
||
this._initViews();
|
||
|
||
this._handleCheckboxState();
|
||
|
||
return this;
|
||
},
|
||
|
||
_initViews: function () {
|
||
this.$el.append(template({ modelIsNew: this._isNew() }));
|
||
|
||
this.$('.js-api-keys-form').append(this._formView.render().el);
|
||
|
||
this._tableGrantsView = new TableGrantsView({
|
||
apiKeyModel: this._apiKeyModel,
|
||
userTablesModel: this._userTablesModel
|
||
});
|
||
|
||
this.addView(this._tableGrantsView);
|
||
|
||
this.$('.js-api-keys-tables').append(this._tableGrantsView.render().el);
|
||
|
||
if (this._isNew()) {
|
||
this._renderTooltip();
|
||
}
|
||
},
|
||
|
||
_generateForm: function () {
|
||
const isDisabled = !this._isNew();
|
||
|
||
const schema = {
|
||
name: {
|
||
type: 'Text',
|
||
title: 'Name',
|
||
validators: ['required'],
|
||
hideValidationErrors: true,
|
||
editorAttrs: {
|
||
disabled: isDisabled,
|
||
placeholder: 'Your API key name',
|
||
id: 'js-api-key-name'
|
||
}
|
||
},
|
||
token: {
|
||
type: 'Text',
|
||
title: 'API Key',
|
||
hasCopyButton: isDisabled,
|
||
editorAttrs: {
|
||
disabled: true
|
||
}
|
||
},
|
||
apis: {
|
||
type: 'MultiCheckbox',
|
||
title: 'APIs',
|
||
validators: ['required'],
|
||
hideValidationErrors: true,
|
||
fieldClass: 'u-iBlock',
|
||
optional: true,
|
||
inputs: [
|
||
{ name: API_TYPES.sql, label: 'SQL' },
|
||
{ name: API_TYPES.maps, label: 'MAPS' }
|
||
],
|
||
editorAttrs: {
|
||
disabled: isDisabled
|
||
}
|
||
},
|
||
datasets: {
|
||
type: 'MultiCheckbox',
|
||
title: 'Datasets',
|
||
hideValidationErrors: true,
|
||
optional: true,
|
||
inputs: [
|
||
{ name: DATASET_SCOPE_TYPES.create, label: 'CREATE' },
|
||
{ name: DATASET_SCOPE_TYPES.listing, label: 'LISTING' }
|
||
],
|
||
editorAttrs: {
|
||
disabled: isDisabled
|
||
}
|
||
}
|
||
};
|
||
|
||
this._formView = new Backbone.Form({ model: this._apiKeyModel, schema });
|
||
|
||
return this._formView;
|
||
},
|
||
|
||
_isNew: function () {
|
||
return !this._apiKeyModel.get('id');
|
||
},
|
||
|
||
_onClickBack: function () {
|
||
this._stackLayoutModel.goToStep(0);
|
||
},
|
||
|
||
_renderTooltip: function () {
|
||
this._validationTooltip = new TipsyTooltipView({
|
||
el: this.$('.js-submit'),
|
||
gravity: 's',
|
||
title: () => 'Name and Datasets fields are required (Choose SQL or MAPS for specific permissions in selected datasets)'
|
||
});
|
||
this.addView(this._validationTooltip);
|
||
},
|
||
|
||
_hasErrors: function () {
|
||
const formErrors = this._formView.validate();
|
||
const selectedDatasetsPermissions = this._apiKeyModel.hasPermissionsSelected();
|
||
const selectedApis = _.some(this._formView.getValue().apis);
|
||
const selectedCreate = this._formView.getValue().datasets.create;
|
||
const selectedListing = this._formView.getValue().datasets.listing;
|
||
const selectedPermissions =
|
||
(selectedDatasetsPermissions && selectedApis) ||
|
||
selectedCreate ||
|
||
(selectedListing && !selectedApis);
|
||
|
||
return !!formErrors || !selectedPermissions;
|
||
},
|
||
|
||
_addApiKeyNameError: function (message) {
|
||
this._errorTooltip = new TipsyTooltipView({
|
||
el: this.$('#js-api-key-name'),
|
||
gravity: 'w',
|
||
title: () => message
|
||
});
|
||
this.addView(this._errorTooltip);
|
||
this._errorTooltip.showTipsy();
|
||
|
||
this.$('#js-api-key-name').addClass('has-error');
|
||
},
|
||
|
||
_onFormChanged: function () {
|
||
this._handleFormErrors();
|
||
this._handleCheckboxState();
|
||
this.$('.js-error').hide();
|
||
},
|
||
|
||
_onChangeCreateCheckbox: function () {
|
||
const createCheckbox = this._formView.getValue().datasets.create;
|
||
|
||
if (createCheckbox) {
|
||
const apisValues = _.clone(this._formView.getValue().apis);
|
||
apisValues.sql = createCheckbox;
|
||
this._formView.setValue('apis', apisValues);
|
||
this.$('input#sql').prop('checked', createCheckbox);
|
||
}
|
||
this._onFormChanged();
|
||
},
|
||
|
||
_onChangeSqlCheckbox: function () {
|
||
const SqlCheckbox = this._formView.getValue().apis.sql;
|
||
|
||
if (!SqlCheckbox) {
|
||
const datasetsValues = _.clone(this._formView.getValue().datasets);
|
||
datasetsValues.create = SqlCheckbox;
|
||
this._formView.setValue('datasets', datasetsValues);
|
||
this.$('input#create').prop('checked', SqlCheckbox);
|
||
}
|
||
this._onFormChanged();
|
||
},
|
||
|
||
_handleCheckboxState: function () {
|
||
const apis = this._isNew()
|
||
? this._formView.getValue().apis
|
||
: this._apiKeyModel.get('apis');
|
||
const activeApis = _.keys(apis).filter(name => apis[name]);
|
||
const disableCheckboxes = activeApis.length === 1 && _.contains(activeApis, API_TYPES.maps);
|
||
|
||
this.$('.ApiKeysForm-grantsTable').toggleClass('showOnlySelect', disableCheckboxes);
|
||
},
|
||
|
||
_handleFormErrors: function () {
|
||
const hasErrors = this._hasErrors();
|
||
|
||
this.$('.js-submit').toggleClass('is-disabled', hasErrors);
|
||
|
||
if (hasErrors) {
|
||
this._validationTooltip || this._renderTooltip();
|
||
} else {
|
||
this._validationTooltip && this._validationTooltip.clean();
|
||
this._validationTooltip = null;
|
||
}
|
||
|
||
this.$('#js-api-key-name').removeClass('has-error');
|
||
this._errorTooltip && this._errorTooltip.clean();
|
||
},
|
||
|
||
_onFormSubmit: function (event) {
|
||
const saveButton = this.$('.js-submit');
|
||
const isButtonLoading = saveButton.hasClass('is-loading');
|
||
|
||
if (this._hasErrors() || isButtonLoading) return;
|
||
|
||
saveButton.addClass('is-loading');
|
||
|
||
const errors = this._formView.commit({ validate: true });
|
||
|
||
if (errors) return;
|
||
|
||
this._apiKeysCollection.create(this._apiKeyModel.attributes, {
|
||
success: (model) => {
|
||
this._apiKeyModel = model;
|
||
this._generateForm();
|
||
this.render();
|
||
},
|
||
error: (model, request) => {
|
||
this._handleServerErrors(request.responseText);
|
||
saveButton.removeClass('is-loading');
|
||
},
|
||
userModel: this._userModel
|
||
});
|
||
},
|
||
|
||
_handleServerErrors: function (error) {
|
||
let message;
|
||
|
||
if (error.indexOf('Name has already been taken') !== -1) {
|
||
message = 'Name already exists';
|
||
} else if (error.indexOf('Name can\'t be blank') !== -1) {
|
||
message = 'Name can\'t be blank';
|
||
}
|
||
|
||
message && this._addApiKeyNameError(message);
|
||
|
||
if (!message) {
|
||
this._displayUnhandledError(error);
|
||
}
|
||
},
|
||
|
||
_displayUnhandledError: function (errorJSON) {
|
||
const parsed = JSON.parse(errorJSON);
|
||
this.$('.js-error').text(parsed.errors).show();
|
||
},
|
||
|
||
clean: function () {
|
||
this._userTablesModel.clearParams();
|
||
CoreView.prototype.clean.apply(this, arguments);
|
||
}
|
||
});
|