cartodb/spec/requests/carto/api/vizjson_shared_examples.rb
2020-06-15 10:58:47 +08:00

435 lines
18 KiB
Ruby

# Contains specs that both vizjson2 and 3 must verify.
# Specs including this should define:
# - api_vx_visualizations_vizjson_url: an alias for the desired URL generator.
# - vizjson_vx_version: vizjson version.
# Example:
# 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
# end
shared_examples_for 'vizjson generator' do
describe 'vizjson2 compatibility' do
include_context 'visualization creation helpers'
include_context 'users helper'
TEST_UUID = '00000000-0000-0000-0000-000000000000'.freeze
before(:all) do
bypass_named_maps
@user_1 = FactoryGirl.create(:valid_user, private_tables_enabled: false)
@api_key = @user_1.api_key
@user_2 = FactoryGirl.create(:valid_user)
@headers = { 'CONTENT_TYPE' => 'application/json' }
host! "#{@user_1.subdomain}.localhost.lan"
end
after(:all) do
@user_1.destroy
@user_2.destroy
end
it 'returns 404 for non existing visualizations' do
get api_vx_visualizations_vizjson_url(id: TEST_UUID, api_key: @api_key), {}, http_json_headers
last_response.status.should == 404
end
it 'tests privacy of vizjsons' do
Carto::NamedMaps::Api.any_instance.stubs(show: { template: { auth: { valid_tokens: ['mammas', 'pappas'] } } },
create: true,
update: true,
destroy: true)
user_1 = create_user(
username: "test#{rand(9999)}-1",
email: "client#{rand(9999)}@carto.com",
password: 'clientex',
private_tables_enabled: true
)
user_2 = create_user(
username: "test#{rand(9999)}-2",
email: "client#{rand(9999)}@carto.com",
password: 'clientex',
private_tables_enabled: true
)
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
post api_v1_visualizations_create_url(user_domain: user_1.username, api_key: user_1.api_key),
factory(user_1).to_json, http_json_headers
last_response.status.should == 200
body = JSON.parse(last_response.body)
u1_vis_1_id = body.fetch('id')
u1_vis_1_perm_id = body.fetch('permission').fetch('id')
# By default derived vis from private tables are WITH_LINK, so setprivate
put api_v1_visualizations_update_url(user_domain: user_1.username, id: u1_vis_1_id, api_key: user_1.api_key),
{
id: u1_vis_1_id,
privacy: CartoDB::Visualization::Member::PRIVACY_PRIVATE
}.to_json, http_json_headers
last_response.status.should == 200
# Share vis with user_2 in readonly (vis can never be shared in readwrite)
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, http_json_headers
last_response.status.should == 200
# privacy private checks
# ----------------------
# Owner, authenticated
get api_vx_visualizations_vizjson_url(user_domain: user_1.username, id: u1_vis_1_id, api_key: user_1.api_key)
last_response.status.should == 200
body = JSON.parse(last_response.body)
body['id'].should eq u1_vis_1_id
# Other user, has it shared in readonly mode
get api_vx_visualizations_vizjson_url(user_domain: user_2.username, id: u1_vis_1_id, api_key: user_2.api_key)
last_response.status.should == 200
body = JSON.parse(last_response.body)
body['id'].should eq u1_vis_1_id
# Unauthenticated user
get api_vx_visualizations_vizjson_url(user_domain: user_1.username, id: u1_vis_1_id, api_key: @user_1.api_key)
last_response.status.should == 403
# Unauthenticated user
get api_vx_visualizations_vizjson_url(user_domain: user_1.username, id: u1_vis_1_id, api_key: @user_1.api_key)
last_response.status.should == 403
# Now with link
# -------------
put api_v1_visualizations_update_url(user_domain: user_1.username, id: u1_vis_1_id, api_key: user_1.api_key),
{
id: u1_vis_1_id,
privacy: CartoDB::Visualization::Member::PRIVACY_LINK
}.to_json, http_json_headers
last_response.status.should == 200
# Owner authenticated
get api_vx_visualizations_vizjson_url(user_domain: user_1.username, id: u1_vis_1_id, api_key: user_1.api_key)
last_response.status.should == 200
body = JSON.parse(last_response.body)
body['id'].should eq u1_vis_1_id
# Other user has it shared in readonly mode
get api_vx_visualizations_vizjson_url(user_domain: user_2.username, id: u1_vis_1_id, api_key: user_2.api_key)
last_response.status.should == 200
body = JSON.parse(last_response.body)
body['id'].should eq u1_vis_1_id
# Unauthenticated user
get api_vx_visualizations_vizjson_url(user_domain: user_1.username, id: u1_vis_1_id, api_key: @user_1.api_key)
last_response.status.should == 200
body = JSON.parse(last_response.body)
body['id'].should eq u1_vis_1_id
# Now public
# ----------
put api_v1_visualizations_update_url(user_domain: user_1.username, id: u1_vis_1_id, api_key: user_1.api_key),
{
id: u1_vis_1_id,
privacy: CartoDB::Visualization::Member::PRIVACY_LINK
}.to_json, http_json_headers
last_response.status.should == 200
get api_vx_visualizations_vizjson_url(user_domain: user_1.username, id: u1_vis_1_id, api_key: user_1.api_key)
last_response.status.should == 200
body = JSON.parse(last_response.body)
body['id'].should eq u1_vis_1_id
# Other user has it shared in readonly mode
get api_vx_visualizations_vizjson_url(user_domain: user_2.username, id: u1_vis_1_id, api_key: user_2.api_key)
last_response.status.should == 200
body = JSON.parse(last_response.body)
body['id'].should eq u1_vis_1_id
# Unauthenticated user
get api_vx_visualizations_vizjson_url(user_domain: user_1.username, id: u1_vis_1_id, api_key: @user_1.api_key)
last_response.status.should == 200
body = JSON.parse(last_response.body)
body['id'].should eq u1_vis_1_id
# Check visualization id under wrong subdomain triggers 404
get api_vx_visualizations_vizjson_url(user_domain: @user_1.username, id: u1_vis_1_id, api_key: @user_1.api_key)
last_response.status.should == 404
# Check visualization id under shared with user subdomain triggers 200
get api_vx_visualizations_vizjson_url(user_domain: user_2.username, id: u1_vis_1_id, api_key: user_2.api_key)
last_response.status.should == 200
end
it 'Sanitizes vizjson callback' do
valid_callback = 'my_function'
valid_callback2 = 'a'
invalid_callback1 = 'alert(1);'
invalid_callback2 = '%3B'
invalid_callback3 = '123func' # JS names cannot start by number
viz = api_visualization_creation(@user_1, @headers, { privacy: Visualization::Member::PRIVACY_PUBLIC, type: Visualization::Member::TYPE_DERIVED })
viz_id = viz.id
get api_vx_visualizations_vizjson_url(id: viz_id, api_key: @api_key, callback: valid_callback), {}, @headers
last_response.status.should == 200
(last_response.body =~ /^(\/\*\*\/)?#{valid_callback}\(\{/i).should eq 0
get api_vx_visualizations_vizjson_url(id: viz_id, api_key: @api_key, callback: invalid_callback1), {}, @headers
last_response.status.should == 400
get api_vx_visualizations_vizjson_url(id: viz_id, api_key: @api_key, callback: invalid_callback2), {}, @headers
last_response.status.should == 400
get api_vx_visualizations_vizjson_url(id: viz_id, api_key: @api_key, callback: invalid_callback3), {}, @headers
last_response.status.should == 400
# if param specified, must not be empty
get api_vx_visualizations_vizjson_url(id: viz_id, api_key: @api_key, callback: ''), {}, @headers
last_response.status.should == 400
get api_vx_visualizations_vizjson_url(id: viz_id, api_key: @api_key, callback: valid_callback2), {}, @headers
last_response.status.should == 200
(last_response.body =~ /^(\/\*\*\/)?#{valid_callback2}\(\{/i).should eq 0
get api_vx_visualizations_vizjson_url(id: viz_id, api_key: @api_key), {}, @headers
last_response.status.should == 200
(last_response.body =~ /^\{/i).should eq 0
end
describe 'get vizjson' do
before(:each) do
Carto::NamedMaps::Api.any_instance.stubs(show: nil, create: true, update: true, destroy: true)
delete_user_data(@user_1)
end
it 'renders vizjson vx' do
viz = api_visualization_creation(@user_1, @headers, { privacy: Visualization::Member::PRIVACY_PUBLIC, type: Visualization::Member::TYPE_DERIVED })
get api_vx_visualizations_vizjson_url(id: viz.id, api_key: @api_key),
{}, @headers
last_response.status.should == 200
::JSON.parse(last_response.body).keys.length.should > 1
end
it 'returns 200 if subdomain is empty' do
viz = api_visualization_creation(@user_1, @headers, { privacy: Visualization::Member::PRIVACY_PUBLIC, type: Visualization::Member::TYPE_DERIVED })
# INFO: I couldn't get rid of subdomain, so I stubbed
CartoDB.stubs(:extract_subdomain).returns('')
get api_vx_visualizations_vizjson_url(id: viz.id)
last_response.status.should == 200
end
it 'returns 200 if subdomain matches' do
viz = api_visualization_creation(@user_1, @headers, { privacy: Visualization::Member::PRIVACY_PUBLIC, type: Visualization::Member::TYPE_DERIVED })
get api_vx_visualizations_vizjson_url(user_domain: @user_1.username, id: viz.id)
last_response.status.should == 200
end
it 'returns 404 if subdomain does not match' do
viz = api_visualization_creation(@user_1, @headers, { privacy: Visualization::Member::PRIVACY_PUBLIC, type: Visualization::Member::TYPE_DERIVED })
get api_vx_visualizations_vizjson_url(user_domain: 'whatever', id: viz.id)
last_response.status.should == 404
end
it "comes with proper surrogate-key" do
Carto::NamedMaps::Api.any_instance.stubs(get: nil, create: true, update: true)
table = table_factory(privacy: 1)
source_visualization = table.fetch('table_visualization')
map_id = source_visualization[:map_id]
derived_visualization = FactoryGirl.create(:derived_visualization, user_id: @user_1.id, map_id: map_id)
viz_id = derived_visualization.id
put api_v1_visualizations_show_url(user_domain: @user_1.username, id: viz_id, api_key: @api_key),
{ privacy: Carto::Visualization::PRIVACY_PUBLIC, id: viz_id }.to_json, @headers
get api_vx_visualizations_vizjson_url(user_domain: @user_1.username, id: viz_id, api_key: @api_key),
{ id: viz_id }, @headers
last_response.status.should == 200
last_response.headers.should have_key('Surrogate-Key')
last_response['Surrogate-Key'].should include(CartoDB::SURROGATE_NAMESPACE_VIZJSON)
last_response['Surrogate-Key'].should include(get_surrogate_key(CartoDB::SURROGATE_NAMESPACE_VISUALIZATION, viz_id))
delete api_v1_visualizations_destroy_url(user_domain: @user_1.username, id: viz_id, api_key: @api_key),
{ id: viz_id }.to_json,
@headers
end
it 'joins the attributions of the layers in a layergroup in the viz.json' do
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
# Set the attributions of the tables to check that they are included in the viz.json
table1_visualization = Carto::Visualization.find(table1["table_visualization"][:id])
table1_visualization.update_attribute(:attributions, 'attribution1')
table2_visualization = Carto::Visualization.find(table2["table_visualization"][:id])
table2_visualization.update_attribute(:attributions, 'attribution2')
table3_visualization = Carto::Visualization.find(table3["table_visualization"][:id])
table3_visualization.update_attribute(:attributions, '')
visualization = JSON.parse(last_response.body)
get api_vx_visualizations_vizjson_url(id: visualization.fetch('id'), api_key: @api_key), {}, @headers
visualization = JSON.parse(last_response.body)
# Attribution of the layergroup layer is right
layer_group_attributions = attributions_from_vizjson(visualization)
layer_group_attributions.size.should == 2
layer_group_attributions.should include('attribution1')
layer_group_attributions.should include('attribution2')
end
it 'joins the attributions of the layers in a namedmap in the viz.json' do
table1 = table_factory
table2 = table_factory
table3 = table_factory
payload = {
name: 'new visualization',
tables: [
table1.fetch('name'),
table2.fetch('name'),
table3.fetch('name')
],
privacy: 'private'
}
post api_v1_visualizations_create_url(api_key: @api_key), payload.to_json, @headers
last_response.status.should eq 200
# Set the attributions of the tables to check that they are included in the viz.json
table1_visualization = Carto::Visualization.find(table1["table_visualization"][:id])
table1_visualization.update_attribute(:attributions, 'attribution1')
table2_visualization = Carto::Visualization.find(table2["table_visualization"][:id])
table2_visualization.update_attribute(:attributions, 'attribution2')
table3_visualization = Carto::Visualization.find(table3["table_visualization"][:id])
table3_visualization.update_attribute(:attributions, '')
visualization = JSON.parse(last_response.body)
# Stubs privacy of the visualization so that the viz_json generates a named_map
Carto::Visualization.any_instance.stubs('retrieve_named_map?' => true)
get api_vx_visualizations_vizjson_url(id: visualization.fetch('id'), api_key: @api_key), {}, @headers
visualization = JSON.parse(last_response.body)
named_map_attributions = attributions_from_vizjson(visualization)
named_map_attributions.size.should == 2
named_map_attributions.should include('attribution1')
named_map_attributions.should include('attribution2')
end
it "Updates viz.json's layergroup when attributions in a related layer change" do
table_1_attribution = 'attribution 1'
modified_table_2_attribution = 'modified attribution 2'
table1 = table_factory
table2 = table_factory
payload = {
name: 'new visualization',
tables: [
table1.fetch('name'),
table2.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)
table1_visualization = Carto::Visualization.find(table1["table_visualization"][:id])
table1_visualization.update_attribute(:attributions, table_1_attribution)
table2_visualization = Carto::Visualization.find(table2["table_visualization"][:id])
table2_visualization.update_attribute(:attributions, 'attribution 2')
# Call to cache the vizjson after generating it
get api_vx_visualizations_vizjson_url(id: visualization.fetch('id'), api_key: @api_key),
{ id: visualization.fetch('id') },
@headers
# Now force a change
put api_v1_visualizations_update_url(api_key: @api_key, id: table2_visualization.id),
{ attributions: modified_table_2_attribution, id: table2_visualization.id }.to_json,
@headers
last_response.status.should == 200
get api_vx_visualizations_vizjson_url(id: visualization.fetch('id'), api_key: @api_key),
{ id: visualization.fetch('id') },
@headers
visualization = JSON.parse(last_response.body)
layer_group_attributions = attributions_from_vizjson(visualization)
layer_group_attributions.size.should eq 2
layer_group_attributions.should include(table_1_attribution)
layer_group_attributions.should include(modified_table_2_attribution)
end
end
end
describe 'attributes' do
include Carto::Factories::Visualizations
include_context 'visualization creation helpers'
before(:all) do
@user_1 = FactoryGirl.create(:valid_user, private_tables_enabled: false)
host! "#{@user_1.subdomain}.localhost.lan"
@map, @table, @table_visualization, @visualization = create_full_visualization(Carto::User.find(@user_1.id))
end
after(:all) do
destroy_full_visualization(@map, @table, @table_visualization, @visualization)
@user_1.destroy
end
let(:viewer_user) { @visualization.user }
it 'contain type, not kind, for basemaps' do
get api_vx_visualizations_vizjson_url(id: @visualization.id, api_key: @api_key), {}, http_json_headers
last_response.status.should eq 200
visualization = JSON.parse(last_response.body)
basemap_layer = visualization["layers"][0]
basemap_layer['type'].should eq @visualization.map.base_layers.first.kind
basemap_layer['kind'].should be_nil
end
end
end