parent
f880f9a1c7
commit
7a35796d9e
@ -0,0 +1,19 @@
|
||||
module Carto
|
||||
module Api
|
||||
class SnapshotPresenter
|
||||
def initialize(snapshot)
|
||||
@snapshot = snapshot
|
||||
end
|
||||
|
||||
def to_hash
|
||||
{
|
||||
id: @snapshot.id,
|
||||
created_at: @snapshot.created_at,
|
||||
updated_at: @snapshot.updated_at,
|
||||
state: @snapshot.state,
|
||||
user: Carto::Api::UserPresenter.new(@snapshot.user).to_public_poro
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,86 @@
|
||||
# encoding utf-8
|
||||
|
||||
module Carto
|
||||
module Api
|
||||
class SnapshotsController < ::Api::ApplicationController
|
||||
include ControllerHelper
|
||||
|
||||
ssl_required :index, :show, :create, :update, :destroy
|
||||
|
||||
before_filter :load_visualization,
|
||||
:check_visualization_viewable
|
||||
before_filter :load_snapshot,
|
||||
:owners_only, only: [:show, :update, :destroy]
|
||||
|
||||
rescue_from LoadError,
|
||||
UnauthorizedError,
|
||||
UnprocesableEntityError, with: :rescue_from_carto_error
|
||||
|
||||
def index
|
||||
snapshots = @visualization.snapshots.where(user_id: current_viewer.id)
|
||||
|
||||
presentation = snapshots.map do |snapshot|
|
||||
SnapshotPresenter.new(snapshot).to_hash
|
||||
end
|
||||
|
||||
render json: presentation
|
||||
end
|
||||
|
||||
def show
|
||||
render json: SnapshotPresenter.new(@snapshot).to_hash
|
||||
end
|
||||
|
||||
def create
|
||||
snapshot = Snapshot.create!(user_id: current_viewer.id,
|
||||
visualization_id: @visualization.id,
|
||||
state: params[:state])
|
||||
|
||||
render json: SnapshotPresenter.new(snapshot).to_hash, status: :created
|
||||
rescue ActiveRecord::RecordInvalid => exception
|
||||
message = exception.record.errors.full_messages.join(', ')
|
||||
raise UnprocesableEntityError.new(message)
|
||||
end
|
||||
|
||||
def update
|
||||
@snapshot.update_attributes!(state: params[:state])
|
||||
|
||||
render json: SnapshotPresenter.new(@snapshot).to_hash
|
||||
rescue ActiveRecord::RecordInvalid => exception
|
||||
message = exception.record.errors.full_messages.join(', ')
|
||||
raise UnprocesableEntityError.new(message)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@snapshot.destroy
|
||||
|
||||
render json: Hash.new, status: :no_content
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_visualization
|
||||
@visualization = Visualization.find(params[:visualization_id])
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
raise LoadError.new('Visualization not found')
|
||||
end
|
||||
|
||||
def check_visualization_viewable
|
||||
unless @visualization.is_viewable_by_user?(current_viewer)
|
||||
raise UnauthorizedError.new
|
||||
end
|
||||
end
|
||||
|
||||
def load_snapshot
|
||||
@snapshot = Snapshot.find(params[:id])
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
raise LoadError.new('Snapshot not found')
|
||||
end
|
||||
|
||||
def owners_only
|
||||
unless @snapshot.user_id == current_viewer.id
|
||||
raise UnauthorizedError.new
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,17 @@
|
||||
# encoding utf-8
|
||||
|
||||
require 'json'
|
||||
require_relative './carto_json_serializer'
|
||||
|
||||
module Carto
|
||||
class Snapshot < ActiveRecord::Base
|
||||
belongs_to :visualization, class_name: Carto::Visualization
|
||||
belongs_to :user, class_name: Carto::User
|
||||
|
||||
default_scope order('created_at DESC')
|
||||
|
||||
serialize :state, ::Carto::CartoJsonSymbolizerSerializer
|
||||
validates :state, carto_json_symbolizer: true
|
||||
validates :visualization, :user, presence: true
|
||||
end
|
||||
end
|
@ -0,0 +1,32 @@
|
||||
# encoding: utf-8
|
||||
|
||||
require 'spec_helper_min'
|
||||
require 'factories/carto_visualizations'
|
||||
|
||||
describe Carto::Snapshot do
|
||||
include Carto::Factories::Visualizations
|
||||
|
||||
before(:all) do
|
||||
@user = FactoryGirl.create(:carto_user)
|
||||
@_m, @_t, @_tv, @visualization = create_full_visualization(@user)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
destroy_full_visualization(@_m, @_t, @_tv, @visualization)
|
||||
@user.destroy
|
||||
end
|
||||
|
||||
describe('#validation') do
|
||||
it 'rejects nil visualization' do
|
||||
snapshot = Carto::Snapshot.new(user_id: @user.id)
|
||||
snapshot.save.should be_false
|
||||
snapshot.errors[:visualization].should_not be_empty
|
||||
end
|
||||
|
||||
it 'rejects nil user' do
|
||||
snapshot = Carto::Snapshot.new(visualization_id: @visualization.id)
|
||||
snapshot.save.should be_false
|
||||
snapshot.errors[:user].should_not be_empty
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,414 @@
|
||||
# encoding utf-8
|
||||
|
||||
require 'spec_helper_min'
|
||||
require 'support/helpers'
|
||||
require 'factories/carto_visualizations'
|
||||
|
||||
describe Carto::Api::SnapshotsController do
|
||||
include Carto::Factories::Visualizations
|
||||
include HelperMethods
|
||||
|
||||
let(:fake_state) { { manolo: 'escobar' } }
|
||||
|
||||
before(:all) do
|
||||
@user = FactoryGirl.create(:carto_user)
|
||||
@intruder = FactoryGirl.create(:carto_user)
|
||||
@_m, @_t, @_tv, @visualization = create_full_visualization(@user)
|
||||
@_om, @_ot, @_otv, @other_visualization = create_full_visualization(@user)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
destroy_full_visualzation(@_m, @_t, @_tv, @visualization)
|
||||
destroy_full_visualzation(@_om, @_ot, @_otv, @other_visualization)
|
||||
@intruder.destroy
|
||||
@user.destroy
|
||||
end
|
||||
|
||||
describe('#index') do
|
||||
def snapshots_index_url(user_domain: @user.subdomain,
|
||||
visualization_id: @visualization.id,
|
||||
api_key: @user.api_key)
|
||||
snapshots_url(user_domain: user_domain,
|
||||
visualization_id: visualization_id,
|
||||
api_key: api_key)
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
5.times do
|
||||
Carto::Snapshot.create!(user_id: @user.id,
|
||||
visualization_id: @visualization.id,
|
||||
state: fake_state)
|
||||
end
|
||||
|
||||
@buddy = FactoryGirl.create(:carto_user)
|
||||
5.times do
|
||||
Carto::Snapshot.create!(user_id: @buddy.id,
|
||||
visualization_id: @visualization.id,
|
||||
state: fake_state)
|
||||
end
|
||||
|
||||
5.times do
|
||||
Carto::Snapshot.create!(user_id: @buddy.id,
|
||||
visualization_id: @other_visualization.id,
|
||||
state: fake_state)
|
||||
end
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
Carto::Snapshot.where(user_id: @user.id).map(&:destroy)
|
||||
Carto::Snapshot.where(user_id: @buddy.id).map(&:destroy)
|
||||
|
||||
@buddy.destroy
|
||||
end
|
||||
|
||||
it 'rejects unauthenticated access' do
|
||||
Carto::Visualization.any_instance
|
||||
.stubs(:is_publically_accesible?)
|
||||
.returns(false)
|
||||
|
||||
get_json(snapshots_index_url(api_key: nil), Hash.new) do |response|
|
||||
response.status.should eq 401
|
||||
end
|
||||
end
|
||||
|
||||
it 'rejects users with no read access' do
|
||||
Carto::Visualization.any_instance
|
||||
.stubs(:is_viewable_by_user?)
|
||||
.returns(false)
|
||||
|
||||
intruder_url = snapshots_index_url(user_domain: @intruder.subdomain,
|
||||
api_key: @intruder.api_key)
|
||||
get_json(intruder_url, Hash.new) do |response|
|
||||
response.status.should eq 403
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns 404 for non existent visualizations' do
|
||||
not_found_url = snapshots_index_url(visualization_id: random_uuid)
|
||||
get_json(not_found_url, Hash.new) do |response|
|
||||
response.status.should eq 404
|
||||
end
|
||||
end
|
||||
|
||||
it 'should not list visualization state for owner' do
|
||||
get_json(snapshots_index_url, Hash.new) do |response|
|
||||
response.status.should eq 200
|
||||
|
||||
response_ids = response.body
|
||||
.map { |snapshot| snapshot['id'] }
|
||||
.compact
|
||||
.sort
|
||||
response_ids.should_not be_empty
|
||||
|
||||
response_ids.should_not include(@visualization.id)
|
||||
end
|
||||
end
|
||||
|
||||
it 'lists only snapshots for user and visualization' do
|
||||
buddy_url = snapshots_index_url(user_domain: @buddy.subdomain,
|
||||
api_key: @buddy.api_key)
|
||||
|
||||
buddy_snaps = Carto::Snapshot.where(user_id: @buddy.id,
|
||||
visualization_id: @visualization.id)
|
||||
.map(&:id)
|
||||
.sort
|
||||
|
||||
get_json(buddy_url, Hash.new) do |response|
|
||||
response.status.should eq 200
|
||||
|
||||
response_ids = response.body
|
||||
.map { |snapshot| snapshot['id'] }
|
||||
.compact
|
||||
.sort
|
||||
response_ids.should_not be_empty
|
||||
|
||||
response_ids.should eq buddy_snaps
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe('#show') do
|
||||
def snapshots_show_url(user_domain: @user.subdomain,
|
||||
visualization_id: @visualization.id,
|
||||
snapshot_id: @snapshot.id,
|
||||
api_key: @user.api_key)
|
||||
snapshot_url(user_domain: user_domain,
|
||||
visualization_id: visualization_id,
|
||||
id: snapshot_id,
|
||||
api_key: api_key)
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
@snapshot = Carto::Snapshot.create!(user_id: @user.id,
|
||||
visualization_id: @visualization.id,
|
||||
state: fake_state)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
@snapshot.destroy
|
||||
end
|
||||
|
||||
it 'rejects unauthenticated access' do
|
||||
Carto::Visualization.any_instance
|
||||
.stubs(:is_publically_accesible?)
|
||||
.returns(false)
|
||||
|
||||
get_json(snapshots_show_url(api_key: nil), Hash.new) do |response|
|
||||
response.status.should eq 401
|
||||
end
|
||||
end
|
||||
|
||||
it 'rejects users with no read access' do
|
||||
Carto::Visualization.any_instance
|
||||
.stubs(:is_viewable_by_user?)
|
||||
.returns(false)
|
||||
|
||||
intruder_url = snapshots_show_url(user_domain: @intruder.subdomain,
|
||||
api_key: @intruder.api_key)
|
||||
get_json(intruder_url, Hash.new) do |response|
|
||||
response.status.should eq 403
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns 404 for non existent visualizations' do
|
||||
not_found_url = snapshots_show_url(visualization_id: random_uuid)
|
||||
get_json(not_found_url, Hash.new) do |response|
|
||||
response.status.should eq 404
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns 404 for inexistent snapshots' do
|
||||
not_found_url = snapshots_show_url(snapshot_id: random_uuid)
|
||||
|
||||
get_json(not_found_url, Hash.new) do |response|
|
||||
response.status.should eq 404
|
||||
end
|
||||
end
|
||||
|
||||
it 'only accepts owners of snapshots' do
|
||||
intruder_url = snapshots_show_url(user_domain: @intruder.subdomain,
|
||||
api_key: @intruder.api_key)
|
||||
get_json(intruder_url, Hash.new) do |response|
|
||||
response.status.should eq 403
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows a snapshot' do
|
||||
get_json(snapshots_show_url, Hash.new) do |response|
|
||||
response.status.should eq 200
|
||||
response.body[:id].should eq @snapshot.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe('#create') do
|
||||
def snapshots_create_url(user_domain: @user.subdomain,
|
||||
visualization_id: @visualization.id,
|
||||
api_key: @user.api_key)
|
||||
snapshots_url(user_domain: user_domain,
|
||||
visualization_id: visualization_id,
|
||||
api_key: api_key)
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
@user.visualizations.map(&:snapshots).flatten.map(&:destroy)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
@user.visualizations.map(&:snapshots).flatten.map(&:destroy)
|
||||
end
|
||||
|
||||
it 'rejects unauthenticated access' do
|
||||
Carto::Visualization.any_instance
|
||||
.stubs(:is_publically_accesible?)
|
||||
.returns(false)
|
||||
|
||||
nil_api_key_url = snapshots_create_url(api_key: nil)
|
||||
post_json(nil_api_key_url, state: fake_state) do |response|
|
||||
response.status.should eq 401
|
||||
end
|
||||
end
|
||||
|
||||
it 'rejects users with no read access' do
|
||||
Carto::Visualization.any_instance
|
||||
.stubs(:is_viewable_by_user?)
|
||||
.returns(false)
|
||||
|
||||
intruder_url = snapshots_create_url(user_domain: @intruder.subdomain,
|
||||
api_key: @intruder.api_key)
|
||||
post_json(intruder_url, state: fake_state) do |response|
|
||||
response.status.should eq 403
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns 404 for non existent visualizations' do
|
||||
not_found_url = snapshots_create_url(visualization_id: random_uuid)
|
||||
post_json(not_found_url, state: fake_state) do |response|
|
||||
response.status.should eq 404
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates a snapshot' do
|
||||
@visualization.snapshots.count.should eq 0
|
||||
|
||||
post_json(snapshots_create_url, state: fake_state) do |response|
|
||||
response.status.should eq 201
|
||||
|
||||
@visualization.reload
|
||||
|
||||
@visualization.snapshots.count.should eq 1
|
||||
@visualization.snapshots.first.id.should eq response.body[:id]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe('#update') do
|
||||
def snapshots_update_url(user_domain: @user.subdomain,
|
||||
visualization_id: @visualization.id,
|
||||
snapshot_id: @snapshot.id,
|
||||
api_key: @user.api_key)
|
||||
snapshot_url(user_domain: user_domain,
|
||||
visualization_id: visualization_id,
|
||||
id: snapshot_id,
|
||||
api_key: api_key)
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
@snapshot = Carto::Snapshot.create!(user_id: @user.id,
|
||||
visualization_id: @visualization.id,
|
||||
state: fake_state)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
@snapshot.destroy
|
||||
end
|
||||
|
||||
it 'rejects unauthenticated access' do
|
||||
Carto::Visualization.any_instance
|
||||
.stubs(:is_publically_accesible?)
|
||||
.returns(false)
|
||||
|
||||
put_json(snapshots_update_url(api_key: nil), Hash.new) do |response|
|
||||
response.status.should eq 401
|
||||
end
|
||||
end
|
||||
|
||||
it 'rejects users with no read access' do
|
||||
Carto::Visualization.any_instance
|
||||
.stubs(:is_viewable_by_user?)
|
||||
.returns(false)
|
||||
|
||||
intruder_url = snapshots_update_url(user_domain: @intruder.subdomain,
|
||||
api_key: @intruder.api_key)
|
||||
put_json(intruder_url, Hash.new) do |response|
|
||||
response.status.should eq 403
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns 404 for non existent visualizations' do
|
||||
not_found_url = snapshots_update_url(visualization_id: random_uuid)
|
||||
put_json(not_found_url, Hash.new) do |response|
|
||||
response.status.should eq 404
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns 404 for inexistent snapshots' do
|
||||
not_found_url = snapshots_update_url(snapshot_id: random_uuid)
|
||||
|
||||
put_json(not_found_url, Hash.new) do |response|
|
||||
response.status.should eq 404
|
||||
end
|
||||
end
|
||||
|
||||
it 'only accepts owners of snapshots' do
|
||||
intruder_url = snapshots_update_url(user_domain: @intruder.subdomain,
|
||||
api_key: @intruder.api_key)
|
||||
put_json(intruder_url, Hash.new) do |response|
|
||||
response.status.should eq 403
|
||||
end
|
||||
end
|
||||
|
||||
it 'updates a snapshot' do
|
||||
new_state = { minili: 'iscibir' }
|
||||
|
||||
put_json(snapshots_update_url, state: new_state) do |response|
|
||||
response.status.should eq 200
|
||||
end
|
||||
|
||||
@snapshot.reload.state.should eq new_state
|
||||
end
|
||||
end
|
||||
|
||||
describe('#destroy') do
|
||||
def snapshots_update_url(user_domain: @user.subdomain,
|
||||
visualization_id: @visualization.id,
|
||||
snapshot_id: @snapshot.id,
|
||||
api_key: @user.api_key)
|
||||
snapshot_url(user_domain: user_domain,
|
||||
visualization_id: visualization_id,
|
||||
id: snapshot_id,
|
||||
api_key: api_key)
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
@snapshot = Carto::Snapshot.create!(user_id: @user.id,
|
||||
visualization_id: @visualization.id,
|
||||
state: fake_state)
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
@snapshot.destroy
|
||||
end
|
||||
|
||||
it 'rejects unauthenticated access' do
|
||||
Carto::Visualization.any_instance
|
||||
.stubs(:is_publically_accesible?)
|
||||
.returns(false)
|
||||
|
||||
delete_json(snapshots_update_url(api_key: nil), Hash.new) do |response|
|
||||
response.status.should eq 401
|
||||
end
|
||||
end
|
||||
|
||||
it 'rejects users with no read access' do
|
||||
Carto::Visualization.any_instance
|
||||
.stubs(:is_viewable_by_user?)
|
||||
.returns(false)
|
||||
|
||||
intruder_url = snapshots_update_url(user_domain: @intruder.subdomain,
|
||||
api_key: @intruder.api_key)
|
||||
delete_json(intruder_url, Hash.new) do |response|
|
||||
response.status.should eq 403
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns 404 for non existent visualizations' do
|
||||
not_found_url = snapshots_update_url(visualization_id: random_uuid)
|
||||
delete_json(not_found_url, Hash.new) do |response|
|
||||
response.status.should eq 404
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns 404 for inexistent snapshots' do
|
||||
not_found_url = snapshots_update_url(snapshot_id: random_uuid)
|
||||
|
||||
delete_json(not_found_url, Hash.new) do |response|
|
||||
response.status.should eq 404
|
||||
end
|
||||
end
|
||||
|
||||
it 'only accepts owners of snapshots' do
|
||||
intruder_url = snapshots_update_url(user_domain: @intruder.subdomain,
|
||||
api_key: @intruder.api_key)
|
||||
delete_json(intruder_url, Hash.new) do |response|
|
||||
response.status.should eq 403
|
||||
end
|
||||
end
|
||||
|
||||
it 'destroys a snapshot' do
|
||||
delete_json(snapshots_update_url, Hash.new) do |response|
|
||||
response.status.should eq 204
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in new issue