cartodb/lib/assets/javascripts/dashboard/views/api-keys/api-keys-form-view.js
2020-06-15 10:58:47 +08:00

288 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
});