435 lines
18 KiB
Ruby
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
|