3606 lines
151 KiB
Ruby
3606 lines
151 KiB
Ruby
require_relative '../../../spec_helper'
|
|
require_relative '../../../factories/users_helper'
|
|
require_relative '../../../../app/controllers/carto/api/visualizations_controller'
|
|
|
|
# TODO: Remove once Carto::Visualization is complete enough
|
|
require_relative '../../../../app/models/visualization/member'
|
|
require_relative './vizjson_shared_examples'
|
|
require 'helpers/unique_names_helper'
|
|
require_dependency 'carto/uuidhelper'
|
|
require 'factories/carto_visualizations'
|
|
require 'helpers/visualization_destruction_helper'
|
|
require 'helpers/feature_flag_helper'
|
|
|
|
include Carto::UUIDHelper
|
|
|
|
describe Carto::Api::VisualizationsController do
|
|
include UniqueNamesHelper
|
|
include Carto::Factories::Visualizations
|
|
include VisualizationDestructionHelper
|
|
include FeatureFlagHelper
|
|
|
|
before(:all) do
|
|
create_account_type_fg('ORGANIZATION USER')
|
|
end
|
|
|
|
describe 'vizjson2 generator' do
|
|
it_behaves_like 'vizjson generator' do
|
|
def api_vx_visualizations_vizjson_url(options)
|
|
api_v2_visualizations_vizjson_url(options)
|
|
end
|
|
|
|
def vizjson_vx_version
|
|
'0.1.0'
|
|
end
|
|
|
|
def attributions_from_vizjson(visualization)
|
|
visualization['layers'][1]['options']['attribution'].split(',').map(&:strip)
|
|
end
|
|
|
|
before(:each) do
|
|
bypass_named_maps
|
|
end
|
|
|
|
it 'marks visualizations as using vizjson2' do
|
|
visualization = FactoryGirl.create(:carto_visualization)
|
|
Carto::Api::VisualizationsController.any_instance.stubs(:generate_vizjson2).returns({})
|
|
get(
|
|
api_v2_visualizations_vizjson_url(user_domain: visualization.user.username, id: visualization.id),
|
|
{},
|
|
'HTTP_REFERER' => 'http://wadus.com'
|
|
)
|
|
visualization.uses_vizjson2?.should be_true
|
|
end
|
|
|
|
it 'marks visualizations as using vizjson2 with invalid referer' do
|
|
visualization = FactoryGirl.create(:carto_visualization)
|
|
Carto::Api::VisualizationsController.any_instance.stubs(:generate_vizjson2).returns({})
|
|
get(
|
|
api_v2_visualizations_vizjson_url(user_domain: visualization.user.username, id: visualization.id),
|
|
{},
|
|
'HTTP_REFERER' => 'invalid'
|
|
)
|
|
visualization.uses_vizjson2?.should be_true
|
|
end
|
|
|
|
it 'marks visualizations as using vizjson2 without referer' do
|
|
visualization = FactoryGirl.create(:carto_visualization)
|
|
Carto::Api::VisualizationsController.any_instance.stubs(:generate_vizjson2).returns({})
|
|
get api_v2_visualizations_vizjson_url(user_domain: visualization.user.username, id: visualization.id)
|
|
visualization.uses_vizjson2?.should be_true
|
|
end
|
|
|
|
it 'does not mark visualizations as using vizjson2 with carto referer' do
|
|
visualization = FactoryGirl.create(:carto_visualization)
|
|
Carto::Api::VisualizationsController.any_instance.stubs(:generate_vizjson2).returns({})
|
|
get(
|
|
api_v2_visualizations_vizjson_url(user_domain: visualization.user.username, id: visualization.id),
|
|
{},
|
|
'HTTP_REFERER' => 'https://carto.com/wadus'
|
|
)
|
|
visualization.uses_vizjson2?.should be_false
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'vizjson3 generator' do
|
|
it_behaves_like 'vizjson generator' do
|
|
def api_vx_visualizations_vizjson_url(options)
|
|
api_v3_visualizations_vizjson_url(options)
|
|
end
|
|
|
|
def vizjson_vx_version
|
|
'3.0.0'
|
|
end
|
|
|
|
def attributions_from_vizjson(visualization)
|
|
visualization['layers'].select { |l| l['type'] == 'CartoDB' }
|
|
.map { |l| l['options']['attribution'] }
|
|
.select(&:present?)
|
|
end
|
|
end
|
|
end
|
|
|
|
TEST_UUID = '00000000-0000-0000-0000-000000000000'.freeze
|
|
|
|
DATE_ATTRIBUTES = %w{ created_at updated_at }.freeze
|
|
NORMALIZED_ASSOCIATION_ATTRIBUTES = {
|
|
attributes: DATE_ATTRIBUTES,
|
|
associations: {
|
|
'permission' => {
|
|
attributes: DATE_ATTRIBUTES,
|
|
associations: {}
|
|
},
|
|
'table' => {
|
|
attributes: DATE_ATTRIBUTES,
|
|
associations: {}
|
|
}
|
|
}
|
|
}.freeze
|
|
|
|
NEW_ATTRIBUTES = {
|
|
attributes: [],
|
|
associations: {
|
|
'table' => {
|
|
attributes: [],
|
|
associations: {
|
|
'permission' => {
|
|
attributes: [],
|
|
associations: {
|
|
'owner' => {
|
|
attributes: ['email', 'quota_in_bytes', 'db_size_in_bytes', 'public_visualization_count',
|
|
'all_visualization_count', 'table_count'],
|
|
associations: {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'permission' => {
|
|
attributes: [],
|
|
associations: {
|
|
'owner' => {
|
|
attributes: ['email', 'quota_in_bytes', 'db_size_in_bytes', 'public_visualization_count',
|
|
'all_visualization_count', 'table_count'],
|
|
associations: {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}.freeze
|
|
|
|
BBOX_GEOM = '{"type":"MultiPolygon","coordinates":[[[[-75.234375,54.57206166],[4.921875,54.36775852],[7.03125,-0.35156029],[-71.71875,1.75753681],[-75.234375,54.57206166]]]]}'.freeze
|
|
OUTSIDE_BBOX_GEOM = '{"type":"MultiPolygon","coordinates":[[[[-149.4140625,79.74993208],[-139.921875,79.74993208],[-136.0546875,78.13449318],[-148.7109375,78.06198919],[-149.4140625,79.74993208]]]]}'.freeze
|
|
|
|
describe 'static_map' do
|
|
include_context 'visualization creation helpers'
|
|
include_context 'users helper'
|
|
|
|
before(:all) do
|
|
Carto::NamedMaps::Api.any_instance.stubs(get: nil, create: true, update: true)
|
|
|
|
@user_1 = FactoryGirl.create(:valid_user, private_tables_enabled: false)
|
|
@table1 = create_random_table(@user_1)
|
|
|
|
@headers = { 'CONTENT_TYPE' => 'application/json' }
|
|
host! "#{@user_1.subdomain}.localhost.lan"
|
|
end
|
|
|
|
after(:all) do
|
|
@user_1.destroy
|
|
end
|
|
|
|
it 'tests with non-existing cdn config, which uses maps_api_template url' do
|
|
width = 123
|
|
height = 456
|
|
|
|
Carto::StaticMapsURLHelper.any_instance
|
|
.stubs(:get_cdn_config)
|
|
.returns(nil)
|
|
ApplicationHelper.stubs(:maps_api_template)
|
|
.returns("http://#{@user_1.username}.localhost.lan:8181")
|
|
|
|
get api_v2_visualizations_static_map_url(
|
|
user_domain: @user_1.username,
|
|
id: @table1.table_visualization.id,
|
|
width: width,
|
|
height: height), @headers
|
|
last_response.status.should == 302
|
|
|
|
tpl_id = Carto::NamedMaps::Template.new(Carto::Visualization.find(@table1.table_visualization.id)).name
|
|
last_response.location.should == "http://#{@user_1.username}.localhost.lan:8181/api/v1/map/static/named/#{tpl_id}/#{width}/#{height}.png"
|
|
end
|
|
|
|
it 'tests with existing cdn config' do
|
|
width = 123
|
|
height = 456
|
|
|
|
Carto::StaticMapsURLHelper.any_instance
|
|
.stubs(:get_cdn_config)
|
|
.returns("http" => "cdn.local.lan")
|
|
|
|
get api_v2_visualizations_static_map_url(
|
|
user_domain: @user_1.username,
|
|
id: @table1.table_visualization.id,
|
|
width: width,
|
|
height: height
|
|
), @headers
|
|
last_response.status.should == 302
|
|
|
|
tpl_id = Carto::NamedMaps::Template.new(Carto::Visualization.find(@table1.table_visualization.id)).name
|
|
last_response.location.should == "http://cdn.local.lan/#{@user_1.username}/api/v1/map/static/named/#{tpl_id}/#{width}/#{height}.png"
|
|
end
|
|
|
|
it 'tests privacy of static_maps calls' do
|
|
# As privacy is equal to other visualizations controller methods, no need to test every option, just generally
|
|
width = 123
|
|
height = 456
|
|
|
|
# By default no private tables so all are created public
|
|
@user_1.private_tables_enabled = true
|
|
@user_1.save
|
|
|
|
private_table = create_random_table(@user_1)
|
|
|
|
@user_1.private_tables_enabled = false
|
|
@user_1.save
|
|
|
|
Carto::StaticMapsURLHelper.any_instance
|
|
.stubs(:get_cdn_config)
|
|
.returns(nil)
|
|
ApplicationHelper.stubs(:maps_api_template)
|
|
.returns("http://#{@user_1.username}.localhost.lan:8181")
|
|
|
|
get api_v2_visualizations_static_map_url(
|
|
user_domain: @user_1.username,
|
|
id: @table1.table_visualization.id,
|
|
width: width,
|
|
height: height
|
|
), @headers
|
|
last_response.status.should == 302
|
|
|
|
template_name = Carto::NamedMaps::Template.new(Carto::Visualization.find(@table1.table_visualization.id)).name
|
|
last_response.location.should == "http://#{@user_1.username}.localhost.lan:8181/api/v1/map/static/named/#{template_name}/#{width}/#{height}.png"
|
|
|
|
get api_v2_visualizations_static_map_url(
|
|
user_domain: @user_1.username,
|
|
id: private_table.table_visualization.id,
|
|
width: width,
|
|
height: height
|
|
), @headers
|
|
last_response.status.should == 403
|
|
|
|
get api_v2_visualizations_static_map_url(
|
|
user_domain: @user_1.username,
|
|
api_key: @user_1.api_key,
|
|
id: private_table.table_visualization.id,
|
|
width: width,
|
|
height: height
|
|
), @headers
|
|
last_response.status.should == 302
|
|
|
|
visualization_id = private_table.table_visualization.id
|
|
|
|
template_name = Carto::NamedMaps::Template.new(Carto::Visualization.find(visualization_id)).name
|
|
last_response.location.should == "http://#{@user_1.username}.localhost.lan:8181/api/v1/map/static/named/#{template_name}/#{width}/#{height}.png"
|
|
end
|
|
|
|
it 'tests varnish keys' do
|
|
width = 123
|
|
height = 456
|
|
|
|
Carto::StaticMapsURLHelper.any_instance
|
|
.stubs(:get_cdn_config)
|
|
.returns("http" => "cdn.local.lan")
|
|
|
|
get api_v2_visualizations_static_map_url(
|
|
user_domain: @user_1.username,
|
|
# api_key: @user_1.api_key,
|
|
id: @table1.table_visualization.id,
|
|
width: width,
|
|
height: height
|
|
), @headers
|
|
last_response.status.should == 302
|
|
last_response.headers["X-Cache-Channel"].should include(@table1.name)
|
|
last_response.headers["X-Cache-Channel"].should include(@table1.table_visualization.varnish_key)
|
|
last_response.headers["Surrogate-Key"].should_not be_empty
|
|
last_response.headers["Surrogate-Key"].should include(CartoDB::SURROGATE_NAMESPACE_VIZJSON)
|
|
last_response.headers["Surrogate-Key"].should include(@table1.table_visualization.surrogate_key)
|
|
end
|
|
|
|
end
|
|
|
|
describe 'index' do
|
|
include_context 'visualization creation helpers'
|
|
include_context 'users helper'
|
|
|
|
before(:all) do
|
|
Carto::NamedMaps::Api.any_instance.stubs(get: nil, create: true, update: true)
|
|
|
|
@user_1 = FactoryGirl.create(:valid_user)
|
|
end
|
|
|
|
before(:each) do
|
|
login(@user_1)
|
|
@headers = { 'CONTENT_TYPE' => 'application/json' }
|
|
host! "#{@user_1.subdomain}.localhost.lan"
|
|
end
|
|
|
|
after(:each) do
|
|
delete_user_data @user_1
|
|
end
|
|
|
|
after(:all) do
|
|
@user_1.destroy
|
|
end
|
|
|
|
def compare_with_dates(a, b)
|
|
# Compares two hashes, ignoring differences in timezones
|
|
a.keys.sort.should eq b.keys.sort
|
|
a.each do |k, v|
|
|
v2 = b[k]
|
|
if k.ends_with?('_at')
|
|
# DateTime.parse(v).should eq DateTime.parse(v2) unless v.blank? && v2.blank?
|
|
elsif v.is_a?(Hash)
|
|
compare_with_dates(v, v2)
|
|
else
|
|
v.should eq v2
|
|
end
|
|
end
|
|
end
|
|
|
|
it 'returns success, empty response for empty user' do
|
|
expected_response = {
|
|
'visualizations' => [],
|
|
'total_entries' => 0,
|
|
'total_likes' => 0,
|
|
'total_user_entries' => 0,
|
|
'total_shared' => 0,
|
|
'total_locked' => 0
|
|
}
|
|
response_body.should == expected_response
|
|
end
|
|
|
|
it 'returns 400 if wrong page parameter is passed' do
|
|
get base_url, { page: '%00' }, @headers
|
|
last_response.status.should == 400
|
|
end
|
|
|
|
it 'returns 400 if wrong per_page parameter is passed' do
|
|
get base_url, { per_page: '%00' }, @headers
|
|
last_response.status.should == 400
|
|
end
|
|
|
|
it 'returns valid information for a user with one table' do
|
|
table1 = create_random_table(@user_1)
|
|
table1_visualization = CartoDB::Visualization::Member.new(id: table1.table_visualization.id).fetch
|
|
table1_visualization_hash = table1_visualization.to_hash(
|
|
related: false,
|
|
table_data: true,
|
|
user: @user_1,
|
|
table: table1,
|
|
synchronization: nil)
|
|
table1_visualization_hash[:permission][:owner].delete(:groups)
|
|
table1_visualization_hash[:table][:permission][:owner].delete(:groups)
|
|
expected_visualization = JSON.parse(table1_visualization_hash.to_json)
|
|
expected_visualization = normalize_hash(expected_visualization)
|
|
|
|
# This is only in the Carto::Visualization presenter (not in old member presenter)
|
|
expected_visualization['uses_builder_features'] = false
|
|
expected_visualization['auth_tokens'] = nil # normalize_hash converts [] to nil
|
|
expected_visualization['version'] = 2
|
|
|
|
response = response_body(type: CartoDB::Visualization::Member::TYPE_CANONICAL)
|
|
# INFO: old API won't support server side generated urls for visualizations. See #5250 and #5279
|
|
response['visualizations'][0].delete('url')
|
|
response['visualizations'][0]['synchronization'] = {}
|
|
compare_with_dates(response['visualizations'][0], expected_visualization)
|
|
response['total_entries'].should eq 1
|
|
response['total_user_entries'].should eq 1
|
|
response['total_shared'].should eq 0
|
|
response['total_locked'].should eq 0
|
|
end
|
|
|
|
it 'returns locked count' do
|
|
FactoryGirl.build(:derived_visualization, user_id: @user_1.id, locked: false).store
|
|
FactoryGirl.build(:derived_visualization, user_id: @user_1.id, locked: true).store
|
|
user2 = FactoryGirl.create(:valid_user)
|
|
FactoryGirl.build(:derived_visualization, user_id: user2.id, locked: true).store
|
|
|
|
response_body(type: CartoDB::Visualization::Member::TYPE_DERIVED)['total_locked'].should == 1
|
|
end
|
|
|
|
it 'does a partial match search' do
|
|
FactoryGirl.build(:derived_visualization, user_id: @user_1.id, name: 'foo').store
|
|
FactoryGirl.build(:derived_visualization, user_id: @user_1.id, name: 'bar').store
|
|
FactoryGirl.build(:derived_visualization, user_id: @user_1.id, name: 'foo_patata_bar').store
|
|
FactoryGirl.build(:derived_visualization, user_id: @user_1.id, name: 'foo_patata_baz').store
|
|
|
|
body = response_body(q: 'patata', type: CartoDB::Visualization::Member::TYPE_DERIVED)
|
|
body['total_entries'].should == 2
|
|
body['total_user_entries'].should == 4
|
|
|
|
body = response_body(q: '_atata', type: CartoDB::Visualization::Member::TYPE_DERIVED)
|
|
body['total_entries'].should == 0
|
|
body['total_user_entries'].should == 4
|
|
end
|
|
|
|
it 'allows to search datasets including dependent visualizations' do
|
|
FactoryGirl.create(:table_visualization, user_id: @user_1.id, name: 'foo')
|
|
|
|
body = response_body(q: 'foo', type: CartoDB::Visualization::Member::TYPE_CANONICAL,
|
|
with_dependent_visualizations: 10)
|
|
|
|
body['total_entries'].should == 1
|
|
body['total_user_entries'].should == 1
|
|
body['visualizations'][0]['dependent_visualizations_count'].should be_zero
|
|
end
|
|
|
|
describe 'performance with many tables' do
|
|
# The bigger the number the better the improvement, but test gets too slow
|
|
VIZS_N = 20
|
|
|
|
before(:all) do
|
|
@visualizations = (1..VIZS_N).map { FactoryGirl.create(:carto_user_table_with_canonical, user_id: @user_1.id) }
|
|
end
|
|
|
|
LIST_NAMES_PARAMS = {
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL,
|
|
order: :updated_at,
|
|
page: 1,
|
|
per_page: 3000,
|
|
exclude_shared: false,
|
|
exclude_raster: true
|
|
}.freeze
|
|
|
|
NO_FETCHING_PARAMS = {
|
|
show_liked: false,
|
|
show_stats: false,
|
|
show_table: false,
|
|
show_permission: false,
|
|
show_synchronization: false,
|
|
show_uses_builder_features: false,
|
|
show_auth_tokens: false,
|
|
load_totals: false
|
|
}.freeze
|
|
|
|
it 'should improve with reduced fetching (see #12058)' do
|
|
no_fetching_params = LIST_NAMES_PARAMS.merge(NO_FETCHING_PARAMS)
|
|
|
|
beginning = Time.now
|
|
get base_url, LIST_NAMES_PARAMS.dup, @headers
|
|
body1 = last_response.body
|
|
full_time = Time.now
|
|
|
|
get base_url, no_fetching_params.dup, @headers
|
|
body2 = last_response.body
|
|
no_fetch_time = Time.now
|
|
|
|
((full_time - beginning) / (no_fetch_time - full_time)).should be >= 3
|
|
|
|
body1 = JSON.parse(body1)
|
|
body1['visualizations'].count.should eq VIZS_N
|
|
body1['total_entries'].should eq VIZS_N
|
|
body1['total_shared'].should_not be_nil
|
|
body2 = JSON.parse(body2)
|
|
body2['visualizations'].count.should eq VIZS_N
|
|
body2['total_entries'].should eq VIZS_N
|
|
body2['total_shared'].should be_nil
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
describe 'main behaviour' do
|
|
before(:all) do
|
|
CartoDB::Varnish.any_instance.stubs(:send_command).returns(true)
|
|
|
|
Carto::NamedMaps::Api.any_instance.stubs(get: nil, create: true, update: true)
|
|
|
|
@user_1 = FactoryGirl.create(:valid_user)
|
|
@carto_user1 = Carto::User.find(@user_1.id)
|
|
@user_2 = FactoryGirl.create(:valid_user, private_maps_enabled: true)
|
|
@carto_user2 = Carto::User.find(@user_2.id)
|
|
@api_key = @user_1.api_key
|
|
end
|
|
|
|
before(:each) do
|
|
begin
|
|
delete_user_data @user_1
|
|
rescue => exception
|
|
# Silence named maps problems only here upon data cleaning, not in specs
|
|
raise unless exception.class.to_s == 'CartoDB::NamedMapsWrapper::HTTPResponseError'
|
|
end
|
|
|
|
@headers = {
|
|
'CONTENT_TYPE' => 'application/json'
|
|
}
|
|
host! "#{@user_1.username}.localhost.lan"
|
|
end
|
|
|
|
after(:all) do
|
|
@user_1.destroy
|
|
@user_2.destroy
|
|
end
|
|
|
|
it 'tests exclude_shared and only_shared filters' do
|
|
Carto::NamedMaps::Api.any_instance.stubs(get: nil, create: true, update: true)
|
|
|
|
user_1 = create_user(
|
|
username: unique_name('user'),
|
|
email: unique_email,
|
|
password: 'clientex',
|
|
private_tables_enabled: false
|
|
)
|
|
|
|
user_2 = create_user(
|
|
username: unique_name('user'),
|
|
email: unique_email,
|
|
password: 'clientex',
|
|
private_tables_enabled: false
|
|
)
|
|
|
|
organization = test_organization.save
|
|
|
|
user_org = CartoDB::UserOrganization.new(organization.id, user_1.id)
|
|
user_org.promote_user_to_admin
|
|
organization.reload
|
|
user_1.reload
|
|
|
|
user_2.organization_id = organization.id
|
|
user_2.save.reload
|
|
organization.reload
|
|
|
|
table = create_table(privacy: UserTable::PRIVACY_PUBLIC, name: unique_name('table'), user_id: user_1.id)
|
|
u1_t_1_id = table.table_visualization.id
|
|
u1_t_1_perm_id = table.table_visualization.permission.id
|
|
|
|
table = create_table(privacy: UserTable::PRIVACY_PUBLIC, name: unique_name('table'), user_id: user_2.id)
|
|
u2_t_1_id = table.table_visualization.id
|
|
|
|
post api_v1_visualizations_create_url(user_domain: user_1.username, api_key: user_1.api_key),
|
|
factory(user_1).to_json, @headers
|
|
last_response.status.should == 200
|
|
u1_vis_1_id = JSON.parse(last_response.body).fetch('id')
|
|
u1_vis_1_perm_id = JSON.parse(last_response.body).fetch('permission').fetch('id')
|
|
|
|
post api_v1_visualizations_create_url(user_domain: user_2.username, api_key: user_2.api_key),
|
|
factory(user_2).to_json, @headers
|
|
last_response.status.should == 200
|
|
u2_vis_1_id = JSON.parse(last_response.body).fetch('id')
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_1.username, api_key: user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
vis = body['visualizations'].first
|
|
vis['id'].should eq u1_t_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_1.username, api_key: user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
vis = body['visualizations'].first
|
|
vis['id'].should eq u1_vis_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
vis = body['visualizations'].first
|
|
vis['id'].should eq u2_t_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
vis = body['visualizations'].first
|
|
vis['id'].should eq u2_vis_1_id
|
|
|
|
# Share u1 vis with u2
|
|
put api_v1_permissions_update_url(user_domain:user_1.username, api_key: user_1.api_key, id: u1_vis_1_perm_id),
|
|
{acl: [{
|
|
type: CartoDB::Permission::TYPE_USER,
|
|
entity: {
|
|
id: user_2.id,
|
|
},
|
|
access: CartoDB::Permission::ACCESS_READONLY
|
|
}]}.to_json, @headers
|
|
last_response.status.should == 200
|
|
|
|
# Vis listing checks
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 2
|
|
# Permissions don't change updated_at
|
|
body['visualizations'][0]['id'].should eq u2_vis_1_id
|
|
body['visualizations'][1]['id'].should eq u1_vis_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_1.username, api_key: user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
body['visualizations'][0]['id'].should eq u1_vis_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at',
|
|
exclude_shared: false), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 2
|
|
body['visualizations'][0]['id'].should eq u2_vis_1_id
|
|
body['visualizations'][1]['id'].should eq u1_vis_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at',
|
|
only_shared: false), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 2
|
|
body['visualizations'][0]['id'].should eq u2_vis_1_id
|
|
body['visualizations'][1]['id'].should eq u1_vis_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at',
|
|
exclude_shared: true), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
body['visualizations'][0]['id'].should eq u2_vis_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at',
|
|
only_shared: true), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
body['visualizations'][0]['id'].should eq u1_vis_1_id
|
|
|
|
# Same with 'shared' filter (convenience alias for not handling both exclude_shared and only_shared)
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at',
|
|
shared: CartoDB::Visualization::Collection::FILTER_SHARED_YES), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 2
|
|
body['visualizations'][0]['id'].should eq u2_vis_1_id
|
|
body['visualizations'][1]['id'].should eq u1_vis_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at',
|
|
shared: CartoDB::Visualization::Collection::FILTER_SHARED_NO), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
body['visualizations'][0]['id'].should eq u2_vis_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at',
|
|
shared: CartoDB::Visualization::Collection::FILTER_SHARED_ONLY), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
body['visualizations'][0]['id'].should eq u1_vis_1_id
|
|
|
|
# Share u1 table with u2
|
|
put api_v1_permissions_update_url(user_domain:user_1.username, api_key: user_1.api_key, id: u1_t_1_perm_id),
|
|
{acl: [{
|
|
type: CartoDB::Permission::TYPE_USER,
|
|
entity: {
|
|
id: user_2.id,
|
|
},
|
|
access: CartoDB::Permission::ACCESS_READONLY
|
|
}]}.to_json, @headers
|
|
last_response.status.should == 200
|
|
|
|
# Dunno why (rack test error?) but this call seems to cache previous params, so just call it to "flush" them
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at',
|
|
shared: 'wadus',
|
|
exclude_shared: false,
|
|
only_shared: false),
|
|
@headers
|
|
# -------------
|
|
|
|
# Table listing checks
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 2
|
|
body['visualizations'][0]['id'].should eq u2_t_1_id
|
|
body['visualizations'][1]['id'].should eq u1_t_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_1.username, api_key: user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
body['visualizations'][0]['id'].should eq u1_t_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at',
|
|
exclude_shared: false), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 2
|
|
body['visualizations'][0]['id'].should eq u2_t_1_id
|
|
body['visualizations'][1]['id'].should eq u1_t_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at',
|
|
only_shared: false), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 2
|
|
body['visualizations'][0]['id'].should eq u2_t_1_id
|
|
body['visualizations'][1]['id'].should eq u1_t_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at',
|
|
exclude_shared: true), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
body['visualizations'][0]['id'].should eq u2_t_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: user_2.username, api_key: user_2.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at',
|
|
only_shared: true), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
body['visualizations'][0]['id'].should eq u1_t_1_id
|
|
end
|
|
|
|
context 'visualization likes endpoints' do
|
|
before(:each) do
|
|
@map, @table, @table_visualization, @map_visualization = create_full_visualization(@carto_user1, visualization_attributes: { version: nil, privacy: Carto::Visualization::PRIVACY_PUBLIC })
|
|
@vis = FactoryGirl.create(:carto_visualization, user: @carto_user1)
|
|
@vis_user2 = FactoryGirl.create(:carto_visualization, user: @carto_user2)
|
|
@user_domain = @carto_user1.username
|
|
@user_domain2 = @carto_user2.username
|
|
end
|
|
|
|
after(:each) do
|
|
destroy_full_visualization(@map, @table, @table_visualization, @map_visualization)
|
|
end
|
|
|
|
describe 'POST add_like' do
|
|
it 'triggers error 403 if not authenticated' do
|
|
post api_v1_visualizations_add_like_url(user_domain: @user_domain, id: @vis.id, api_key: 'foo')
|
|
expect(last_response.status).to eq(403)
|
|
end
|
|
|
|
it 'triggers error 403 if dont have at least read permission' do
|
|
post api_v1_visualizations_add_like_url(user_domain: @user_domain, id: @vis_user2.id, api_key: 'foo')
|
|
expect(last_response.status).to eq(403)
|
|
end
|
|
|
|
it 'add likes to a given visualization' do
|
|
post api_v1_visualizations_add_like_url(user_domain: @user_domain, id: @vis.id, api_key: @carto_user1.api_key)
|
|
|
|
expect(last_response.status).to eq(200)
|
|
end
|
|
|
|
it 'returns an error if you try to like twice a visualization' do
|
|
post api_v1_visualizations_add_like_url(user_domain: @user_domain, id: @vis.id, api_key: @carto_user1.api_key)
|
|
|
|
expect(last_response.status).to eq(200)
|
|
|
|
post api_v1_visualizations_add_like_url(user_domain: @user_domain, id: @vis.id, api_key: @carto_user1.api_key)
|
|
|
|
expect(last_response.status).to eq(400)
|
|
expect(JSON.parse(last_response.body)['text']).to eq("You've already favorited this visualization")
|
|
end
|
|
end
|
|
|
|
describe 'POST remove_like' do
|
|
it 'triggers error 403 if not authenticated' do
|
|
delete api_v1_visualizations_remove_like_url(user_domain: @user_domain, id: @vis.id, api_key: 'foo')
|
|
expect(last_response.status).to eq(403)
|
|
end
|
|
|
|
it 'triggers error 403 if you dont have at least read permission' do
|
|
delete api_v1_visualizations_remove_like_url(user_domain: @user_domain, id: @vis_user2.id, api_key: 'foo')
|
|
expect(last_response.status).to eq(403)
|
|
end
|
|
|
|
it 'removes a like from a given visualization' do
|
|
@vis.add_like_from(@carto_user1)
|
|
|
|
delete api_v1_visualizations_remove_like_url(user_domain: @user_domain, id: @vis.id, api_key: @carto_user1.api_key)
|
|
|
|
expect(last_response.status).to eq(200)
|
|
|
|
get api_v1_visualizations_show_url(user_domain: @user_domain, id: @vis.id, api_key: @carto_user1.api_key, show_liked: true)
|
|
expect(last_response.status).to eq(200)
|
|
expect(JSON.parse(last_response.body).fetch('liked')).to eq(false)
|
|
end
|
|
|
|
it 'does not returns error if you try to remove a non-existent like' do
|
|
delete api_v1_visualizations_remove_like_url(user_domain: @user_domain, id: @vis.id, api_key: @carto_user1.api_key, show_liked: true)
|
|
|
|
expect(last_response.status).to eq(200)
|
|
expect(JSON.parse(last_response.body).fetch('liked')).to eq(false)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
describe 'tests visualization likes endpoints in organizations' do
|
|
include_context 'organization with users helper'
|
|
|
|
describe 'PUT notify_watching' do
|
|
it 'adds the user to the watching list' do
|
|
vis = FactoryGirl.create(:carto_visualization, user: @carto_org_user_1)
|
|
user_domain = @carto_org_user_1.username
|
|
|
|
put api_v1_visualizations_notify_watching_url(user_domain: user_domain, id: vis.id, api_key: @carto_org_user_1.api_key)
|
|
expect(last_response.status).to eq(200)
|
|
expect(JSON.parse(last_response.body)).to eq([@carto_org_user_1.username])
|
|
end
|
|
|
|
it 'returns 403 if user does not have read permissions on the visualization' do
|
|
private_vis = FactoryGirl.create(:carto_visualization, user: @carto_org_user_1, privacy: Carto::Visualization::PRIVACY_PRIVATE)
|
|
|
|
put api_v1_visualizations_notify_watching_url(user_domain: @carto_org_user_2.username, id: private_vis.id, api_key: @carto_org_user_2.api_key)
|
|
expect(last_response.status).to eq(403)
|
|
end
|
|
end
|
|
|
|
describe 'GET list_watching' do
|
|
it 'returns the users currently on the watching list' do
|
|
vis = FactoryGirl.create(:carto_visualization, user: @carto_org_user_1)
|
|
user_domain = @carto_org_user_1.username
|
|
|
|
get api_v1_visualizations_notify_watching_url(user_domain: user_domain, id: vis.id, api_key: @carto_org_user_1.api_key)
|
|
expect(last_response.status).to eq(200)
|
|
expect(JSON.parse(last_response.body)).to eq([])
|
|
|
|
put api_v1_visualizations_notify_watching_url(user_domain: user_domain, id: vis.id, api_key: @carto_org_user_1.api_key)
|
|
expect(last_response.status).to eq(200)
|
|
|
|
get api_v1_visualizations_notify_watching_url(user_domain: user_domain, id: vis.id, api_key: @carto_org_user_1.api_key)
|
|
expect(last_response.status).to eq(200)
|
|
expect(JSON.parse(last_response.body)).to eq([@carto_org_user_1.username])
|
|
end
|
|
|
|
it 'returns 403 if user does not have read permissions on the visualization' do
|
|
private_vis = FactoryGirl.create(:carto_visualization, user: @carto_org_user_1, privacy: Carto::Visualization::PRIVACY_PRIVATE)
|
|
|
|
get api_v1_visualizations_notify_watching_url(user_domain: @carto_org_user_2.username, id: private_vis.id, api_key: @carto_org_user_2.api_key)
|
|
expect(last_response.status).to eq(403)
|
|
end
|
|
end
|
|
|
|
it 'tests totals calculations' do
|
|
bypass_named_maps
|
|
|
|
# user 1 will have 1 table and 1 vis
|
|
# user 2 will have 2 of each
|
|
# user 2 will share 1 table and 1 vis with the org
|
|
# user 2 will share the other table and other vis with user 1
|
|
|
|
table = create_table(privacy: UserTable::PRIVACY_PUBLIC, name: unique_name('table'), user_id: @org_user_1.id)
|
|
u1_t_1_id = table.table_visualization.id
|
|
|
|
table = create_table(privacy: UserTable::PRIVACY_PUBLIC, name: unique_name('table'), user_id: @org_user_2.id)
|
|
u2_t_1_id = table.table_visualization.id
|
|
u2_t_1_perm_id = table.table_visualization.permission.id
|
|
|
|
table = create_table(privacy: UserTable::PRIVACY_PUBLIC, name: unique_name('table'), user_id: @org_user_2.id)
|
|
u2_t_2 = table
|
|
u2_t_2_id = table.table_visualization.id
|
|
u2_t_2_perm_id = table.table_visualization.permission.id
|
|
|
|
post api_v1_visualizations_create_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key),
|
|
factory(@org_user_1).to_json, @headers
|
|
last_response.status.should == 200
|
|
u1_vis_1_id = JSON.parse(last_response.body).fetch('id')
|
|
|
|
post api_v1_visualizations_create_url(user_domain: @org_user_2.username, api_key: @org_user_2.api_key),
|
|
factory(@org_user_2).to_json, @headers
|
|
last_response.status.should == 200
|
|
u2_vis_1_id = JSON.parse(last_response.body).fetch('id')
|
|
u2_vis_1_perm_id = JSON.parse(last_response.body).fetch('permission').fetch('id')
|
|
|
|
post api_v1_visualizations_create_url(user_domain: @org_user_2.username, api_key: @org_user_2.api_key),
|
|
factory(@org_user_2).to_json, @headers
|
|
last_response.status.should == 200
|
|
u2_vis_2_id = JSON.parse(last_response.body).fetch('id')
|
|
u2_vis_2_perm_id = JSON.parse(last_response.body).fetch('permission').fetch('id')
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
body['total_shared'].should eq 0
|
|
body['total_locked'].should eq 0
|
|
vis = body['visualizations'].first
|
|
vis['id'].should eq u1_t_1_id
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 1
|
|
body['total_shared'].should eq 0
|
|
body['total_locked'].should eq 0
|
|
vis = body['visualizations'].first
|
|
vis['id'].should eq u1_vis_1_id
|
|
|
|
# Share u2 vis1 with organization
|
|
put api_v1_permissions_update_url(user_domain: @org_user_2.username, api_key: @org_user_2.api_key, id: u2_vis_1_perm_id),
|
|
{acl: [{
|
|
type: CartoDB::Permission::TYPE_ORGANIZATION,
|
|
entity: {
|
|
id: @organization.id,
|
|
},
|
|
access: CartoDB::Permission::ACCESS_READONLY
|
|
}]}.to_json, @headers
|
|
last_response.status.should == 200
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 2
|
|
body['total_shared'].should eq 1
|
|
body['total_locked'].should eq 0
|
|
|
|
# Share u2 vis2 with u1
|
|
put api_v1_permissions_update_url(user_domain: @org_user_2.username, api_key: @org_user_2.api_key, id: u2_vis_2_perm_id),
|
|
{acl: [{
|
|
type: CartoDB::Permission::TYPE_USER,
|
|
entity: {
|
|
id: @org_user_1.id,
|
|
},
|
|
access: CartoDB::Permission::ACCESS_READONLY
|
|
}]}.to_json, @headers
|
|
last_response.status.should == 200
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 3
|
|
body['total_shared'].should eq 2
|
|
body['total_locked'].should eq 0
|
|
|
|
post api_v1_visualizations_add_like_url(user_domain: @org_user_1.username, id: u1_vis_1_id, api_key: @org_user_1.api_key)
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 3
|
|
body['total_shared'].should eq 2
|
|
body['total_locked'].should eq 0
|
|
|
|
# Multiple likes to same vis shouldn't increment total as is per vis
|
|
post api_v1_visualizations_add_like_url(user_domain: @org_user_1.username, id: u1_vis_1_id, api_key: @org_user_1.api_key)
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_DERIVED, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 3
|
|
body['total_shared'].should eq 2
|
|
body['total_locked'].should eq 0
|
|
|
|
|
|
# Share u2 table1 with org
|
|
put api_v1_permissions_update_url(user_domain:@org_user_2.username, api_key: @org_user_2.api_key, id: u2_t_1_perm_id),
|
|
{acl: [{
|
|
type: CartoDB::Permission::TYPE_ORGANIZATION,
|
|
entity: {
|
|
id: @organization.id,
|
|
},
|
|
access: CartoDB::Permission::ACCESS_READONLY
|
|
}]}.to_json, @headers
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 2
|
|
body['total_shared'].should eq 1
|
|
body['total_locked'].should eq 0
|
|
|
|
# Share u2 table2 with org
|
|
put api_v1_permissions_update_url(user_domain:@org_user_2.username, api_key: @org_user_2.api_key, id: u2_t_2_perm_id),
|
|
{acl: [{
|
|
type: CartoDB::Permission::TYPE_USER,
|
|
entity: {
|
|
id: @org_user_1.id,
|
|
},
|
|
access: CartoDB::Permission::ACCESS_READONLY
|
|
}]}.to_json, @headers
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 3
|
|
body['total_shared'].should eq 2
|
|
body['total_locked'].should eq 0
|
|
body['visualizations'][0]['table']['name'].should == "\"#{@org_user_2.database_schema}\".#{u2_t_2.name}"
|
|
|
|
post api_v1_visualizations_add_like_url(user_domain: @org_user_1.username, id: u1_t_1_id, api_key: @org_user_1.api_key)
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 3
|
|
body['total_shared'].should eq 2
|
|
body['total_locked'].should eq 0
|
|
|
|
# Multiple likes to same table shouldn't increment total as is per vis
|
|
post api_v1_visualizations_add_like_url(user_domain: @org_user_1.username, id: u1_t_1_id, api_key: @org_user_2.api_key)
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 3
|
|
body['total_shared'].should eq 2
|
|
body['total_locked'].should eq 0
|
|
end
|
|
|
|
end
|
|
|
|
describe 'index endpoint' do
|
|
|
|
it 'tests normal users authenticated and unauthenticated calls' do
|
|
bypass_named_maps
|
|
|
|
collection = CartoDB::Visualization::Collection.new.fetch(user_id: @user_2.id)
|
|
collection.map(&:delete)
|
|
|
|
post api_v1_visualizations_create_url(user_domain: @user_2.username, api_key: @user_2.api_key),
|
|
factory(@user_2).to_json, @headers
|
|
last_response.status.should == 200
|
|
pub_vis_id = JSON.parse(last_response.body).fetch('id')
|
|
|
|
put api_v1_visualizations_update_url(user_domain: @user_2.username, api_key: @user_2.api_key, id: pub_vis_id),
|
|
{ id: pub_vis_id, privacy: CartoDB::Visualization::Member::PRIVACY_PUBLIC }.to_json,
|
|
@headers
|
|
last_response.status.should == 200
|
|
|
|
post api_v1_visualizations_create_url(user_domain: @user_2.username, api_key: @user_2.api_key),
|
|
factory(@user_2).to_json, @headers
|
|
last_response.status.should == 200
|
|
priv_vis_id = JSON.parse(last_response.body).fetch('id')
|
|
|
|
put api_v1_visualizations_update_url(user_domain: @user_2.username, api_key: @user_2.api_key, id: priv_vis_id),
|
|
{ id: priv_vis_id, privacy: CartoDB::Visualization::Member::PRIVACY_PRIVATE }.to_json,
|
|
@headers
|
|
last_response.status.should == 200
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @user_2.username, type: 'derived'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
|
|
body['total_entries'].should eq 1
|
|
vis = body['visualizations'].first
|
|
vis['id'].should eq pub_vis_id
|
|
vis['privacy'].should eq CartoDB::Visualization::Member::PRIVACY_PUBLIC.upcase
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @user_2.username, type: 'derived', api_key: @user_2.api_key,
|
|
order: 'updated_at'),
|
|
{}, @headers
|
|
body = JSON.parse(last_response.body)
|
|
|
|
body['total_entries'].should eq 2
|
|
vis = body['visualizations'][0]
|
|
vis['id'].should eq priv_vis_id
|
|
vis['privacy'].should eq CartoDB::Visualization::Member::PRIVACY_PRIVATE.upcase
|
|
vis = body['visualizations'][1]
|
|
vis['id'].should eq pub_vis_id
|
|
vis['privacy'].should eq CartoDB::Visualization::Member::PRIVACY_PUBLIC.upcase
|
|
end
|
|
|
|
it 'tests organization users authenticated and unauthenticated calls' do
|
|
bypass_named_maps
|
|
|
|
organization = test_organization.save
|
|
|
|
user_2 = create_user(
|
|
username: unique_name('user'),
|
|
email: unique_email,
|
|
password: 'clientex'
|
|
)
|
|
|
|
user_org = CartoDB::UserOrganization.new(organization.id, user_2.id)
|
|
user_org.promote_user_to_admin
|
|
organization.reload
|
|
user_2.reload
|
|
|
|
post "http://#{organization.name}.cartodb.test#{api_v1_visualizations_create_path(user_domain: user_2.username,
|
|
api_key: user_2.api_key)}",
|
|
factory(user_2).to_json, @headers
|
|
last_response.status.should == 200
|
|
pub_vis_id = JSON.parse(last_response.body).fetch('id')
|
|
|
|
put "http://#{organization.name}.cartodb.test#{api_v1_visualizations_update_path(user_domain: user_2.username,
|
|
api_key: user_2.api_key,
|
|
id: pub_vis_id)}",
|
|
{
|
|
id: pub_vis_id,
|
|
privacy: CartoDB::Visualization::Member::PRIVACY_PUBLIC
|
|
}.to_json, @headers
|
|
last_response.status.should == 200
|
|
|
|
post "http://#{organization.name}.cartodb.test#{api_v1_visualizations_create_path(user_domain: user_2.username,
|
|
api_key: user_2.api_key)}",
|
|
factory(user_2).to_json, @headers
|
|
last_response.status.should == 200
|
|
priv_vis_id = JSON.parse(last_response.body).fetch('id')
|
|
|
|
put "http://#{organization.name}.cartodb.test#{api_v1_visualizations_update_path(user_domain: user_2.username,
|
|
api_key: user_2.api_key,
|
|
id: priv_vis_id)}",
|
|
{
|
|
id: priv_vis_id,
|
|
privacy: CartoDB::Visualization::Member::PRIVACY_PRIVATE
|
|
}.to_json, @headers
|
|
last_response.status.should == 200
|
|
|
|
get "http://#{organization.name}.cartodb.test#{api_v1_visualizations_index_path(user_domain: user_2.username,
|
|
type: 'derived')}", @headers
|
|
body = JSON.parse(last_response.body)
|
|
|
|
body['total_entries'].should eq 1
|
|
vis = body['visualizations'].first
|
|
vis['id'].should eq pub_vis_id
|
|
vis['privacy'].should eq CartoDB::Visualization::Member::PRIVACY_PUBLIC.upcase
|
|
|
|
|
|
get "http://#{organization.name}.cartodb.test#{api_v1_visualizations_index_path(user_domain: user_2.username,
|
|
api_key: user_2.api_key,
|
|
type: 'derived',
|
|
order: 'updated_at')}", {}, @headers
|
|
body = JSON.parse(last_response.body)
|
|
|
|
body['total_entries'].should eq 2
|
|
vis = body['visualizations'][0]
|
|
vis['id'].should eq priv_vis_id
|
|
vis['privacy'].should eq CartoDB::Visualization::Member::PRIVACY_PRIVATE.upcase
|
|
vis = body['visualizations'][1]
|
|
vis['id'].should eq pub_vis_id
|
|
vis['privacy'].should eq CartoDB::Visualization::Member::PRIVACY_PUBLIC.upcase
|
|
|
|
user_2.destroy
|
|
end
|
|
end
|
|
|
|
describe 'GET /api/v1/viz' do
|
|
before(:each) do
|
|
bypass_named_maps
|
|
delete_user_data(@user_1)
|
|
end
|
|
|
|
it 'retrieves a collection of visualizations' do
|
|
payload = factory(@user_1)
|
|
post api_v1_visualizations_create_url(api_key: @api_key), payload.to_json, @headers
|
|
id = JSON.parse(last_response.body).fetch('id')
|
|
|
|
get api_v1_visualizations_index_url(api_key: @api_key),
|
|
{}, @headers
|
|
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.first.fetch('id').should == id
|
|
end
|
|
|
|
it 'is updated after creating a visualization' do
|
|
payload = factory(@user_1)
|
|
post api_v1_visualizations_create_url(api_key: @api_key),
|
|
payload.to_json, @headers
|
|
|
|
get api_v1_visualizations_index_url(api_key: @api_key),
|
|
{}, @headers
|
|
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.size.should == 1
|
|
|
|
payload = factory(@user_1).merge('name' => 'another one')
|
|
post api_v1_visualizations_create_url(api_key: @api_key),
|
|
payload.to_json, @headers
|
|
|
|
get api_v1_visualizations_index_url(api_key: @api_key),
|
|
{}, @headers
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.size.should == 2
|
|
end
|
|
|
|
it 'is updated after deleting a visualization' do
|
|
payload = factory(@user_1)
|
|
post api_v1_visualizations_create_url(api_key: @api_key),
|
|
payload.to_json, @headers
|
|
id = JSON.parse(last_response.body).fetch('id')
|
|
|
|
get api_v1_visualizations_index_url(api_key: @api_key),
|
|
{}, @headers
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.should_not be_empty
|
|
|
|
delete api_v1_visualizations_destroy_url(id: id, api_key: @api_key),
|
|
{ id: id }.to_json,
|
|
@headers
|
|
get api_v1_visualizations_index_url(api_key: @api_key),
|
|
{}, @headers
|
|
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.should be_empty
|
|
end
|
|
|
|
it 'paginates results' do
|
|
per_page = 10
|
|
total_entries = 20
|
|
|
|
total_entries.times do
|
|
post api_v1_visualizations_index_url(api_key: @api_key),
|
|
factory(@user_1).to_json, @headers
|
|
end
|
|
|
|
get api_v1_visualizations_index_url(api_key: @api_key, page: 1, per_page: per_page), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should == per_page
|
|
response.fetch('total_entries').should == total_entries
|
|
end
|
|
|
|
it 'returns filtered results' do
|
|
post api_v1_visualizations_create_url(api_key: @api_key),
|
|
factory(@user_1).to_json, @headers
|
|
|
|
get api_v1_visualizations_index_url(api_key: @api_key, type: 'table'),
|
|
{}, @headers
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.should be_empty
|
|
|
|
post api_v1_visualizations_create_url(api_key: @api_key),
|
|
factory(@user_1).to_json, @headers
|
|
post api_v1_visualizations_create_url(api_key: @api_key),
|
|
factory(@user_1).merge(type: 'table').to_json, @headers
|
|
get api_v1_visualizations_index_url(api_key: @api_key, type: 'derived'),
|
|
{}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.size.should == 2
|
|
end
|
|
|
|
it 'creates a visualization from a list of tables' do
|
|
bypass_named_maps
|
|
table1 = table_factory
|
|
table2 = table_factory
|
|
table3 = table_factory
|
|
|
|
payload = {
|
|
name: 'new visualization',
|
|
tables: [
|
|
table1.fetch('name'),
|
|
table2.fetch('name'),
|
|
table3.fetch('name')
|
|
],
|
|
privacy: 'public'
|
|
}
|
|
|
|
post api_v1_visualizations_create_url(api_key: @api_key),
|
|
payload.to_json, @headers
|
|
last_response.status.should == 200
|
|
|
|
visualization = JSON.parse(last_response.body)
|
|
|
|
# TODO: this endpoint doesn't exist now. Current replacement?
|
|
#get "/api/v1/viz/#{visualization.fetch('id')}/viz?api_key=#{@api_key}",
|
|
# {}, @headers
|
|
#last_response.status.should == 403
|
|
|
|
get api_v2_visualizations_vizjson_url(id: visualization.fetch('id'), api_key: @api_key),
|
|
{}, @headers
|
|
last_response.status.should == 200
|
|
|
|
# include overlays
|
|
|
|
get overlays_url(visualization_id: visualization.fetch('id'), api_key: @api_key),
|
|
{}, @headers
|
|
last_response.status.should == 200
|
|
overlays = JSON.parse(last_response.body)
|
|
overlays.length.should == 5
|
|
end
|
|
|
|
end
|
|
|
|
describe 'GET /api/v1/viz/:id' do
|
|
before(:each) do
|
|
@carto_user1.private_maps_enabled = true
|
|
@carto_user1.save
|
|
|
|
bypass_named_maps
|
|
delete_user_data(@user_1)
|
|
@map, @table, @table_visualization, @visualization = create_full_builder_vis(@carto_user1,
|
|
visualization_attributes:
|
|
{
|
|
tags: ['foo'],
|
|
description: 'dull desc'
|
|
})
|
|
end
|
|
|
|
after(:each) do
|
|
destroy_full_visualization(@map, @table, @table_visualization, @visualization)
|
|
end
|
|
|
|
it 'returns a visualization' do
|
|
get api_v1_visualizations_show_url(id: @visualization.id, api_key: @api_key), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
|
|
response.fetch('id') .should_not be_nil
|
|
response.fetch('map_id') .should_not be_nil
|
|
response.fetch('tags') .should_not be_empty
|
|
response.fetch('description') .should_not be_nil
|
|
response.fetch('related_tables') .should_not be_nil
|
|
|
|
# Optional information
|
|
response['likes'].should eq nil
|
|
end
|
|
|
|
it 'returns a specific error for password-protected visualizations and required, public information' do
|
|
@visualization.privacy = Carto::Visualization::PRIVACY_PROTECTED
|
|
@visualization.password = 'wadus'
|
|
@visualization.save!
|
|
@visualization.user.update_attribute(:google_maps_key, 'waaaaadus')
|
|
|
|
expected_visualization_info = {
|
|
privacy: @visualization.privacy,
|
|
user: {
|
|
google_maps_query_string: @visualization.user.google_maps_query_string
|
|
}
|
|
}
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id) do |response|
|
|
response.status.should eq 403
|
|
response.body[:errors].should eq "Visualization not viewable"
|
|
response.body[:errors_cause].should eq "privacy_password"
|
|
response.body[:visualization].should eq expected_visualization_info
|
|
end
|
|
end
|
|
|
|
it 'doesn\'t return a specific error for private visualizations' do
|
|
@visualization.privacy = Carto::Visualization::PRIVACY_PRIVATE
|
|
@visualization.save!
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id) do |response|
|
|
response.status.should eq 403
|
|
response.body[:errors].should eq "Visualization not viewable"
|
|
response.body[:errors_cause].should be_nil
|
|
end
|
|
end
|
|
|
|
it 'returns a visualization with optional information if requested' do
|
|
url = api_v1_visualizations_show_url(
|
|
id: @visualization.id,
|
|
api_key: @api_key,
|
|
show_liked: true
|
|
)
|
|
|
|
get url, {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
|
|
response['liked'].should eq false
|
|
end
|
|
|
|
it 'returns related_canonical_visualizations if requested' do
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id, api_key: @carto_user1.api_key) do |response|
|
|
response.status.should eq 200
|
|
response.body[:related_canonical_visualizations].should be_nil
|
|
end
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id,
|
|
api_key: @carto_user1.api_key,
|
|
fetch_related_canonical_visualizations: true) do |response|
|
|
response.status.should eq 200
|
|
related_canonical = response.body[:related_canonical_visualizations]
|
|
related_canonical.should_not be_nil
|
|
related_canonical.count.should eq 1
|
|
related_canonical[0][:id].should eq @table_visualization.id
|
|
end
|
|
end
|
|
|
|
it 'returns private related_canonical_visualizations for users that can see it' do
|
|
related_canonical_visualizations = @visualization.related_canonical_visualizations
|
|
related_canonical_visualizations.should_not be_empty
|
|
old_privacies = related_canonical_visualizations.map(&:privacy)
|
|
related_canonical_visualizations.each do |v|
|
|
v.update_attribute(:privacy, Carto::Visualization::PRIVACY_PRIVATE)
|
|
end
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id,
|
|
api_key: @carto_user1.api_key,
|
|
fetch_related_canonical_visualizations: true) do |response|
|
|
response.status.should eq 200
|
|
related_canonical = response.body[:related_canonical_visualizations]
|
|
related_canonical.count.should eq related_canonical_visualizations.count
|
|
response.body[:related_canonical_visualizations_count].should eq related_canonical_visualizations.count
|
|
end
|
|
|
|
related_canonical_visualizations.zip(old_privacies).each do |v, p|
|
|
v.update_attribute(:privacy, p)
|
|
end
|
|
end
|
|
|
|
it 'returns private information about the user if requested' do
|
|
url = api_v1_visualizations_show_url(id: @visualization.id, api_key: @carto_user1.api_key)
|
|
get_json url do |response|
|
|
response.status.should eq 200
|
|
response.body[:user].should be_nil
|
|
end
|
|
|
|
get_json url, fetch_user: true do |response|
|
|
response.status.should eq 200
|
|
user = response.body[:user]
|
|
user.should_not be_nil
|
|
user[:avatar_url].should_not be_nil
|
|
user[:quota_in_bytes].should_not be_nil
|
|
end
|
|
end
|
|
|
|
# This is a contrast to the anonymous use case. A public endpoint shouldn't hit the user DB to avoid
|
|
# workers locks if user DB is under heavy load. Nevertheless, the private one can (and needs).
|
|
describe 'user db connectivity issues' do
|
|
before(:each) do
|
|
@actual_database_name = @visualization.user.database_name
|
|
@visualization.user.update_attribute(:database_name, 'wadus')
|
|
end
|
|
|
|
after(:each) do
|
|
@visualization.user.update_attribute(:database_name, @actual_database_name)
|
|
end
|
|
|
|
it 'needs connection to the user db if the viewer is the owner' do
|
|
CartoDB::Logger.expects(:error).once.with do |e|
|
|
e[:exception].message.should eq "FATAL: database \"#{@visualization.user.database_name}\" does not exist\n"
|
|
end
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id),
|
|
api_key: @visualization.user.api_key,
|
|
fetch_related_canonical_visualizations: true,
|
|
fetch_user: true,
|
|
show_liked: true,
|
|
show_permission: true,
|
|
show_auth_tokens: true,
|
|
show_stats: true do |response|
|
|
# We currently log 404 on errors. Maybe something that we should change in the future...
|
|
response.status.should == 404
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'to anonymous users' do
|
|
it 'returns a 403 on private visualizations' do
|
|
@visualization.privacy = Carto::Visualization::PRIVACY_PRIVATE
|
|
@visualization.save!
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id) do |response|
|
|
response.status.should eq 403
|
|
expect(response.body[:errors]).to eq('Visualization not viewable')
|
|
end
|
|
end
|
|
|
|
describe 'publically accessible visualizations' do
|
|
before(:each) do
|
|
@visualization.privacy = Carto::Visualization::PRIVACY_LINK
|
|
@visualization.save!
|
|
end
|
|
|
|
it 'returns 403 for unpublished visualizations' do
|
|
@visualization.published?.should eq false
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id) do |response|
|
|
response.status.should eq 403
|
|
expect(response.body[:errors]).to eq('Visualization not viewable')
|
|
end
|
|
end
|
|
|
|
it 'returns 403 for unpublished and password protected visualizations' do
|
|
@visualization.published?.should eq false
|
|
@visualization.privacy = Carto::Visualization::PRIVACY_PROTECTED
|
|
@visualization.password = 'wadus'
|
|
@visualization.save!
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id) do |response|
|
|
response.status.should eq 403
|
|
expect(response.body[:errors]).to eq('Visualization not viewable')
|
|
end
|
|
end
|
|
|
|
describe 'published visualizations' do
|
|
before(:each) do
|
|
@visualization.create_mapcap!
|
|
@visualization.published?.should eq true
|
|
end
|
|
|
|
it 'only returns public information' do
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id) do |response|
|
|
response.status.should eq 200
|
|
response.body[:name].should_not be_nil
|
|
response.body[:updated_at].should_not be_nil
|
|
response.body[:tags].should_not be_nil
|
|
response.body[:title].should_not be_nil
|
|
response.body[:description].should_not be_nil
|
|
|
|
# Optional information requiring parameters
|
|
response.body[:user].should eq nil
|
|
response.body[:liked].should eq nil
|
|
response.body[:stats].should be_empty
|
|
response.body[:auth_tokens].should be_empty
|
|
response.body[:permission].should eq nil
|
|
end
|
|
end
|
|
|
|
it 'only returns public information, including optional if requested' do
|
|
url = api_v1_visualizations_show_url(
|
|
id: @visualization.id,
|
|
show_liked: true,
|
|
show_permission: true,
|
|
show_auth_tokens: true,
|
|
show_stats: true,
|
|
fetch_user: true,
|
|
show_user_basemaps: true
|
|
)
|
|
|
|
get_json url do |response|
|
|
response.status.should eq 200
|
|
response.body[:user].should_not be_nil
|
|
response.body[:liked].should eq false
|
|
response.body[:stats].should_not be_empty
|
|
|
|
response_user = response.body[:user]
|
|
response_user[:basemaps].should be_nil # Even if requested, because it's not public
|
|
|
|
permission = response.body[:permission]
|
|
permission.should_not eq nil
|
|
permission[:owner].should_not eq nil
|
|
owner = permission[:owner]
|
|
user = @visualization.permission.owner
|
|
owner[:name].should eq user.name
|
|
owner[:last_name].should eq user.last_name
|
|
owner[:username].should eq user.username
|
|
owner[:avatar_url].should eq user.avatar_url
|
|
owner[:base_url].should eq user.public_url
|
|
owner[:org_admin].should eq user.org_admin
|
|
owner[:org_user].should eq user.organization_id.present?
|
|
end
|
|
end
|
|
|
|
it 'not only returns public information for authenticated requests' do
|
|
url = api_v1_visualizations_show_url(
|
|
id: @visualization.id,
|
|
fetch_user: true,
|
|
show_user_basemaps: true,
|
|
api_key: @visualization.user.api_key
|
|
)
|
|
|
|
get_json url do |response|
|
|
response.status.should eq 200
|
|
response.body[:user].should_not be_nil
|
|
|
|
response_user = response.body[:user]
|
|
response_user[:basemaps].should_not be_empty
|
|
end
|
|
end
|
|
|
|
it 'returns auth_tokens for password protected visualizations if correct password is provided' do
|
|
password = 'wadus'
|
|
@visualization.privacy = Carto::Visualization::PRIVACY_PROTECTED
|
|
@visualization.password = password
|
|
@visualization.save!
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id) do |response|
|
|
response.status.should eq 403
|
|
expect(response.body[:errors]).to eq('Visualization not viewable')
|
|
end
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id, password: password * 2) do |response|
|
|
response.status.should eq 403
|
|
expect(response.body[:errors]).to eq('Visualization not viewable')
|
|
end
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id, password: password) do |response|
|
|
response.status.should eq 200
|
|
response.body[:auth_tokens].should_not be_nil
|
|
end
|
|
end
|
|
|
|
it 'returns auth_tokens for password protected visualizations if requested by the owner' do
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id) do |response|
|
|
response.status.should eq 200
|
|
response.body[:auth_tokens].should_not be_nil
|
|
end
|
|
end
|
|
|
|
it 'returns public information about the user if requested' do
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id) do |response|
|
|
response.status.should eq 200
|
|
response.body[:user].should be_nil
|
|
end
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id), fetch_user: true do |response|
|
|
@visualization.user.update_attribute(:google_maps_key, 'waaaaadus')
|
|
|
|
response.status.should eq 200
|
|
user = response.body[:user]
|
|
user.should_not be_nil
|
|
user[:avatar_url].should_not be_nil
|
|
user[:quota_in_bytes].should be_nil
|
|
user[:google_maps_query_string].should_not be_nil
|
|
user[:google_maps_query_string].should eq @visualization.user.google_maps_query_string
|
|
end
|
|
end
|
|
|
|
it 'returns related_canonical_visualizations if requested' do
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id) do |response|
|
|
response.status.should eq 200
|
|
response.body[:related_canonical_visualizations].should be_nil
|
|
end
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id,
|
|
fetch_related_canonical_visualizations: true) do |response|
|
|
response.status.should eq 200
|
|
related_canonical = response.body[:related_canonical_visualizations]
|
|
related_canonical.should_not be_nil
|
|
related_canonical.count.should eq 1
|
|
related_canonical[0][:id].should eq @table_visualization.id
|
|
end
|
|
end
|
|
|
|
it 'doesn\'t return private related_canonical_visualizations' do
|
|
related_canonical_visualizations = @visualization.related_canonical_visualizations
|
|
related_canonical_visualizations.should_not be_empty
|
|
old_privacies = related_canonical_visualizations.map(&:privacy)
|
|
related_canonical_visualizations.each do |v|
|
|
v.update_attribute(:privacy, Carto::Visualization::PRIVACY_PRIVATE)
|
|
end
|
|
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id,
|
|
fetch_related_canonical_visualizations: true) do |response|
|
|
response.status.should eq 200
|
|
related_canonical = response.body[:related_canonical_visualizations]
|
|
related_canonical.count.should eq 0
|
|
response.body[:related_canonical_visualizations_count].should eq related_canonical_visualizations.count
|
|
end
|
|
|
|
related_canonical_visualizations.zip(old_privacies).each do |v, p|
|
|
v.update_attribute(:privacy, p)
|
|
end
|
|
end
|
|
|
|
describe 'user db connectivity issues' do
|
|
before(:each) do
|
|
@actual_database_name = @visualization.user.database_name
|
|
@visualization.user.update_attribute(:database_name, 'wadus')
|
|
end
|
|
|
|
after(:each) do
|
|
@visualization.user.update_attribute(:database_name, @actual_database_name)
|
|
end
|
|
|
|
it 'does not need connection to the user db if viewer is anonymous' do
|
|
CartoDB::Logger.expects(:warning).never
|
|
CartoDB::Logger.expects(:error).never
|
|
get_json api_v1_visualizations_show_url(id: @visualization.id),
|
|
fetch_related_canonical_visualizations: true,
|
|
fetch_user: true,
|
|
show_liked: true,
|
|
show_permission: true,
|
|
show_stats: true do |response|
|
|
response.status.should == 200
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# Specific tests for vizjson 3. Common are at `vizjson_shared_examples`
|
|
describe '#vizjson3' do
|
|
include Fixtures::Layers::Infowindows
|
|
include Fixtures::Layers::Tooltips
|
|
include Carto::Factories::Visualizations
|
|
|
|
include_context 'visualization creation helpers'
|
|
|
|
def get_vizjson3_url(user, visualization)
|
|
args = { user_domain: user.username, id: visualization.id, api_key: user.api_key }
|
|
api_v3_visualizations_vizjson_url(args)
|
|
end
|
|
|
|
def first_layer_definition_from_response(response)
|
|
index = response.body[:layers].index { |l| l[:options] && l[:options][:layer_definition] }
|
|
response.body[:layers][index][:options][:layer_definition]
|
|
end
|
|
|
|
def first_layer_named_map_from_response(response)
|
|
index = response.body[:layers].index { |l| l[:options] && l[:options][:named_map] }
|
|
response.body[:layers][index][:options][:named_map]
|
|
end
|
|
|
|
def first_data_layer_from_response(response)
|
|
index = response.body[:layers].index { |l| l[:type] == 'CartoDB' }
|
|
response.body[:layers][index]
|
|
end
|
|
|
|
let(:infowindow) do
|
|
FactoryGirl.build_stubbed(:carto_layer_with_infowindow).infowindow
|
|
end
|
|
|
|
let(:tooltip) do
|
|
FactoryGirl.build_stubbed(:carto_layer_with_tooltip).tooltip
|
|
end
|
|
|
|
before(:each) do
|
|
@map, @table, @table_visualization, @visualization = create_full_visualization(Carto::User.find(@user_1.id))
|
|
|
|
@table.privacy = UserTable::PRIVACY_PUBLIC
|
|
@table.save
|
|
layer = @visualization.data_layers.first
|
|
layer.infowindow = infowindow
|
|
layer.tooltip = tooltip
|
|
layer.options[:table_name] = @table.name
|
|
layer.options[:query] = "select * from #{@table.name}"
|
|
layer.save
|
|
end
|
|
|
|
after(:each) do
|
|
destroy_full_visualization(@map, @table, @table_visualization, @visualization)
|
|
end
|
|
|
|
describe 'layer templates' do
|
|
describe 'anonymous maps' do
|
|
before(:each) do
|
|
@table.privacy = UserTable::PRIVACY_PUBLIC
|
|
@table.save
|
|
end
|
|
|
|
it 'uses v3 infowindows and tooltips templates removing "table/views/" from template_name' do
|
|
# vizjson v2 doesn't change
|
|
get_json api_v2_visualizations_vizjson_url(user_domain: @user_1.username,
|
|
id: @visualization.id,
|
|
api_key: @user_1.api_key), @headers do |response|
|
|
response.status.should eq 200
|
|
|
|
layer_definition = first_layer_definition_from_response(response)
|
|
response_infowindow = layer_definition[:layers][0][:infowindow]
|
|
response_infowindow[:template_name].should eq infowindow[:template_name]
|
|
response_infowindow[:template].should include(v2_infowindow_light_template_fragment)
|
|
response_infowindow[:template].should_not include(v3_infowindow_light_template_fragment)
|
|
|
|
response_tooltip = layer_definition[:layers][0][:tooltip]
|
|
response_tooltip[:template_name].should eq tooltip[:template_name]
|
|
response_tooltip[:template].should include(v2_tooltip_light_template_fragment)
|
|
response_tooltip[:template].should_not include(v3_tooltip_light_template_fragment)
|
|
|
|
end
|
|
|
|
get_json get_vizjson3_url(@user_1, @visualization), @headers do |response|
|
|
response.status.should eq 200
|
|
|
|
layer = first_data_layer_from_response(response)
|
|
response_infowindow = layer[:infowindow]
|
|
infowindow[:template_name].should eq "table/views/infowindow_light"
|
|
response_infowindow[:template_name].should eq "infowindow_light"
|
|
response_infowindow[:template].should include(v3_infowindow_light_template_fragment)
|
|
response_infowindow[:template].should_not include(v2_infowindow_light_template_fragment)
|
|
|
|
response_tooltip = layer[:tooltip]
|
|
response_tooltip[:template_name].should eq tooltip[:template_name]
|
|
response_tooltip[:template].should include(v3_tooltip_light_template_fragment)
|
|
response_tooltip[:template].should_not include(v2_tooltip_light_template_fragment)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'named maps' do
|
|
before(:each) do
|
|
@user_1.private_tables_enabled = true
|
|
@user_1.save
|
|
@table.user.reload
|
|
@table.privacy = UserTable::PRIVACY_PRIVATE
|
|
@table.save!
|
|
end
|
|
|
|
it 'uses v3 infowindows templates at named maps removing "table/views/" from template_name' do
|
|
# vizjson v2 doesn't change
|
|
get_json api_v2_visualizations_vizjson_url(user_domain: @user_1.username,
|
|
id: @visualization.id,
|
|
api_key: @user_1.api_key), @headers do |response|
|
|
response.status.should eq 200
|
|
|
|
layer_named_map = first_layer_named_map_from_response(response)
|
|
response_infowindow = layer_named_map[:layers][0][:infowindow]
|
|
response_infowindow[:template_name].should eq infowindow[:template_name]
|
|
response_infowindow[:template].should include(v2_infowindow_light_template_fragment)
|
|
response_infowindow[:template].should_not include(v3_infowindow_light_template_fragment)
|
|
|
|
response_tooltip = layer_named_map[:layers][0][:tooltip]
|
|
response_tooltip[:template_name].should eq tooltip[:template_name]
|
|
response_tooltip[:template].should include(v2_tooltip_light_template_fragment)
|
|
response_tooltip[:template].should_not include(v3_tooltip_light_template_fragment)
|
|
end
|
|
|
|
get_json get_vizjson3_url(@user_1, @visualization), @headers do |response|
|
|
response.status.should eq 200
|
|
|
|
layer = first_data_layer_from_response(response)
|
|
response_infowindow = layer[:infowindow]
|
|
infowindow[:template_name].should eq "table/views/infowindow_light"
|
|
response_infowindow[:template_name].should eq 'infowindow_light'
|
|
response_infowindow[:template].should include(v3_infowindow_light_template_fragment)
|
|
response_infowindow[:template].should_not include(v2_infowindow_light_template_fragment)
|
|
|
|
response_tooltip = layer[:tooltip]
|
|
response_tooltip[:template_name].should eq tooltip[:template_name]
|
|
response_tooltip[:template].should include(v3_tooltip_light_template_fragment)
|
|
response_tooltip[:template].should_not include(v2_tooltip_light_template_fragment)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'layer custom infowindows and tooltips' do
|
|
before(:each) do
|
|
layer = @visualization.data_layers.first
|
|
layer.infowindow = custom_infowindow
|
|
layer.tooltip = custom_tooltip
|
|
layer.save
|
|
end
|
|
|
|
describe 'anonymous maps' do
|
|
before(:each) do
|
|
@table.privacy = UserTable::PRIVACY_PUBLIC
|
|
@table.save
|
|
end
|
|
|
|
it 'uses v3 infowindows and tooltips templates' do
|
|
# vizjson v2 doesn't change
|
|
get_json api_v2_visualizations_vizjson_url(user_domain: @user_1.username,
|
|
id: @visualization.id,
|
|
api_key: @user_1.api_key), @headers do |response|
|
|
response.status.should eq 200
|
|
|
|
layer_definition = first_layer_definition_from_response(response)
|
|
response_infowindow = layer_definition[:layers][0][:infowindow]
|
|
response_infowindow[:template_name].should eq ''
|
|
response_infowindow[:template].should eq custom_infowindow[:template]
|
|
|
|
response_tooltip = layer_definition[:layers][0][:tooltip]
|
|
response_tooltip[:template_name].should eq ''
|
|
response_tooltip[:template].should eq custom_tooltip[:template]
|
|
end
|
|
|
|
get_json get_vizjson3_url(@user_1, @visualization), @headers do |response|
|
|
response.status.should eq 200
|
|
|
|
layer = first_data_layer_from_response(response)
|
|
response_infowindow = layer[:infowindow]
|
|
response_infowindow[:template_name].should eq ''
|
|
response_infowindow[:template].should eq custom_infowindow[:template]
|
|
|
|
response_tooltip = layer[:tooltip]
|
|
response_tooltip[:template_name].should eq ''
|
|
response_tooltip[:template].should eq custom_tooltip[:template]
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'named maps' do
|
|
before(:each) do
|
|
@user_1.private_tables_enabled = true
|
|
@user_1.save
|
|
@table.user.reload
|
|
@table.privacy = UserTable::PRIVACY_PRIVATE
|
|
@table.save
|
|
end
|
|
|
|
it 'uses v3 infowindows templates at named maps' do
|
|
# vizjson v2 doesn't change
|
|
get_json api_v2_visualizations_vizjson_url(user_domain: @user_1.username,
|
|
id: @visualization.id,
|
|
api_key: @user_1.api_key), @headers do |response|
|
|
response.status.should eq 200
|
|
|
|
layer_named_map = first_layer_named_map_from_response(response)
|
|
response_infowindow = layer_named_map[:layers][0][:infowindow]
|
|
response_infowindow[:template_name].should eq ''
|
|
response_infowindow[:template].should eq custom_infowindow[:template]
|
|
|
|
response_tooltip = layer_named_map[:layers][0][:tooltip]
|
|
response_tooltip[:template_name].should eq ''
|
|
response_tooltip[:template].should eq custom_tooltip[:template]
|
|
end
|
|
|
|
get_json get_vizjson3_url(@user_1, @visualization), @headers do |response|
|
|
response.status.should eq 200
|
|
|
|
layer = first_data_layer_from_response(response)
|
|
response_infowindow = layer[:infowindow]
|
|
response_infowindow[:template_name].should eq ''
|
|
response_infowindow[:template].should eq custom_infowindow[:template]
|
|
|
|
response_tooltip = layer[:tooltip]
|
|
response_tooltip[:template_name].should eq ''
|
|
response_tooltip[:template].should eq custom_tooltip[:template]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
it 'returns a vizjson with empty widgets array for visualizations without widgets' do
|
|
get_json get_vizjson3_url(@user_1, @visualization), @headers do |response|
|
|
response.status.should == 200
|
|
vizjson3 = response.body
|
|
vizjson3.keys.should include(:widgets)
|
|
vizjson3[:widgets].should == []
|
|
end
|
|
end
|
|
|
|
it 'returns visualization widgets' do
|
|
layer = @visualization.layers.first
|
|
widget = FactoryGirl.create(:widget, layer: layer)
|
|
|
|
widget2 = FactoryGirl.create(:widget_with_layer, type: 'fake')
|
|
|
|
get_json get_vizjson3_url(@user_1, @visualization), @headers do |response|
|
|
response.status.should == 200
|
|
vizjson3 = response.body
|
|
vizjson3[:widgets].length.should == 1
|
|
|
|
vizjson3[:widgets].map { |w| w[:type] }.should include(widget.type)
|
|
vizjson3[:widgets].map { |w| w[:layer_id] }.should include(layer.id)
|
|
|
|
widget2.destroy
|
|
widget.destroy
|
|
end
|
|
end
|
|
|
|
it 'returns datasource' do
|
|
get_json get_vizjson3_url(@user_1, @visualization), @headers do |response|
|
|
response.status.should == 200
|
|
vizjson3 = response.body
|
|
vizjson3[:datasource][:user_name].should == @user_1.username
|
|
vizjson3[:datasource][:maps_api_template].should_not be_nil
|
|
vizjson3[:datasource][:stat_tag].should_not be_nil
|
|
|
|
vizjson3[:user][:fullname].should == (@user_1.name.nil? ? @user_1.username : @user_1.name)
|
|
vizjson3[:user][:avatar_url].should_not be_nil
|
|
end
|
|
end
|
|
|
|
it 'returns datasource.template_name for visualizations with retrieve_named_map? true' do
|
|
Carto::Visualization.any_instance.stubs(:retrieve_named_map?).returns(true)
|
|
get_json get_vizjson3_url(@user_1, @visualization), @headers do |response|
|
|
response.status.should == 200
|
|
vizjson3 = response.body
|
|
vizjson3[:datasource][:template_name].should_not be_nil
|
|
end
|
|
end
|
|
|
|
it 'returns nil datasource.template_name for visualizations with retrieve_named_map? false' do
|
|
Carto::Visualization.any_instance.stubs(:retrieve_named_map?).returns(false)
|
|
get_json get_vizjson3_url(@user_1, @visualization), @headers do |response|
|
|
response.status.should == 200
|
|
vizjson3 = response.body
|
|
vizjson3[:datasource].key?(:template_name).should be_false
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'filters' do
|
|
before(:each) do
|
|
bypass_named_maps
|
|
delete_user_data(@user_1)
|
|
end
|
|
|
|
it 'uses locked filter' do
|
|
bypass_named_maps
|
|
|
|
post api_v1_visualizations_create_url(api_key: @api_key), factory(@user_1, locked: true).to_json, @headers
|
|
vis_1_id = JSON.parse(last_response.body).fetch('id')
|
|
post api_v1_visualizations_create_url(api_key: @api_key), factory(@user_1, locked: false).to_json, @headers
|
|
vis_2_id = JSON.parse(last_response.body).fetch('id')
|
|
|
|
get api_v1_visualizations_index_url(api_key: @api_key, type: 'derived'), {}, @headers
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 2
|
|
|
|
get api_v1_visualizations_index_url(api_key: @api_key, type: 'derived', locked: true), {}, @headers
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 1
|
|
collection.first.fetch('id').should eq vis_1_id
|
|
|
|
get api_v1_visualizations_index_url(api_key: @api_key, type: 'derived', locked: false), {}, @headers
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 1
|
|
collection.first.fetch('id').should eq vis_2_id
|
|
end
|
|
|
|
it 'searches by tag' do
|
|
post api_v1_visualizations_create_url(api_key: @api_key), factory(@user_1, locked: true, tags: ['test1']).to_json, @headers
|
|
vis_1_id = JSON.parse(last_response.body).fetch('id')
|
|
post api_v1_visualizations_create_url(api_key: @api_key), factory(@user_1, locked: false, tags: ['test2']).to_json, @headers
|
|
|
|
get api_v1_visualizations_index_url(api_key: @api_key, tags: 'test1'), {}, @headers
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 1
|
|
collection.first['id'].should == vis_1_id
|
|
end
|
|
|
|
context 'only_published' do
|
|
before(:each) do
|
|
carto_user = Carto::User.find(@user_1.id)
|
|
unpublished_attrs = { privacy: Carto::Visualization::PRIVACY_PUBLIC, version: 3 }
|
|
_, _, _, @unpublished_viz = create_full_visualization(carto_user, visualization_attributes: unpublished_attrs)
|
|
published_attrs = { privacy: Carto::Visualization::PRIVACY_PUBLIC }
|
|
_, _, _, @published_viz = create_full_visualization(carto_user, visualization_attributes: published_attrs)
|
|
end
|
|
|
|
after(:each) do
|
|
@unpublished_viz.destroy
|
|
@published_viz.destroy
|
|
end
|
|
|
|
it 'filters with only_published = true' do
|
|
get api_v1_visualizations_index_url(api_key: @api_key, only_published: true), {}, @headers
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 1
|
|
collection.first['id'].should == @published_viz.id
|
|
end
|
|
|
|
it 'does not filter without it' do
|
|
get api_v1_visualizations_index_url(api_key: @api_key), {}, @headers
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 2
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
describe 'non existent visualization' do
|
|
it 'returns 404' do
|
|
get api_v1_visualizations_show_url(id: TEST_UUID, api_key: @api_key), {}, @headers
|
|
last_response.status.should == 404
|
|
|
|
put api_v1_visualizations_update_url(id: TEST_UUID, api_key: @api_key), { id: TEST_UUID }.to_json, @headers
|
|
last_response.status.should == 404
|
|
|
|
delete api_v1_visualizations_destroy_url(id: TEST_UUID, api_key: @api_key), { id: TEST_UUID }.to_json, @headers
|
|
last_response.status.should == 404
|
|
end
|
|
end
|
|
|
|
describe '/api/v1/viz/:id/watching' do
|
|
before(:all) do
|
|
@user_1_1 = create_test_user
|
|
@user_1_2 = create_test_user
|
|
|
|
organization = test_organization.save
|
|
|
|
user_org = CartoDB::UserOrganization.new(organization.id, @user_1_1.id)
|
|
user_org.promote_user_to_admin
|
|
@user_1_1.reload
|
|
|
|
@user_1_2.organization_id = organization.id
|
|
@user_1_2.save.reload
|
|
end
|
|
|
|
it 'returns an empty array if no other user is watching' do
|
|
Carto::Visualization::Watcher.any_instance.stubs(:list).returns([])
|
|
|
|
bypass_named_maps
|
|
|
|
login(@user_1_1)
|
|
post api_v1_visualizations_create_url(api_key: @user_1_1.api_key), factory(@user_1_1, locked: true).to_json, @headers
|
|
id = JSON.parse(last_response.body).fetch('id')
|
|
|
|
login(@user_1_1)
|
|
get api_v1_visualizations_notify_watching_url(id: id, api_key: @user_1_1.api_key)
|
|
body = JSON.parse(last_response.body)
|
|
|
|
expect(body).to eq([])
|
|
end
|
|
end
|
|
|
|
describe 'legacy controller migration' do
|
|
before(:all) do
|
|
@user = create_user
|
|
end
|
|
|
|
after(:all) do
|
|
bypass_named_maps
|
|
@user.destroy
|
|
end
|
|
|
|
before(:each) do
|
|
bypass_named_maps
|
|
# bypass_metrics
|
|
|
|
host! "#{@user.username}.localhost.lan"
|
|
end
|
|
|
|
after(:each) do
|
|
bypass_named_maps
|
|
delete_user_data @user
|
|
end
|
|
|
|
describe '#create' do
|
|
describe '#duplicate map' do
|
|
before(:all) do
|
|
@other_user = create_user
|
|
end
|
|
|
|
before(:each) do
|
|
bypass_named_maps
|
|
|
|
@map = Map.create(user_id: @user.id)
|
|
@visualization = FactoryGirl.create(:derived_visualization,
|
|
map_id: @map.id,
|
|
user_id: @user.id,
|
|
privacy: Visualization::Member::PRIVACY_PRIVATE)
|
|
end
|
|
|
|
after(:each) do
|
|
@map.destroy
|
|
end
|
|
|
|
after(:all) do
|
|
@other_user.destroy
|
|
end
|
|
|
|
it 'duplicates a map' do
|
|
new_name = @visualization.name + ' patatas'
|
|
|
|
post_json api_v1_visualizations_create_url(api_key: @user.api_key),
|
|
source_visualization_id: @visualization.id,
|
|
name: new_name
|
|
|
|
last_response.status.should be_success
|
|
|
|
Carto::Visualization.exists?(user_id: @user.id, type: 'derived', name: new_name).should be_true
|
|
end
|
|
|
|
it 'registers table dependencies for duplicated maps' do
|
|
map, table, table_visualization, visualization = create_full_visualization(Carto::User.find(@user.id))
|
|
new_name = visualization.name + ' registered'
|
|
|
|
post_json api_v1_visualizations_create_url(api_key: @user.api_key),
|
|
source_visualization_id: visualization.id,
|
|
name: new_name
|
|
|
|
last_response.status.should be_success
|
|
|
|
visualization = Carto::Visualization.where(user_id: @user.id, type: 'derived', name: new_name).first
|
|
visualization.should be
|
|
visualization.data_layers.first.user_tables.count.should eq 1
|
|
|
|
destroy_full_visualization(map, table, table_visualization, visualization)
|
|
end
|
|
|
|
it "duplicates someone else's map if has at least read permission to it" do
|
|
new_name = @visualization.name + ' patatas'
|
|
|
|
Carto::Visualization.any_instance.stubs(:is_viewable_by_user?).returns(true)
|
|
|
|
post_json api_v1_visualizations_create_url(user_domain: @other_user.username, api_key: @other_user.api_key),
|
|
source_visualization_id: @visualization.id,
|
|
name: new_name
|
|
|
|
last_response.status.should be_success
|
|
|
|
Carto::Visualization.exists?(user_id: @other_user.id, type: 'derived', name: new_name).should be_true
|
|
end
|
|
|
|
it "doesn't duplicate someone else's map without permission" do
|
|
new_name = @visualization.name + ' patatatosky'
|
|
|
|
post_json api_v1_visualizations_create_url(user_domain: @other_user.username, api_key: @other_user.api_key),
|
|
source_visualization_id: @visualization.id,
|
|
name: new_name
|
|
|
|
last_response.status.should == 403
|
|
|
|
Carto::Visualization.exists?(user_id: @other_user.id, type: 'derived', name: new_name).should be_false
|
|
end
|
|
end
|
|
|
|
describe 'map creation from datasets' do
|
|
include_context 'organization with users helper'
|
|
include TableSharing
|
|
|
|
it 'creates a visualization from a dataset given the viz id' do
|
|
table1 = create_table(user_id: @org_user_1.id)
|
|
payload = {
|
|
source_visualization_id: table1.table_visualization.id,
|
|
visChanges: 0,
|
|
name: "untitled_table_XXX_map"
|
|
}
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = CartoDB::Visualization::Member.new(id: vid).fetch
|
|
|
|
v.user_id.should eq @org_user_1.id
|
|
v.map.user_id.should eq @org_user_1.id
|
|
end
|
|
end
|
|
|
|
it 'does not create visualizations if user is viewer' do
|
|
table1 = create_table(user_id: @org_user_1.id)
|
|
payload = {
|
|
source_visualization_id: table1.table_visualization.id,
|
|
visChanges: 0,
|
|
name: "untitled_table_XXX_map"
|
|
}
|
|
|
|
@org_user_1.viewer = true
|
|
@org_user_1.save
|
|
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 403
|
|
end
|
|
|
|
@org_user_1.viewer = false
|
|
@org_user_1.save
|
|
end
|
|
|
|
it 'creates a visualization from a dataset given the table id' do
|
|
table1 = create_table(user_id: @org_user_1.id)
|
|
payload = {
|
|
tables: [table1.name]
|
|
}
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = CartoDB::Visualization::Member.new(id: vid).fetch
|
|
|
|
v.user_id.should eq @org_user_1.id
|
|
v.map.user_id.should eq @org_user_1.id
|
|
end
|
|
end
|
|
|
|
it 'correctly creates a visualization from two dataset of different users' do
|
|
table1 = create_table(user_id: @org_user_1.id)
|
|
table2 = create_table(user_id: @org_user_2.id)
|
|
share_table_with_user(table1, @org_user_2)
|
|
payload = {
|
|
type: 'derived',
|
|
tables: ["#{@org_user_1.username}.#{table1.name}", table2.name]
|
|
}
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_2.username, api_key: @org_user_2.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = CartoDB::Visualization::Member.new(id: vid).fetch
|
|
|
|
v.user_id.should eq @org_user_2.id
|
|
v.map.user_id.should eq @org_user_2.id
|
|
end
|
|
end
|
|
|
|
describe 'builder and editor behaviour' do
|
|
before(:all) do
|
|
@old_builder_enabled = @org_user_1.builder_enabled
|
|
end
|
|
|
|
after(:all) do
|
|
@org_user_1.builder_enabled = @old_builder_enabled
|
|
@org_user_1.save
|
|
end
|
|
|
|
describe 'for editor users' do
|
|
before(:all) do
|
|
@org_user_1.builder_enabled = false
|
|
@org_user_1.save
|
|
end
|
|
|
|
it 'copies the styles' do
|
|
table1 = create_table(user_id: @org_user_1.id)
|
|
payload = {
|
|
tables: [table1.name]
|
|
}
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = CartoDB::Visualization::Member.new(id: vid).fetch
|
|
original_layer = table1.map.data_layers.first
|
|
layer = v.map.data_layers.first
|
|
layer.options['tile_style'].should eq original_layer.options['tile_style']
|
|
end
|
|
end
|
|
|
|
it 'doesn\'t add style properties' do
|
|
table1 = create_table(user_id: @org_user_1.id)
|
|
payload = {
|
|
tables: [table1.name]
|
|
}
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = CartoDB::Visualization::Member.new(id: vid).fetch
|
|
|
|
layer = v.map.data_layers.first
|
|
layer.options['style_properties'].should be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'for builder users' do
|
|
before(:all) do
|
|
@org_user_1.builder_enabled = true
|
|
@org_user_1.save
|
|
end
|
|
|
|
it 'resets the styles' do
|
|
table1 = create_table(user_id: @org_user_1.id)
|
|
Table.any_instance.stubs(:geometry_types).returns(['ST_Point'])
|
|
payload = {
|
|
tables: [table1.name]
|
|
}
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = CartoDB::Visualization::Member.new(id: vid).fetch
|
|
|
|
original_layer = table1.map.data_layers.first
|
|
layer = v.map.data_layers.first
|
|
layer.options['tile_style'].should_not eq original_layer.options['tile_style']
|
|
end
|
|
end
|
|
|
|
it 'adds style properties' do
|
|
table1 = create_table(user_id: @org_user_1.id)
|
|
Table.any_instance.stubs(:geometry_types).returns(['ST_Point'])
|
|
payload = {
|
|
tables: [table1.name]
|
|
}
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = CartoDB::Visualization::Member.new(id: vid).fetch
|
|
|
|
layer = v.map.data_layers.first
|
|
layer.options['style_properties'].should_not be_nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
it 'rewrites queries for other user datasets' do
|
|
table1 = create_table(user_id: @org_user_1.id)
|
|
layer = table1.map.data_layers.first
|
|
layer.options['query'] = "SELECT * FROM #{table1.name} LIMIT 1"
|
|
layer.save
|
|
share_table_with_user(table1, @org_user_2)
|
|
payload = {
|
|
type: 'derived',
|
|
tables: ["#{@org_user_1.username}.#{table1.name}"]
|
|
}
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_2.username, api_key: @org_user_2.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = CartoDB::Visualization::Member.new(id: vid).fetch
|
|
layer = v.map.data_layers.first
|
|
layer.options['query'].should eq "SELECT * FROM #{@org_user_1.username}.#{table1.name} LIMIT 1"
|
|
end
|
|
end
|
|
|
|
it 'does not rewrite queries for same user datasets' do
|
|
table1 = create_table(user_id: @org_user_1.id)
|
|
layer = table1.map.data_layers.first
|
|
layer.options['query'] = "SELECT * FROM #{table1.name} LIMIT 1"
|
|
layer.save
|
|
share_table_with_user(table1, @org_user_1)
|
|
payload = {
|
|
type: 'derived',
|
|
tables: ["#{@org_user_1.username}.#{table1.name}"]
|
|
}
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = CartoDB::Visualization::Member.new(id: vid).fetch
|
|
new_layer = v.map.data_layers.first
|
|
new_layer.options['query'].should eq layer.options['query']
|
|
end
|
|
end
|
|
|
|
it 'sets table privacy if the user has private_maps' do
|
|
table1 = create_table(user_id: @org_user_1.id)
|
|
payload = {
|
|
tables: [table1.name]
|
|
}
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = CartoDB::Visualization::Member.new(id: vid).fetch
|
|
v.privacy.should eq CartoDB::Visualization::Member::PRIVACY_PRIVATE
|
|
end
|
|
end
|
|
|
|
it 'sets PUBLIC privacy if the user doesn\'t have private_maps' do
|
|
@carto_org_user_2.update_column(:private_maps_enabled, false) # Direct to DB to skip validations
|
|
table1 = create_table(user_id: @org_user_2.id)
|
|
payload = {
|
|
tables: [table1.name]
|
|
}
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_2.username, api_key: @org_user_2.api_key),
|
|
payload) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = CartoDB::Visualization::Member.new(id: vid).fetch
|
|
v.privacy.should eq CartoDB::Visualization::Member::PRIVACY_PUBLIC
|
|
end
|
|
end
|
|
|
|
it 'enables scrollwheel zoom by default' do
|
|
table1 = create_table(user_id: @org_user_2.id)
|
|
table1.map.scrollwheel = false
|
|
table1.map.options[:scrollwheel] = false
|
|
table1.map.save
|
|
|
|
post_json(api_v1_visualizations_create_url(user_domain: @org_user_2.username, api_key: @org_user_2.api_key),
|
|
tables: [table1.name]) do |response|
|
|
response.status.should eq 200
|
|
vid = response.body[:id]
|
|
v = Carto::Visualization.find(vid)
|
|
v.map.scrollwheel.should eq true
|
|
v.map.options[:scrollwheel].should eq true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#update" do
|
|
before(:each) do
|
|
login(@user)
|
|
end
|
|
|
|
it "Does not update visualizations if user is viewer" do
|
|
table = new_table(user_id: @user.id, privacy: ::UserTable::PRIVACY_PUBLIC).save.reload
|
|
|
|
@user.viewer = true
|
|
@user.save
|
|
|
|
payload = { id: table.table_visualization.id, privacy: Carto::Visualization::PRIVACY_PRIVATE }
|
|
put_json api_v1_visualizations_update_url(id: table.table_visualization.id), payload do |response|
|
|
response.status.should eq 403
|
|
end
|
|
|
|
@user.viewer = false
|
|
@user.save
|
|
|
|
table.destroy
|
|
end
|
|
|
|
it "Updates changes even if named maps communication fails" do
|
|
@user.private_tables_enabled = true
|
|
@user.save
|
|
|
|
table = new_table(user_id: @user.id, privacy: ::UserTable::PRIVACY_PUBLIC).save.reload
|
|
|
|
Carto::NamedMaps::Api.any_instance.stubs(:create).raises('fake named maps failure')
|
|
|
|
payload = { id: table.table_visualization.id, privacy: Carto::Visualization::PRIVACY_PRIVATE }
|
|
put_json api_v1_visualizations_update_url(id: table.table_visualization.id), payload do |response|
|
|
response.status.should be_success
|
|
end
|
|
|
|
table.reload
|
|
table.privacy.should eq ::UserTable::PRIVACY_PRIVATE
|
|
|
|
table.destroy
|
|
|
|
@user.private_tables_enabled = false
|
|
@user.save
|
|
end
|
|
|
|
it 'filters attributes' do
|
|
table = new_table(user_id: @user.id, privacy: ::UserTable::PRIVACY_PUBLIC).save.reload
|
|
|
|
table.table_visualization.description.should_not eq "something"
|
|
|
|
payload = { id: table.table_visualization.id, description: "something", fake: "NO!" }
|
|
put_json api_v1_visualizations_update_url(id: table.table_visualization.id), payload do |response|
|
|
response.status.should be_success
|
|
end
|
|
|
|
table.reload
|
|
table.table_visualization.description.should eq "something"
|
|
|
|
table.destroy
|
|
end
|
|
|
|
it "renames datasets" do
|
|
table = new_table(user_id: @user.id).save.reload
|
|
|
|
payload = { id: table.table_visualization.id, name: 'vis_rename_test1' }
|
|
put_json api_v1_visualizations_update_url(id: table.table_visualization.id), payload do |response|
|
|
response.status.should be_success
|
|
end
|
|
|
|
table.reload
|
|
table.name.should eq 'vis_rename_test1'
|
|
|
|
table.destroy
|
|
end
|
|
|
|
it 'sets password protection' do
|
|
visualization = FactoryGirl.create(:carto_visualization, user_id: @user.id)
|
|
visualization.password_protected?.should be_false
|
|
|
|
payload = {
|
|
id: visualization.id,
|
|
password: 'the_pass',
|
|
privacy: Carto::Visualization::PRIVACY_PROTECTED
|
|
}
|
|
put_json api_v1_visualizations_update_url(id: visualization.id), payload do |response|
|
|
response.status.should be_success
|
|
end
|
|
|
|
visualization.reload
|
|
visualization.password_protected?.should be_true
|
|
visualization.password_valid?('the_pass').should be_true
|
|
|
|
visualization.destroy
|
|
end
|
|
|
|
it 'migrates visualizations to v3' do
|
|
_, _, _, visualization = create_full_visualization(@user)
|
|
visualization.update_attributes!(version: 2)
|
|
visualization.analyses.each(&:destroy)
|
|
|
|
payload = {
|
|
id: visualization.id,
|
|
version: 3
|
|
}
|
|
put_json api_v1_visualizations_update_url(id: visualization.id), payload do |response|
|
|
response.status.should be_success
|
|
expect(response.body[:version]).to eq 3
|
|
end
|
|
|
|
visualization.reload
|
|
expect(visualization.analyses.any?).to be_true
|
|
|
|
visualization.destroy
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#destroy' do
|
|
include_context 'organization with users helper'
|
|
include TableSharing
|
|
|
|
def destroy_url(user, vis_id)
|
|
api_v1_visualizations_destroy_url(id: vis_id, user_domain: user.username, api_key: user.api_key)
|
|
end
|
|
|
|
it 'returns 404 for nonexisting visualizations' do
|
|
delete_json(destroy_url(@carto_org_user_1, random_uuid)) do |response|
|
|
expect(response.status).to eq 404
|
|
end
|
|
end
|
|
|
|
it 'returns 404 for not-accesible visualizations' do
|
|
other_visualization = FactoryGirl.create(:carto_visualization, user: @carto_org_user_2)
|
|
delete_json(destroy_url(@carto_org_user_1, other_visualization.id)) do |response|
|
|
expect(response.status).to eq 404
|
|
end
|
|
end
|
|
|
|
it 'returns 403 for not-owned visualizations' do
|
|
other_visualization = FactoryGirl.create(:carto_visualization, user: @carto_org_user_2)
|
|
share_visualization_with_user(other_visualization, @carto_org_user_1)
|
|
delete_json(destroy_url(@carto_org_user_1, other_visualization.id)) do |response|
|
|
expect(response.status).to eq 403
|
|
end
|
|
end
|
|
|
|
it 'returns 403 for viewer users' do
|
|
visualization = FactoryGirl.create(:carto_visualization, user: @carto_org_user_1)
|
|
@carto_org_user_1.update_attribute(:viewer, true)
|
|
delete_json(destroy_url(@carto_org_user_1, visualization.id)) do |response|
|
|
expect(response.status).to eq 403
|
|
end
|
|
@carto_org_user_1.update_attribute(:viewer, false)
|
|
end
|
|
|
|
it 'destroys a visualization by id' do
|
|
visualization = FactoryGirl.create(:carto_visualization, user: @carto_org_user_1)
|
|
delete_json(destroy_url(@carto_org_user_1, visualization.id)) do |response|
|
|
expect(response.status).to eq 204
|
|
end
|
|
end
|
|
|
|
it 'destroys a visualization by name' do
|
|
visualization = FactoryGirl.create(:carto_visualization, user: @carto_org_user_1)
|
|
delete_json(destroy_url(@carto_org_user_1, visualization.name)) do |response|
|
|
expect(response.status).to eq 204
|
|
end
|
|
end
|
|
|
|
it 'destroys a visualization and all of its dependencies (fully dependent)' do
|
|
_, _, table_visualization, visualization = create_full_visualization(@carto_org_user_1)
|
|
|
|
expect_visualization_to_be_destroyed(visualization) do
|
|
expect_visualization_to_be_destroyed(table_visualization) do
|
|
delete_json(destroy_url(@carto_org_user_1, table_visualization.id)) do |response|
|
|
expect(response.status).to eq 204
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
it 'destroys a visualization and affected layers (partially dependent)' do
|
|
_, _, table_visualization, visualization = create_full_visualization(@carto_org_user_1)
|
|
visualization.layers << FactoryGirl.create(:carto_layer)
|
|
visualization.data_layers.count.should eq 2
|
|
|
|
expect_visualization_to_be_destroyed(table_visualization) do
|
|
delete_json(destroy_url(@carto_org_user_1, table_visualization.id)) do |response|
|
|
expect(response.status).to eq 204
|
|
end
|
|
end
|
|
|
|
expect(Carto::Visualization.exists?(visualization.id)).to be_true
|
|
visualization.reload
|
|
visualization.data_layers.count.should eq 1
|
|
end
|
|
end
|
|
|
|
describe '#update' do
|
|
after(:all) do
|
|
@carto_user1.private_maps_enabled = false
|
|
@carto_user1.public_map_quota = nil
|
|
@carto_user1.save
|
|
end
|
|
|
|
it 'returns a 200 response when making a map public with enough quota' do
|
|
@carto_user1.private_maps_enabled = true
|
|
@carto_user1.public_map_quota = nil
|
|
@carto_user1.save
|
|
visualization = FactoryGirl.create(:carto_visualization, user: @carto_user1,
|
|
privacy: Carto::Visualization::PRIVACY_PRIVATE)
|
|
|
|
request_params = { user_domain: @carto_user1.username, api_key: @carto_user1.api_key, id: visualization.id }
|
|
put api_v1_visualizations_update_url(request_params),
|
|
{ id: visualization.id, privacy: CartoDB::Visualization::Member::PRIVACY_PUBLIC }.to_json,
|
|
@headers
|
|
|
|
last_response.status.should == 200
|
|
visualization.destroy!
|
|
end
|
|
|
|
it 'returns a 403 response when making a map public without enough quota' do
|
|
@carto_user1.private_maps_enabled = true
|
|
@carto_user1.public_map_quota = 0
|
|
@carto_user1.save
|
|
visualization = FactoryGirl.create(:carto_visualization, user: @carto_user1,
|
|
privacy: Carto::Visualization::PRIVACY_PRIVATE)
|
|
|
|
request_params = { user_domain: @carto_user1.username, api_key: @carto_user1.api_key, id: visualization.id }
|
|
put api_v1_visualizations_update_url(request_params),
|
|
{ id: visualization.id, privacy: CartoDB::Visualization::Member::PRIVACY_PUBLIC }.to_json,
|
|
@headers
|
|
|
|
last_response.status.should == 403
|
|
last_response.body.should =~ /public map quota/
|
|
end
|
|
|
|
it 'returns a 200 response when making a table public without enough map quota' do
|
|
@carto_user1.private_maps_enabled = true
|
|
@carto_user1.public_map_quota = 0
|
|
@carto_user1.save
|
|
user_table = FactoryGirl.create(:carto_user_table, :with_db_table, user_id: @carto_user1.id)
|
|
map = FactoryGirl.create(:carto_map)
|
|
user_table.map = map
|
|
user_table.save!
|
|
visualization = FactoryGirl.create(:carto_visualization,
|
|
type: Carto::Visualization::TYPE_CANONICAL,
|
|
map: map,
|
|
user: @carto_user1,
|
|
privacy: Carto::Visualization::PRIVACY_PRIVATE)
|
|
|
|
request_params = { user_domain: @carto_user1.username, api_key: @carto_user1.api_key, id: visualization.id }
|
|
put api_v1_visualizations_update_url(request_params),
|
|
{ id: visualization.id, privacy: CartoDB::Visualization::Member::PRIVACY_PUBLIC }.to_json,
|
|
@headers
|
|
|
|
last_response.status.should == 200
|
|
visualization.destroy!
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'index' do
|
|
include_context 'visualization creation helpers'
|
|
|
|
before(:all) do
|
|
@headers = {'CONTENT_TYPE' => 'application/json'}
|
|
end
|
|
|
|
before(:each) do
|
|
@user = FactoryGirl.create(:valid_user)
|
|
login(@user)
|
|
end
|
|
|
|
after(:each) do
|
|
@user.destroy
|
|
end
|
|
|
|
it 'mixed types search should filter only remote without display name' do
|
|
post api_v1_visualizations_create_url(api_key: @user.api_key),
|
|
factory(@user, locked: true, type: 'table').to_json, @headers
|
|
vis_1_id = JSON.parse(last_response.body).fetch('id')
|
|
|
|
post api_v1_visualizations_create_url(api_key: @user.api_key),
|
|
factory(@user, locked: true, type: 'remote', name: 'visu2', display_name: 'visu2').to_json, @headers
|
|
vis_2_id = JSON.parse(last_response.body).fetch('id')
|
|
Carto::ExternalSource.new(
|
|
visualization_id: vis_2_id,
|
|
import_url: 'http://www.fake.com',
|
|
rows_counted: 1,
|
|
size: 200).save
|
|
|
|
post api_v1_visualizations_create_url(api_key: @user.api_key),
|
|
factory(@user, locked: true, type: 'remote', name: 'visu3').to_json, @headers
|
|
vis_3_id = JSON.parse(last_response.body).fetch('id')
|
|
Carto::ExternalSource.new(
|
|
visualization_id: vis_3_id,
|
|
import_url: 'http://www.fake.com',
|
|
rows_counted: 1,
|
|
size: 200).save
|
|
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'remote,table'), {}, @headers
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 2
|
|
[vis_1_id, vis_2_id].include?(collection[0]['id']).should eq true
|
|
[vis_1_id, vis_2_id].include?(collection[1]['id']).should eq true
|
|
end
|
|
|
|
context 'with_dependent_visualizations' do
|
|
before(:each) do
|
|
table_a = create_random_table(@user)
|
|
@visualization_a = table_a.table_visualization
|
|
Delorean.time_travel_to "2018/01/01 00:00:00" do
|
|
@dependencies = Array.new(3) do
|
|
Delorean.jump(1.day)
|
|
FactoryGirl.create(:carto_visualization, user_id: @user.id)
|
|
end
|
|
end
|
|
Carto::Visualization.any_instance.stubs(:dependent_visualizations).returns(@dependencies)
|
|
end
|
|
|
|
it 'does not return the dependent visualizations by default' do
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'table',
|
|
order: 'dependent_visualizations'), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 1
|
|
collection[0]['dependent_visualizations_count'].should be_nil
|
|
collection[0]['dependent_visualizations'].should be_nil
|
|
end
|
|
|
|
it 'does not return the dependent visualizations if with_dependent_visualizations = 0' do
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'table',
|
|
with_dependent_visualizations: 0), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 1
|
|
collection[0]['dependent_visualizations_count'].should be_nil
|
|
collection[0]['dependent_visualizations'].should be_nil
|
|
end
|
|
|
|
it 'returns the 2 most recent dependent visualizations when with_dependent_visualizations = 2' do
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'table',
|
|
with_dependent_visualizations: 2), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 1
|
|
collection[0]['dependent_visualizations_count'].should eql 3
|
|
collection[0]['dependent_visualizations'].length.should eq 2
|
|
collection[0]['dependent_visualizations'].first['id'].should eql @dependencies[2].id
|
|
collection[0]['dependent_visualizations'].first['name'].should_not be_nil
|
|
collection[0]['dependent_visualizations'].second['id'].should eql @dependencies[1].id
|
|
collection[0]['dependent_visualizations'].second['name'].should_not be_nil
|
|
end
|
|
end
|
|
|
|
context 'ordering' do
|
|
|
|
it 'returns the expected status' do
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'derived', order: ''), {}, @headers
|
|
last_response.status.should == 200
|
|
|
|
get api_v1_visualizations_index_url(
|
|
api_key: @user.api_key,
|
|
types: 'derived',
|
|
order: '',
|
|
page: '',
|
|
per_page: ''
|
|
), {}, @headers
|
|
last_response.status.should == 200
|
|
|
|
['derived', 'slide'].each do |type|
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: type, order: :mapviews), {}, @headers
|
|
last_response.status.should == 200
|
|
end
|
|
|
|
['remote', 'table'].each do |type|
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: type, order: :size), {}, @headers
|
|
last_response.status.should == 200
|
|
end
|
|
|
|
['derived', 'remote', 'slide', 'table'].each do |type|
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: type, order: :whatever), {}, @headers
|
|
last_response.status.should == 400
|
|
JSON.parse(last_response.body).fetch('error').should_not be_nil
|
|
end
|
|
end
|
|
|
|
it 'orders descending by default' do
|
|
visualization_a = FactoryGirl.create(:carto_visualization, name: 'Visualization A', user_id: @user.id).store
|
|
visualization_b = FactoryGirl.create(:carto_visualization, name: 'Visualization B', user_id: @user.id).store
|
|
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'derived', order: 'name'), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 2
|
|
collection[0]['id'].should eq visualization_b.id
|
|
collection[1]['id'].should eq visualization_a.id
|
|
end
|
|
|
|
it 'orders by name' do
|
|
visualization_a = FactoryGirl.create(:carto_visualization, name: 'Visualization A', user_id: @user.id).store
|
|
visualization_b = FactoryGirl.create(:carto_visualization, name: 'Visualization B', user_id: @user.id).store
|
|
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'derived', order: 'name',
|
|
order_direction: 'asc'), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 2
|
|
collection[0]['id'].should eq visualization_a.id
|
|
collection[1]['id'].should eq visualization_b.id
|
|
end
|
|
|
|
it 'orders by favorited' do
|
|
visualization_a = FactoryGirl.create(:carto_visualization, user_id: @user.id).store
|
|
visualization_b = FactoryGirl.create(:carto_visualization, user_id: @user.id).store
|
|
visualization_a.add_like_from(@user)
|
|
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'derived', with_dependent_visualizations: 10,
|
|
order: 'favorited', order_direction: 'desc'), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 2
|
|
collection[0]['id'].should eq visualization_a.id
|
|
collection[1]['id'].should eq visualization_b.id
|
|
end
|
|
|
|
it 'orders by size' do
|
|
vis1 = factory(@user, locked: true, type: 'remote', display_name: 'visu1')
|
|
post api_v1_visualizations_create_url(api_key: @user.api_key), vis1.to_json, @headers
|
|
vis1_id = JSON.parse(last_response.body).fetch('id')
|
|
Carto::ExternalSource.new(
|
|
visualization_id: vis1_id,
|
|
import_url: 'http://www.fake.com',
|
|
rows_counted: 1,
|
|
size: 100
|
|
).save
|
|
|
|
vis2 = factory(@user, locked: true, type: 'remote', display_name: 'visu2')
|
|
post api_v1_visualizations_create_url(api_key: @user.api_key), vis2.to_json, @headers
|
|
vis2_id = JSON.parse(last_response.body).fetch('id')
|
|
Carto::ExternalSource.new(
|
|
visualization_id: vis2_id,
|
|
import_url: 'http://www.fake.com',
|
|
rows_counted: 1,
|
|
size: 200
|
|
).save
|
|
|
|
vis3 = factory(@user, locked: true, type: 'remote', display_name: 'visu3')
|
|
post api_v1_visualizations_create_url(api_key: @user.api_key), vis3.to_json, @headers
|
|
vis3_id = JSON.parse(last_response.body).fetch('id')
|
|
Carto::ExternalSource.new(
|
|
visualization_id: vis3_id,
|
|
import_url: 'http://www.fake.com',
|
|
rows_counted: 1, size: 10
|
|
).save
|
|
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'remote', order: 'size'), {}, @headers
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 3
|
|
collection[0]['id'].should == vis2_id
|
|
collection[1]['id'].should == vis1_id
|
|
collection[2]['id'].should == vis3_id
|
|
end
|
|
|
|
xit 'orders by estimated row count' do
|
|
visualization_a = FactoryGirl.create(:carto_visualization, user_id: @user.id)
|
|
table = FactoryGirl.create(:table, user_id: @user.id)
|
|
table.insert_row!(name: 'name1')
|
|
table.update_table_pg_stats
|
|
visualization_b = FactoryGirl.create(:carto_visualization, user_id: @user.id, map_id: table.map_id)
|
|
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'derived', order: 'estimated_row_count',
|
|
order_direction: 'desc'), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 2
|
|
collection[0]['id'].should eq visualization_b.id
|
|
collection[1]['id'].should eq visualization_a.id
|
|
end
|
|
|
|
it 'orders by privacy' do
|
|
link_privacy = Carto::Visualization::PRIVACY_LINK
|
|
public_privacy = Carto::Visualization::PRIVACY_PUBLIC
|
|
visualization_a = FactoryGirl.create(:carto_visualization, user_id: @user.id, privacy: link_privacy).store
|
|
visualization_b = FactoryGirl.create(:carto_visualization, user_id: @user.id, privacy: public_privacy).store
|
|
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'derived', order: 'privacy',
|
|
order_direction: 'desc'), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 2
|
|
collection[0]['id'].should eq visualization_b.id
|
|
collection[1]['id'].should eq visualization_a.id
|
|
end
|
|
|
|
it 'orders by dependent visualizations' do
|
|
table_a = create_random_table(@user)
|
|
visualization_a = table_a.table_visualization
|
|
dependent_visualization = FactoryGirl.create(:carto_visualization, user_id: @user.id)
|
|
dependent_visualization.map = FactoryGirl.create(:carto_map, user_id: @user.id)
|
|
dependent_visualization.save!
|
|
layer = FactoryGirl.build(:carto_layer)
|
|
layer.options[:table_name] = table_a.name
|
|
layer.save!
|
|
dependent_visualization.layers << layer
|
|
table_b = create_random_table(@user)
|
|
visualization_b = table_b.table_visualization
|
|
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'table', order: 'dependent_visualizations',
|
|
with_dependent_visualizations: 10,
|
|
order_direction: 'desc'), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 2
|
|
collection[0]['id'].should eq visualization_a.id
|
|
collection[1]['id'].should eq visualization_b.id
|
|
end
|
|
|
|
context 'by search rank' do
|
|
before(:each) do
|
|
@visualization_a = FactoryGirl.create(:carto_visualization, name: 'Best rank', user_id: @user.id).store
|
|
@visualization_b = FactoryGirl.create(:carto_visualization, name: 'Another rank, but not the best',
|
|
user_id: @user.id).store
|
|
end
|
|
|
|
it 'orders results by search rank when searching' do
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'derived',
|
|
q: 'Best rank'), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 2
|
|
collection[0]['id'].should eq @visualization_a.id
|
|
collection[1]['id'].should eq @visualization_b.id
|
|
end
|
|
|
|
it 'ignores other ordering parameters' do
|
|
get api_v1_visualizations_index_url(api_key: @user.api_key, types: 'derived', q: 'Best rank',
|
|
order: 'name', order_direction: 'asc'), {}, @headers
|
|
|
|
last_response.status.should == 200
|
|
response = JSON.parse(last_response.body)
|
|
collection = response.fetch('visualizations')
|
|
collection.length.should eq 2
|
|
collection[0]['id'].should eq @visualization_a.id
|
|
collection[1]['id'].should eq @visualization_b.id
|
|
end
|
|
end
|
|
|
|
context 'error handling' do
|
|
before(:each) do
|
|
@valid_order = 'updated_at'
|
|
@invalid_order = 'invalid_order'
|
|
@valid_order_direction = 'asc'
|
|
@invalid_order_direction = 'invalid_order_direction'
|
|
@valid_order_combination = 'name,updated_at'
|
|
@invalid_order_combination = 'size,updated_at'
|
|
@valid_direction_combination = 'asc,desc'
|
|
@invalid_direction_combination = 'asc,invalid'
|
|
end
|
|
|
|
it 'returns an error if an invalid :order is given' do
|
|
get api_v1_visualizations_index_url(order: @valid_order, api_key: @user.api_key,
|
|
types: 'derived'), {}, @headers
|
|
last_response.status.should == 200
|
|
|
|
get api_v1_visualizations_index_url(order: @invalid_order, api_key: @user.api_key,
|
|
types: 'derived'), {}, @headers
|
|
last_response.status.should == 400
|
|
last_response.body.should include "Wrong 'order' parameter value"
|
|
end
|
|
|
|
it 'returns an error if an invalid :order_direction is given' do
|
|
get api_v1_visualizations_index_url(order_direction: @valid_order_direction, order: @valid_order,
|
|
api_key: @user.api_key, types: 'derived'), {}, @headers
|
|
last_response.status.should == 200
|
|
|
|
get api_v1_visualizations_index_url(order_direction: @invalid_order_direction, order: @valid_order,
|
|
api_key: @user.api_key, types: 'derived'), {}, @headers
|
|
last_response.status.should == 400
|
|
last_response.body.should include "Wrong 'order_direction' parameter value"
|
|
end
|
|
|
|
it 'returns an error if an invalid :order combination is given' do
|
|
get api_v1_visualizations_index_url(order: @valid_order_combination, api_key: @user.api_key,
|
|
types: 'derived'), {}, @headers
|
|
last_response.status.should == 200
|
|
|
|
get api_v1_visualizations_index_url(order: @invalid_order_combination, api_key: @user.api_key,
|
|
types: 'derived'), {}, @headers
|
|
last_response.status.should == 400
|
|
last_response.body.should include "Wrong 'order' parameter combination"
|
|
end
|
|
|
|
it 'returns an error if an invalid :order_direction combination is given' do
|
|
get api_v1_visualizations_index_url(order: @valid_order_combination,
|
|
order_direction: @valid_direction_combination,
|
|
api_key: @user.api_key, types: 'derived'), {}, @headers
|
|
last_response.status.should == 200
|
|
|
|
get api_v1_visualizations_index_url(order: @valid_order_combination,
|
|
order_direction: @invalid_direction_combination,
|
|
api_key: @user.api_key, types: 'derived'), {}, @headers
|
|
last_response.status.should == 400
|
|
last_response.body.should include "Wrong 'order_direction' parameter combination"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'index' do
|
|
include_context 'organization with users helper'
|
|
include_context 'visualization creation helpers'
|
|
|
|
describe 'shared_only' do
|
|
it 'should not display nor count the shared visualizations you own' do
|
|
table = create_table(privacy: UserTable::PRIVACY_PUBLIC, name: unique_name('table'), user_id: @org_user_1.id)
|
|
u1_t_1_id = table.table_visualization.id
|
|
u1_t_1_perm_id = table.table_visualization.permission.id
|
|
|
|
share_table_with_organization(table, @org_user_1, @organization)
|
|
|
|
get api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: CartoDB::Visualization::Member::TYPE_CANONICAL, order: 'updated_at',
|
|
shared: CartoDB::Visualization::Collection::FILTER_SHARED_ONLY), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body['total_entries'].should eq 0
|
|
body['visualizations'].count.should eq 0
|
|
end
|
|
end
|
|
|
|
it 'returns auth tokens for password protected viz for the owner but not for users that have them shared' do
|
|
@map, @table, @table_visualization, @visualization = create_full_visualization(@carto_org_user_1)
|
|
@visualization.privacy = Carto::Visualization::PRIVACY_PROTECTED
|
|
@visualization.password = 'wontbeused'
|
|
@visualization.save!
|
|
|
|
share_visualization(@visualization, @org_user_2)
|
|
|
|
get_json api_v1_visualizations_index_url(user_domain: @org_user_1.username, api_key: @org_user_1.api_key,
|
|
type: Carto::Visualization::TYPE_DERIVED,
|
|
shared: CartoDB::Visualization::Collection::FILTER_SHARED_YES), @headers do |response|
|
|
response.status.should eq 200
|
|
response.body[:visualizations][0][:id].should_not be_empty
|
|
response.body[:visualizations][0][:auth_tokens].should_not be_empty
|
|
end
|
|
|
|
get_json api_v1_visualizations_index_url(user_domain: @org_user_2.username, api_key: @org_user_2.api_key,
|
|
type: Carto::Visualization::TYPE_DERIVED,
|
|
shared: CartoDB::Visualization::Collection::FILTER_SHARED_YES), @headers do |response|
|
|
response.status.should eq 200
|
|
response.body[:visualizations][0][:id].should_not be_empty
|
|
response.body[:visualizations][0][:auth_tokens].should be_empty
|
|
end
|
|
|
|
destroy_full_visualization(@map, @table, @table_visualization, @visualization)
|
|
end
|
|
end
|
|
|
|
describe 'visualization url generation' do
|
|
include_context 'visualization creation helpers'
|
|
include_context 'organization with users helper'
|
|
|
|
before(:all) do
|
|
@user = FactoryGirl.create(:valid_user)
|
|
end
|
|
|
|
after(:all) do
|
|
@user.destroy
|
|
end
|
|
|
|
it 'generates a user table visualization url' do
|
|
table = create_table(privacy: UserTable::PRIVACY_PUBLIC, name: unique_name('table'), user_id: @user.id)
|
|
vis_id = table.table_visualization.id
|
|
|
|
get_json api_v1_visualizations_show_url(user_domain: @user.username, id: vis_id, api_key: @user.api_key), {}, http_json_headers do |response|
|
|
response.status.should == 200
|
|
|
|
response.body[:url].should == "http://#{@user.username}#{Cartodb.config[:session_domain]}:#{Cartodb.config[:http_port]}/tables/#{table.name}"
|
|
end
|
|
end
|
|
|
|
it 'generates a user map url' do
|
|
visualization = api_visualization_creation(@user, http_json_headers, { privacy: Visualization::Member::PRIVACY_PUBLIC, type: Visualization::Member::TYPE_DERIVED })
|
|
get_json api_v1_visualizations_show_url(user_domain: @user.username, id: visualization.id, api_key: @user.api_key), {}, http_json_headers do |response|
|
|
response.status.should == 200
|
|
|
|
response.body[:url].should == "http://#{@user.username}#{Cartodb.config[:session_domain]}:#{Cartodb.config[:http_port]}/viz/#{visualization.id}/map"
|
|
end
|
|
end
|
|
|
|
it 'generates a org user table visualization url' do
|
|
table = create_table(privacy: UserTable::PRIVACY_PUBLIC, name: unique_name('table'), user_id: @org_user_1.id)
|
|
vis_id = table.table_visualization.id
|
|
|
|
get_json api_v1_visualizations_show_url(user_domain: @org_user_1.username, id: vis_id, api_key: @org_user_1.api_key), {}, http_json_headers do |response|
|
|
response.status.should == 200
|
|
|
|
response.body[:url].should == "http://#{@org_user_1.organization.name}#{Cartodb.config[:session_domain]}:#{Cartodb.config[:http_port]}/u/#{@org_user_1.username}/tables/#{table.name}"
|
|
end
|
|
end
|
|
|
|
it 'generates a organization user map url' do
|
|
visualization = api_visualization_creation(@org_user_1, http_json_headers, { privacy: Visualization::Member::PRIVACY_PUBLIC, type: Visualization::Member::TYPE_DERIVED })
|
|
get_json api_v1_visualizations_show_url(user_domain: @org_user_1.username, id: visualization.id, api_key: @org_user_1.api_key), {}, http_json_headers do |response|
|
|
response.status.should == 200
|
|
|
|
response.body[:url].should == "http://#{@org_user_1.organization.name}#{Cartodb.config[:session_domain]}:#{Cartodb.config[:http_port]}/u/#{@org_user_1.username}/viz/#{visualization.id}/map"
|
|
end
|
|
end
|
|
|
|
it 'generates the URL for tables shared by another user with hyphens in his username' do
|
|
user_with_hyphen = FactoryGirl.create(:user, username: 'fulano-de-tal', organization: @organization)
|
|
table = create_random_table(user_with_hyphen, 'tabluca', UserTable::PRIVACY_PRIVATE)
|
|
shared_table = table.table_visualization
|
|
share_visualization(shared_table, @org_user_1)
|
|
|
|
request_url = api_v1_visualizations_show_url(user_domain: @org_user_1.username,
|
|
id: shared_table.id, api_key: @org_user_1.api_key)
|
|
get_json request_url, {}, http_json_headers do |response|
|
|
response.status.should == 200
|
|
response.body[:url].should include("/tables/fulano-de-tal.tabluca")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'filter canonical viz by bounding box' do
|
|
include_context 'visualization creation helpers'
|
|
|
|
before(:all) do
|
|
bypass_named_maps
|
|
@user = FactoryGirl.create(:valid_user)
|
|
|
|
@table_inside_bbox = create_geometry_table(@user, BBOX_GEOM)
|
|
@table_outside_bbox = create_geometry_table(@user, OUTSIDE_BBOX_GEOM)
|
|
end
|
|
|
|
after(:all) do
|
|
@user.destroy
|
|
end
|
|
|
|
it 'should show return only visualizations that intersect with the bbox' do
|
|
get api_v1_visualizations_index_url(user_domain: @user.username,
|
|
types: CartoDB::Visualization::Member::TYPE_CANONICAL, bbox: '-18.166667,27.633333,4.333333,43.916667'), @headers
|
|
body = JSON.parse(last_response.body)
|
|
body["visualizations"].length.should eq 1
|
|
body["visualizations"][0]["id"].should eq @table_inside_bbox.table_visualization.id
|
|
end
|
|
|
|
it 'should return 400 when try to filter by bbox and not canonical visualizations' do
|
|
get api_v1_visualizations_index_url(user_domain: @user.username,
|
|
types: CartoDB::Visualization::Member::TYPE_DERIVED, bbox: '-18.166667,27.633333,4.333333,43.916667'), @headers
|
|
last_response.status.should eq 400
|
|
end
|
|
|
|
it 'should return 400 when try to filter by bbox and with more than only canonical visualizations' do
|
|
get api_v1_visualizations_index_url(user_domain: @user.username,
|
|
types: "#{CartoDB::Visualization::Member::TYPE_DERIVED}, #{CartoDB::Visualization::Member::TYPE_CANONICAL}", bbox: '-18.166667,27.633333,4.333333,43.916667'), @headers
|
|
last_response.status.should eq 400
|
|
end
|
|
|
|
it 'should return 400 when try to filter by bbox with less than 4 coordinates' do
|
|
get api_v1_visualizations_index_url(user_domain: @user.username,
|
|
types: CartoDB::Visualization::Member::TYPE_DERIVED, bbox: '27.633333,4.333333,43.916667'), @headers
|
|
last_response.status.should eq 400
|
|
end
|
|
|
|
it 'should return 400 when try to filter by bbox with wrong typed coordinates' do
|
|
get api_v1_visualizations_index_url(user_domain: @user.username,
|
|
types: CartoDB::Visualization::Member::TYPE_CANONICAL, bbox: '18.323232,alal,4.333333,43.916667'), @headers
|
|
last_response.status.should eq 400
|
|
get api_v1_visualizations_index_url(user_domain: @user.username,
|
|
types: CartoDB::Visualization::Member::TYPE_CANONICAL, bbox: 'true,2.393939,4.333333,43.916667'), @headers
|
|
last_response.status.should eq 400
|
|
end
|
|
|
|
end
|
|
|
|
# See #5591
|
|
describe 'error with wrong visualization url' do
|
|
def url(user_domain, visualization_id, api_key, host = @host)
|
|
api_v1_visualizations_show_url(user_domain: user_domain, id: visualization_id, api_key: api_key).
|
|
gsub('www.example.com', host)
|
|
end
|
|
|
|
describe 'normal user urls' do
|
|
before(:all) do
|
|
bypass_named_maps
|
|
@vis_owner = FactoryGirl.create(:valid_user, private_tables_enabled: true)
|
|
@other_user = FactoryGirl.create(:valid_user, private_tables_enabled: true)
|
|
|
|
@table = create_random_table(@vis_owner, unique_name('viz'), UserTable::PRIVACY_PRIVATE)
|
|
@vis = @table.table_visualization
|
|
@vis.private?.should == true
|
|
|
|
@host = "#{@vis_owner.username}.localhost.lan"
|
|
|
|
@headers = http_json_headers
|
|
end
|
|
|
|
after(:all) do
|
|
@table.destroy
|
|
end
|
|
|
|
it 'returns 200 with owner user_domain' do
|
|
get_json url(@vis_owner.username, @vis.id, @vis_owner.api_key), {}, @headers do |response|
|
|
response.status.should == 200
|
|
end
|
|
end
|
|
|
|
it 'returns 404 if visualization does not exist' do
|
|
get_json url(@vis_owner.username, random_uuid, @vis_owner.api_key), {}, @headers do |response|
|
|
response.status.should == 404
|
|
response.body[:errors].should == 'Visualization does not exist'
|
|
end
|
|
end
|
|
|
|
it 'returns 403 under other user domain if visualization is private' do
|
|
get_json url(@other_user.username, @vis.id, @other_user.api_key), {}, @headers do |response|
|
|
response.status.should == 403
|
|
response.body[:errors].should == 'Visualization not viewable'
|
|
end
|
|
end
|
|
|
|
it 'returns 403 if visualization is private' do
|
|
get_json url(@vis_owner.username, @vis.id, @other_user.api_key), {}, @headers do |response|
|
|
response.status.should == 403
|
|
end
|
|
end
|
|
|
|
it 'returns 200 if user at url is empty' do
|
|
ApplicationController.any_instance.stubs(:current_viewer).returns(@vis_owner)
|
|
login_as(@vis_owner, scope: @vis_owner.username)
|
|
get_json url(nil, @vis.id, @vis_owner.api_key), {}, @headers do |response|
|
|
response.status.should == 200
|
|
end
|
|
end
|
|
|
|
it 'returns 404 if user at url does not match visualization owner' do
|
|
app = ApplicationController.any_instance
|
|
app.stubs(:current_user).returns(@vis_owner)
|
|
app.stubs(:current_viewer).returns(@vis_owner)
|
|
app.stubs(:api_authorization_required).returns(true)
|
|
|
|
get_json url(@other_user.username, @vis.id, @vis_owner.api_key), {}, @headers do |response|
|
|
response.status.should == 404
|
|
end
|
|
end
|
|
|
|
it 'returns 404 if user subdomain does not match visualization owner' do
|
|
app = ApplicationController.any_instance
|
|
app.stubs(:current_user).returns(@vis_owner)
|
|
app.stubs(:current_viewer).returns(@vis_owner)
|
|
app.stubs(:api_authorization_required).returns(true)
|
|
|
|
host = "#{@other_user.username}.localhost.lan"
|
|
get_json url(nil, @vis.id, @vis_owner.api_key, host), {}, @headers do |response|
|
|
response.status.should == 404
|
|
response.body[:errors].should == 'Visualization of that user does not exist'
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'organization urls' do
|
|
include_context 'organization with users helper'
|
|
|
|
before(:each) do
|
|
bypass_named_maps
|
|
|
|
@vis_owner = @org_user_1
|
|
@shared_vis = FactoryGirl.build(:derived_visualization,
|
|
user_id: @vis_owner.id,
|
|
name: unique_name('viz'),
|
|
description: 'wadus desc',
|
|
privacy: CartoDB::Visualization::Member::PRIVACY_PRIVATE).store
|
|
@shared_user = @org_user_2
|
|
@not_shared_user = @org_user_owner
|
|
share_visualization(@shared_vis, @shared_user)
|
|
|
|
@host = "#{@vis_owner.organization.name}.localhost.lan"
|
|
|
|
@headers = http_json_headers
|
|
end
|
|
|
|
after(:each) do
|
|
@shared_vis.delete
|
|
end
|
|
|
|
it 'returns 200 with owner user_domain' do
|
|
get_json url(@vis_owner.username, @shared_vis.id, @vis_owner.api_key), {}, @headers do |response|
|
|
response.status.should == 200
|
|
end
|
|
end
|
|
|
|
it 'returns 200 with valid (shared user) user_domain' do
|
|
get_json url(@shared_user.username, @shared_vis.id, @shared_user.api_key), {}, @headers do |response|
|
|
response.status.should == 200
|
|
end
|
|
end
|
|
|
|
it 'returns 200 with valid shared user (current_user) user_domain, with current_viewer being the owner' do
|
|
ApplicationController.any_instance.stubs(:current_viewer).returns(@vis_owner)
|
|
ApplicationController.any_instance.stubs(:current_user).returns(@shared_user)
|
|
get_json url(@shared_user.username, @shared_vis.id, @shared_user.api_key), {}, @headers do |response|
|
|
response.status.should == 200
|
|
end
|
|
end
|
|
|
|
it 'returns 200 and private info with valid shared user user_domain' do
|
|
get_json url(@shared_user.username, @shared_vis.id, @shared_user.api_key), {}, @headers do |response|
|
|
response.status.should == 200
|
|
response.body[:description].should_not be_nil
|
|
response.body[:auth_tokens].should_not be_nil
|
|
end
|
|
end
|
|
|
|
it 'returns 404 if visualization does not exist' do
|
|
get_json url(@vis_owner.username, random_uuid, @vis_owner.api_key), {}, @headers do |response|
|
|
response.status.should == 404
|
|
response.body[:errors].should == 'Visualization does not exist'
|
|
end
|
|
end
|
|
|
|
it 'returns 403 if visualization is not shared with the domain user' do
|
|
get_json url(@not_shared_user.username, @shared_vis.id, @not_shared_user.api_key), {}, @headers do |response|
|
|
response.status.should == 403
|
|
response.body[:errors].should == 'Visualization not viewable'
|
|
end
|
|
end
|
|
|
|
it 'returns 403 if visualization is not shared with the apikey user' do
|
|
get_json url(@shared_user.username, @shared_vis.id, @not_shared_user.api_key), {}, @headers do |response|
|
|
response.status.should == 403
|
|
end
|
|
end
|
|
|
|
it 'returns 404 if user at url is empty' do
|
|
ApplicationController.any_instance.stubs(:current_viewer).returns(@shared_user)
|
|
login_as(@shared_user, scope: @shared_user.organization.name)
|
|
get_json url(nil, @shared_vis.id, @shared_user.api_key), {}, @headers do |response|
|
|
response.status.should == 404
|
|
response.body[:errors].should == 'Visualization of that user does not exist'
|
|
end
|
|
end
|
|
|
|
it 'returns 404 if user at url is empty, current_user is the owner and current_viewer has permission' do
|
|
ApplicationController.any_instance.stubs(:current_user).returns(@vis_owner)
|
|
ApplicationController.any_instance.stubs(:current_viewer).returns(@shared_user)
|
|
login_as(@shared_user, scope: @shared_user.organization.name)
|
|
get_json url(nil, @shared_vis.id, @shared_user.api_key), {}, @headers do |response|
|
|
response.status.should == 404
|
|
response.body[:errors].should == 'Visualization of that user does not exist'
|
|
end
|
|
end
|
|
|
|
it 'returns 404 if user at url does not match visualization owner' do
|
|
app = ApplicationController.any_instance
|
|
app.stubs(:current_user).returns(@shared_user)
|
|
app.stubs(:current_viewer).returns(@shared_user)
|
|
app.stubs(:api_authorization_required).returns(true)
|
|
|
|
login_as(@shared_user, scope: @shared_user.organization.name)
|
|
get_json url(@not_shared_user.username, @shared_vis.id, @shared_user.api_key), {}, @headers do |response|
|
|
response.status.should == 404
|
|
response.body[:errors].should == 'Visualization of that user does not exist'
|
|
end
|
|
end
|
|
|
|
it 'returns 404 if user at url does not match visualization owner with current_user being the owner and current_viewer the shared to' do
|
|
app = ApplicationController.any_instance
|
|
app.stubs(:current_user).returns(@vis_owner)
|
|
app.stubs(:current_viewer).returns(@shared_user)
|
|
app.stubs(:api_authorization_required).returns(true)
|
|
|
|
login_as(@shared_user, scope: @shared_user.organization.name)
|
|
get_json url(@not_shared_user.username, @shared_vis.id, @shared_user.api_key), {}, @headers do |response|
|
|
response.status.should == 404
|
|
response.body[:errors].should == 'Visualization of that user does not exist'
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#google_maps_static_image' do
|
|
before(:all) do
|
|
@user = FactoryGirl.create(:carto_user)
|
|
@map, @table, @table_visualization, @visualization = create_full_visualization(@user)
|
|
base_layer = @visualization.base_layers.first
|
|
base_layer.options[:baseType] = 'roadmap'
|
|
base_layer.options[:style] = '[]'
|
|
base_layer.save
|
|
end
|
|
|
|
before(:each) do
|
|
host! "#{@user.username}.localhost.lan"
|
|
login_as(@user, scope: @user.username)
|
|
end
|
|
|
|
after(:all) do
|
|
destroy_full_visualization(@map, @table, @table_visualization, @visualization)
|
|
@user.destroy
|
|
end
|
|
|
|
let(:params) do
|
|
{
|
|
size: '300x200',
|
|
zoom: 14,
|
|
center: '0.12,-7.56'
|
|
}
|
|
end
|
|
|
|
it 'returns error if user does not have Google configured' do
|
|
@user.google_maps_key = nil
|
|
@user.save
|
|
get_json api_v1_google_maps_static_image_url(params.merge(id: @visualization.id)) do |response|
|
|
expect(response.status).to eq 400
|
|
expect(response.body[:errors]).to be
|
|
end
|
|
end
|
|
|
|
it 'returns signed google maps URL (key)' do
|
|
@user.google_maps_key = 'key=GAdhfasjkd'
|
|
@user.save
|
|
get_json api_v1_google_maps_static_image_url(params.merge(id: @visualization.id)) do |response|
|
|
response.status.should be_success
|
|
response.body[:url].should eq 'https://maps.googleapis.com/maps/api/staticmap?center=0.12,-7.56&mapType=roadmap&size=300x200&zoom=14&key=GAdhfasjkd'
|
|
end
|
|
end
|
|
|
|
it 'returns signed google maps URL (client + signature)' do
|
|
@user.google_maps_key = 'client=GAdhfasjkd'
|
|
@user.google_maps_private_key = 'MjM0MzJk-3N_czQzJmFkc2Rhc2Q='
|
|
@user.save
|
|
get_json api_v1_google_maps_static_image_url(params.merge(id: @visualization.id)) do |response|
|
|
response.status.should be_success
|
|
response.body[:url].should eq 'https://maps.googleapis.com/maps/api/staticmap?center=0.12,-7.56&mapType=roadmap&size=300x200&zoom=14&client=GAdhfasjkd&signature=q3E0WXgV1XlglotqoRXUZ4O8d10='
|
|
end
|
|
end
|
|
end
|
|
|
|
include Rack::Test::Methods
|
|
include Warden::Test::Helpers
|
|
include CacheHelper
|
|
|
|
private
|
|
|
|
# Custom hash comparison, since in the ActiveModel-based controllers
|
|
# we allow some differences:
|
|
# - x to many associations can return [] instead of nil
|
|
def normalize_hash(h, normalized_attributes = NORMALIZED_ASSOCIATION_ATTRIBUTES)
|
|
h.each { |k, v|
|
|
h[k] = nil if v == []
|
|
h[k] = '' if normalized_attributes[:attributes].include?(k)
|
|
if normalized_attributes[:associations].keys.include?(k)
|
|
normalize_hash(v, normalized_attributes[:associations][k])
|
|
end
|
|
}
|
|
end
|
|
|
|
# INFO: this test uses comparison against old data structures to check validity.
|
|
# You can use this method to remove that new data so next comparisons will work.
|
|
def remove_data_only_in_new_controllers(visualization_hash, new_attributes = NEW_ATTRIBUTES)
|
|
visualization_hash.each { |k, v|
|
|
if new_attributes[:attributes].include?(k)
|
|
removed = visualization_hash.delete(k)
|
|
elsif new_attributes[:associations].include?(k)
|
|
remove_data_only_in_new_controllers(v, new_attributes[:associations][k])
|
|
end
|
|
}
|
|
end
|
|
|
|
def login(user)
|
|
login_as(user, {scope: user.username })
|
|
host! "#{user.username}.localhost.lan"
|
|
end
|
|
|
|
def base_url
|
|
'/api/v1/viz'
|
|
end
|
|
|
|
def response_body(params = nil)
|
|
get base_url, params.nil? ? nil : params.dup, @headers
|
|
last_response.status.should == 200
|
|
body = JSON.parse(last_response.body)
|
|
body['visualizations'] = body['visualizations'].map { |v| normalize_hash(v) }.map { |v| remove_data_only_in_new_controllers(v) }
|
|
body
|
|
end
|
|
|
|
def factory(user, attributes={})
|
|
visualization_template(user, attributes)
|
|
end
|
|
|
|
def table_factory(options={})
|
|
create_table_with_options(@user_1, @headers, options)
|
|
end
|
|
|
|
def api_visualization_creation(user, headers, additional_fields = {})
|
|
post api_v1_visualizations_create_url(user_domain: user.username, api_key: user.api_key), factory(user).merge(additional_fields).to_json, headers
|
|
id = JSON.parse(last_response.body).fetch('id')
|
|
id.should_not be_nil
|
|
CartoDB::Visualization::Member.new(id: id).fetch
|
|
end
|
|
|
|
def test_organization
|
|
organization = Organization.new
|
|
organization.name = unique_name('org')
|
|
organization.quota_in_bytes = 1234567890
|
|
organization.seats = 5
|
|
organization.builder_enabled = false
|
|
organization
|
|
end
|
|
|
|
def create_geometry_table(user, the_geom)
|
|
table = new_table(privacy: UserTable::PRIVACY_PUBLIC, user_id: user.id)
|
|
table.force_schema = "the_geom geometry"
|
|
table.the_geom_type = "point"
|
|
table.save.reload
|
|
table.insert_row!(the_geom: the_geom)
|
|
table.update_bounding_box
|
|
table
|
|
end
|
|
|
|
end
|