Merge branch 'master' into bug/ch165944/fix-race-condition-when-do-subscriptions

pull/16311/head
cgonzalez 3 years ago
commit 9a5f5a1cad

@ -17,6 +17,7 @@ Development
- Guard code for vizjson users [#16267](https://github.com/CartoDB/cartodb/pull/16267)
- Guard code for Users and Visualizations [#16265](https://github.com/CartoDB/cartodb/pull/16265)
- Use the organization user's data while editing a user from organization settings [#16280](https://github.com/CartoDB/cartodb/pull/16280)
- Fix schema name in layers created by free users [#16307](https://github.com/CartoDB/cartodb/pull/16307)
- Limit start parameter of Dropbox connector [#16264](https://github.com/CartoDB/cartodb/pull/16264)
- OauthApps restricted by default [#16304](https://github.com/CartoDB/cartodb/pull/16304)
- Support staging hostname in the catalog [#16258](https://github.com/CartoDB/cartodb/pull/16258)
@ -25,6 +26,7 @@ Development
- Allow the usage of WMTS URLs with parameters to create custom basemaps [#16271](https://github.com/CartoDB/cartodb/pull/16271)
- Sync license_type in redis with the values coming from Central [#16270](https://github.com/CartoDB/cartodb/pull/16270)
- Add `do_bq_project` and `do_bq_dataset` to `api/v3/me` endpoint [#16276](https://github.com/CartoDB/cartodb/pull/16276)
- Avoid updating the same layer more than once when reordering widgets in Builder [#16303](https://github.com/CartoDB/cartodb/pull/16303)
- Add endpoint to update DO subscriptions and manage status of full access requests [#16277](https://github.com/CartoDB/cartodb/pull/16277)
- Add new fields `full_access_[aws|azure]_info` to DO subscriptions [#16278](https://github.com/CartoDB/cartodb/pull/16278)
- Avoid updating analysis nodes more than once when moving layers in Builder [#16279](https://github.com/CartoDB/cartodb/pull/16279)
@ -35,6 +37,7 @@ Development
- Fix an issue that prevents API OPTIONS from succeeding because of undue CSRF check [#16292](https://github.com/CartoDB/cartodb/pull/16292)
- Fix a regression test and add some warnings to source code [#16297](https://github.com/CartoDB/cartodb/pull/16297)
- Fix broken links on the public footer [#16308](https://github.com/CartoDB/cartodb/pull/16308)
- Fix search in _Filter by Column Value_ analysis [#16310](https://github.com/CartoDB/cartodb/pull/16310)
4.45.0 (2021-04-14)
-------------------

@ -6,7 +6,7 @@ var checkAndBuildOpts = require('builder/helpers/required-opts');
var CustomListCollection = require('builder/components/custom-list/custom-list-collection');
var BaseModel = require('builder/components/custom-list/custom-list-item-model');
var queryTemplate = _.template("SELECT DISTINCT <%= column %> FROM (<%= sql %>) _table_sql WHERE <%= column %> ilike '%<%= search %>%' ORDER BY <%= column %> ASC");
var queryTemplate = _.template("SELECT DISTINCT <%= column %> FROM (<%= sql %>) _table_sql WHERE <%= column %>::text ilike '%<%= search %>%' ORDER BY <%= column %> ASC");
var REQUIRED_OPTS = [
'configModel',

@ -46,7 +46,7 @@ module.exports = Backbone.Collection.extend({
if (r.type === 'source') {
var tableData = _.find(self._relatedTableData, function (t) {
return TableNameUtils.isSameTableName(r.options.table_name, t.name, self._configModel.get('user_name'));
return TableNameUtils.isSameTableName(r.options.table_name, t.name, self._userModel.getSchemaName());
});
if (tableData) {
opts.tableData = tableData;

@ -223,18 +223,20 @@ module.exports = function (params) {
return widgetDefinitionsCollection.updateWidgetsOrder(widgets);
},
saveWidget: function (widgetDefModel) {
saveWidget: function (widgetDefModel, saveLayerAnalysis = true, saveOnlyOwnedAnalysis = false) {
if (!widgetDefModel) throw new Error('widgetDefModel is required');
var widgetLayerId = widgetDefModel.get('layer_id');
layerDefinitionsCollection.some(function (layerDefModel) {
if (layerDefModel.id === widgetLayerId) {
analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel);
return true; // aborts the "some"-iterator
}
});
if (saveLayerAnalysis) {
var widgetLayerId = widgetDefModel.get('layer_id');
layerDefinitionsCollection.some(function (layerDefModel) {
if (layerDefModel.id === widgetLayerId) {
analysisDefinitionsCollection.saveAnalysisForLayer(layerDefModel, !saveOnlyOwnedAnalysis);
return true; // aborts the "some"-iterator
}
});
}
widgetDefModel.save();
return widgetDefModel.save();
},
deleteAnalysisNode: function (nodeId) {

@ -2,6 +2,7 @@ var _ = require('underscore');
var $ = require('jquery');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var queue = require('queue-async');
var EditorWidgetView = require('./widget-view');
var IconView = require('builder/components/icon/icon-view');
var template = require('./widgets-view.tpl');
@ -30,6 +31,8 @@ var REQUIRED_OPTS = [
'modals'
];
var SAVE_WIDGET_BATCH_SIZE = 5;
/**
* View to render widgets definitions overview
*/
@ -169,13 +172,29 @@ module.exports = CoreView.extend({
}
},
_onSortableFinish: function () {
_onSortableFinish: async function () {
var self = this;
this.$('.js-widgets > .js-widgetItem').each(function (index, item) {
var savedLayers = {};
var widgets = this.$('.js-widgets > .js-widgetItem').map(function (index, item) {
var modelCid = $(item).data('model-cid');
var widgetDefModel = self._widgetDefinitionsCollection.get(modelCid);
widgetDefModel.set('order', index);
self._userActions.saveWidget(widgetDefModel);
// NOTE: Save only each layer analysis once to avoid rate limits
var layerId = widgetDefModel.get('layer_id');
var data = { model: widgetDefModel, saveLayer: !savedLayers[layerId] };
savedLayers[layerId] = true;
return data;
});
var q = queue(SAVE_WIDGET_BATCH_SIZE);
_.each(widgets, function (item) {
q.defer(async function (callback) {
await self._userActions.saveWidget(item.model, item.saveLayer, true);
callback();
});
});
},

@ -35,7 +35,7 @@ module.exports = {
},
getQualifiedTableName: function (tableName, userName, inOrganization) {
var schemaPrefix = (inOrganization && userName) ? this._quoteIfNeeded(userName) + '.' : '';
var schemaPrefix = userName ? (inOrganization ? this._quoteIfNeeded(userName) + '.' : 'public.') : '';
return schemaPrefix + this._quoteIfNeeded(this.getUnqualifiedName(tableName));
},

@ -40,10 +40,10 @@ describe('builder/data/analysis-definition-node-source-model', function () {
spyOn(this.model.tableModel, 'getOwnerName');
});
it('should provide a default query without qualifing if user does not belong to an organization', function () {
it('should provide a default query with qualifing if user does not belong to an organization', function () {
this.userModel.isInsideOrg.and.returnValue(false);
this.model.tableModel.getOwnerName.and.returnValue('pericoo');
expect(this.model.getDefaultQuery()).toBe('SELECT * FROM bar');
expect(this.model.getDefaultQuery()).toBe('SELECT * FROM public.bar');
});
it('should provide a default query with qualifing if user does belong to an organization', function () {

@ -1,16 +1,23 @@
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
describe('data/analysis-definition-nodes-collection', function () {
beforeEach(function () {
var configModel = new ConfigModel({
user_name: 'pepe',
base_url: '/u/pepe'
});
var userModel = new UserModel(
{ username: 'pepe' },
{ configModel: configModel }
);
this.collection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: {},
userModel: userModel,
relatedTableData: [
{
name: 'foo',

@ -188,8 +188,8 @@ describe('data/layer-definitions-collection', function () {
});
var sourceNode3 = analysisDefinitionNodesCollection.get(layer3.get('source'));
expect(layer3.get('sql').toLowerCase()).toBe('select * from table_name');
expect(sourceNode3.get('query').toLowerCase()).toBe('select * from table_name');
expect(layer3.get('sql').toLowerCase()).toBe('select * from public.table_name');
expect(sourceNode3.get('query').toLowerCase()).toBe('select * from public.table_name');
var layer4 = collection.add({
kind: 'carto',

@ -2579,6 +2579,52 @@ describe('builder/data/user-actions', function () {
expect(this.analysisDefinitionsCollection.pluck('node_id')).toEqual(['a0']);
});
});
describe('when flags are not the default ones', function () {
beforeEach(function () {
this.wa1 = this.widgetDefinitionsCollection.add({
id: 'wa1',
layer_id: 'A',
type: 'formula',
source: { id: 'a0' }
});
this.a0 = this.analysisDefinitionsCollection.add({
id: 'A',
analysis_definition: {
id: 'a0', type: 'source', params: { query: 'SELECT * FROM a_single_source' }
}
});
this.b1 = this.analysisDefinitionsCollection.add({
id: 'B',
analysis_definition: {
id: 'b1',
type: 'intersection',
params: {
source: { id: 'b0', type: 'source', params: { query: 'SELECT * FROM my_polygons' } },
target: { id: 'a0' }
},
options: { primary_source_name: 'source' }
}
});
spyOn(this.analysisDefinitionsCollection, 'saveAnalysisForLayer');
spyOn(this.a0, 'save');
spyOn(this.b1, 'save');
});
it('should not save the layer analysis if the "saveLayerAnalysis" flag is set to false', function () {
this.userActions.saveWidget(this.wa1, false);
expect(this.analysisDefinitionsCollection.saveAnalysisForLayer).not.toHaveBeenCalled();
});
it('should save only the owned layer analysis if the "saveOnlyOwnedAnalysis" flag is set to true', function () {
this.userActions.saveWidget(this.wa1, true, true);
expect(this.analysisDefinitionsCollection.saveAnalysisForLayer).toHaveBeenCalledWith(this.A, false);
});
});
});
describe('smoke tests', function () {

@ -217,7 +217,7 @@ describe('dataset/dataset-header-view', function () {
it('should set default query when vis name changes', function () {
HeaderView.prototype._setDocumentTitle.calls.reset();
this.view._visModel.set('name', 'hello_new_table');
expect(this.view._layerDefinitionModel.get('sql')).toBe('SELECT * FROM hello_new_table');
expect(this.view._layerDefinitionModel.get('sql')).toBe('SELECT * FROM public.hello_new_table');
});
it('should open privacy dropdown if user is owner', function () {

@ -206,7 +206,8 @@ describe('editor/widgets/widgets-view', function () {
// End of fake sortable
expect(this.widgetDefModel1.get('order')).toBe(0);
expect(this.widgetDefModel2.get('order')).toBe(1);
expect(this.userActions.saveWidget).toHaveBeenCalled();
expect(this.userActions.saveWidget).toHaveBeenCalledWith(this.widgetDefModel1, true, true);
expect(this.userActions.saveWidget).toHaveBeenCalledWith(this.widgetDefModel2, true, false);
});
});

@ -72,19 +72,19 @@ describe('helpers/sql-utils', function () {
});
it('.getDefaultSQL', function () {
expect(SQLUtils.getDefaultSQL('table', 'user', false)).toBe('SELECT * FROM table');
expect(SQLUtils.getDefaultSQL('table', 'user', false)).toBe('SELECT * FROM public.table');
expect(SQLUtils.getDefaultSQL('table', 'user', true)).toBe('SELECT * FROM user.table');
expect(SQLUtils.getDefaultSQL('table', undefined, true)).toBe('SELECT * FROM table');
expect(SQLUtils.getDefaultSQL('1table', 'user', false)).toBe('SELECT * FROM "1table"');
expect(SQLUtils.getDefaultSQL('1table', 'user', false)).toBe('SELECT * FROM public."1table"');
expect(SQLUtils.getDefaultSQL('1table', 'user', true)).toBe('SELECT * FROM user."1table"');
expect(SQLUtils.getDefaultSQL('1table', undefined, true)).toBe('SELECT * FROM "1table"');
expect(SQLUtils.getDefaultSQL('table', 'user-me', false)).toBe('SELECT * FROM table');
expect(SQLUtils.getDefaultSQL('table', 'user-me', false)).toBe('SELECT * FROM public.table');
expect(SQLUtils.getDefaultSQL('table', 'user-me', true)).toBe('SELECT * FROM "user-me".table');
expect(SQLUtils.getDefaultSQL('table', undefined, true)).toBe('SELECT * FROM table');
expect(SQLUtils.getDefaultSQL('tabl-e', '1stuser', false)).toBe('SELECT * FROM "tabl-e"');
expect(SQLUtils.getDefaultSQL('tabl-e', '1stuser', false)).toBe('SELECT * FROM public."tabl-e"');
expect(SQLUtils.getDefaultSQL('tabl-e', '1stuser', true)).toBe('SELECT * FROM "1stuser"."tabl-e"');
expect(SQLUtils.getDefaultSQL('tabl-e', undefined, true)).toBe('SELECT * FROM "tabl-e"');
});

2
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "cartodb-ui",
"version": "1.0.0-assets.250",
"version": "1.0.0-assets.254",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

@ -1,6 +1,6 @@
{
"name": "cartodb-ui",
"version": "1.0.0-assets.251",
"version": "1.0.0-assets.254",
"description": "CARTO UI frontend",
"repository": {
"type": "git",

Loading…
Cancel
Save