410 lines
19 KiB
Ruby
410 lines
19 KiB
Ruby
|
require 'spec_helper_min'
|
||
|
require 'support/helpers'
|
||
|
|
||
|
describe Carto::FederatedTablesService do
|
||
|
include_context 'users helper'
|
||
|
include HelperMethods
|
||
|
|
||
|
def remote_query(query)
|
||
|
status = system("PGPASSWORD='#{@remote_password}' #{@psql} -q -U #{@remote_username} -d #{@remote_database} -h #{@remote_host} -p #{@remote_port} -c \"#{query};\"")
|
||
|
raise "Failed to execute remote query: #{query}" unless status
|
||
|
end
|
||
|
|
||
|
def get_federated_server_payload(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
mode: 'read-only',
|
||
|
dbname: @remote_database,
|
||
|
host: @remote_host,
|
||
|
port: @remote_port,
|
||
|
username: @remote_username,
|
||
|
password: @remote_username
|
||
|
)
|
||
|
{
|
||
|
federated_server_name: federated_server_name,
|
||
|
mode: mode,
|
||
|
dbname: dbname,
|
||
|
host: host,
|
||
|
port: port,
|
||
|
username: username,
|
||
|
password: password
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def create_federated_server(**attributes)
|
||
|
@service.register_server(get_federated_server_payload(attributes))
|
||
|
end
|
||
|
|
||
|
def update_federated_server(federated_server_name:, **attributes)
|
||
|
@service.update_server(get_federated_server_payload(federated_server_name: federated_server_name, **attributes))
|
||
|
end
|
||
|
|
||
|
FEDERATED_SERVER_ATTRIBUTES = %i(federated_server_name mode dbname host port username password).freeze
|
||
|
GRANT_SERVER_ACCESS_ATTRIBUTES = %i(federated_server_name db_role).freeze
|
||
|
|
||
|
def get_grant_access_to_federated_server_payload(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
db_role: @user1.database_username
|
||
|
)
|
||
|
{
|
||
|
federated_server_name: federated_server_name,
|
||
|
db_role: db_role
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def grant_access_to_federated_server(**attributes)
|
||
|
@service.grant_access_to_federated_server(get_grant_access_to_federated_server_payload(attributes))
|
||
|
end
|
||
|
|
||
|
def create_and_grant_federated_server(**attributes)
|
||
|
server_attributes = attributes.slice(*FEDERATED_SERVER_ATTRIBUTES)
|
||
|
federated_server = create_federated_server(server_attributes)
|
||
|
grant_server_attributes = attributes.slice(*GRANT_SERVER_ACCESS_ATTRIBUTES)
|
||
|
grant_access_to_federated_server(grant_server_attributes)
|
||
|
return federated_server
|
||
|
end
|
||
|
|
||
|
REMOTE_TABLE_ATTRIBUTES = %i(federated_server_name remote_schema_name remote_table_name local_table_name_override id_column_name geom_column_name webmercator_column_name).freeze
|
||
|
|
||
|
def get_remote_table_server_payload(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
remote_schema_name: @remote_schema_name,
|
||
|
remote_table_name: @remote_table_name,
|
||
|
local_table_name_override: @remote_table_name,
|
||
|
id_column_name: 'id',
|
||
|
geom_column_name: 'geom',
|
||
|
webmercator_column_name: 'geom_webmercator'
|
||
|
)
|
||
|
{
|
||
|
federated_server_name: federated_server_name,
|
||
|
remote_schema_name: remote_schema_name,
|
||
|
remote_table_name: remote_table_name,
|
||
|
local_table_name_override: local_table_name_override,
|
||
|
id_column_name: id_column_name,
|
||
|
geom_column_name: geom_column_name,
|
||
|
webmercator_column_name: webmercator_column_name
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def register_remote_table(**attributes)
|
||
|
create_and_grant_federated_server(attributes)
|
||
|
remote_table_attributes = attributes.slice(*REMOTE_TABLE_ATTRIBUTES)
|
||
|
@service.register_table(get_remote_table_server_payload(remote_table_attributes))
|
||
|
end
|
||
|
|
||
|
before(:all) do
|
||
|
puts "Starting remote server"
|
||
|
@dir = Cartodb.get_config(:federated_server, 'dir')
|
||
|
port = Cartodb.get_config(:federated_server, 'port')
|
||
|
user = Cartodb.get_config(:federated_server, 'test_user')
|
||
|
pg_bindir = Cartodb.get_config(:federated_server, 'pg_bindir_path')
|
||
|
unless pg_bindir.present?
|
||
|
pg_bindir = `pg_config --bindir`.delete!("\n")
|
||
|
end
|
||
|
@pg_ctl = "#{pg_bindir}/pg_ctl"
|
||
|
@psql = "#{pg_bindir}/psql"
|
||
|
|
||
|
raise "Federated server directory is not configured!" unless @dir.present?
|
||
|
raise "Federated server port is not configured!" unless port.present?
|
||
|
raise "Federated server user is not configured!" unless user.present?
|
||
|
raise "Binary 'psql' could not be found" unless system("which #{@psql}")
|
||
|
|
||
|
@remote_host = "127.0.0.1"
|
||
|
@remote_port = "#{port}"
|
||
|
@remote_database = "#{user}"
|
||
|
@remote_username = "#{user}"
|
||
|
@remote_password = "#{user}"
|
||
|
end
|
||
|
|
||
|
before(:each) do
|
||
|
@service = Carto::FederatedTablesService.new(user: @user1)
|
||
|
end
|
||
|
|
||
|
after(:each) do
|
||
|
if @federated_server_name.present?
|
||
|
@service.unregister_server(federated_server_name: @federated_server_name)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
after(:all) do
|
||
|
end
|
||
|
|
||
|
describe 'federated server service' do
|
||
|
describe 'federated server' do
|
||
|
it 'should return a empty collection of federated server' do
|
||
|
pagination = { page: 1, per_page: 10, order: 'federated_server_name', direction: 'asc', offset: 0 }
|
||
|
|
||
|
federated_server_list = @service.list_servers(pagination)
|
||
|
|
||
|
expect(federated_server_list).to be_empty
|
||
|
end
|
||
|
|
||
|
it 'should return a collection with one federated server' do
|
||
|
@federated_server_name = "fs_001_from_#{@user1.username}_to_remote"
|
||
|
federated_server = create_federated_server()
|
||
|
|
||
|
pagination = { page: 1, per_page: 10, order: 'federated_server_name', direction: 'asc', offset: 0 }
|
||
|
federated_server_list = @service.list_servers(pagination)
|
||
|
|
||
|
expect(federated_server_list.length()).to eq(1)
|
||
|
expect(federated_server_list[0][:federated_server_name]).to eq(federated_server[:federated_server_name])
|
||
|
expect(federated_server_list[0][:mode]).to eq(federated_server[:mode])
|
||
|
expect(federated_server_list[0][:dbname]).to eq(federated_server[:dbname])
|
||
|
expect(federated_server_list[0][:host]).to eq(federated_server[:host])
|
||
|
expect(federated_server_list[0][:port]).to eq(federated_server[:port])
|
||
|
end
|
||
|
|
||
|
it 'should register a federated server' do
|
||
|
@federated_server_name = "fs_002_from_#{@user1.username}_to_remote"
|
||
|
expected_federated_server = get_federated_server_payload()
|
||
|
federated_server = create_federated_server()
|
||
|
|
||
|
expect(federated_server[:federated_server_name]).to eq(@federated_server_name)
|
||
|
expect(federated_server[:mode]).to eq(expected_federated_server[:mode])
|
||
|
expect(federated_server[:dbname]).to eq(expected_federated_server[:dbname])
|
||
|
expect(federated_server[:host]).to eq(expected_federated_server[:host])
|
||
|
expect(federated_server[:port]).to eq(expected_federated_server[:port])
|
||
|
end
|
||
|
|
||
|
it 'should grant access of a federated server to a role' do
|
||
|
@federated_server_name = "fs_003_from_#{@user1.username}_to_remote"
|
||
|
federated_server = create_federated_server()
|
||
|
|
||
|
expect {
|
||
|
@service.grant_access_to_federated_server(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
db_role: @user1.database_username
|
||
|
)
|
||
|
}.not_to raise_error
|
||
|
end
|
||
|
|
||
|
it 'should get a federated server by name' do
|
||
|
@federated_server_name = "fs_004_from_#{@user1.username}_to_remote"
|
||
|
expected_federated_server = get_federated_server_payload()
|
||
|
create_federated_server()
|
||
|
|
||
|
federated_server = @service.get_server(federated_server_name: @federated_server_name)
|
||
|
|
||
|
expect(federated_server[:federated_server_name]).to eq(@federated_server_name)
|
||
|
expect(federated_server[:mode]).to eq(expected_federated_server[:mode])
|
||
|
expect(federated_server[:dbname]).to eq(expected_federated_server[:dbname])
|
||
|
expect(federated_server[:host]).to eq(expected_federated_server[:host])
|
||
|
expect(federated_server[:port]).to eq(expected_federated_server[:port])
|
||
|
end
|
||
|
|
||
|
it 'should update a federated server by name' do
|
||
|
@federated_server_name = "fs_005_from_#{@user1.username}_to_remote"
|
||
|
expected_federated_server = get_federated_server_payload(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
dbname: @remote_database,
|
||
|
host: @remote_host,
|
||
|
username: @remote_username,
|
||
|
password: @remote_username
|
||
|
)
|
||
|
create_federated_server(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
dbname: @user1.database_name,
|
||
|
host: @user1.database_host,
|
||
|
username: @user1.database_username,
|
||
|
password: @user1.database_password
|
||
|
)
|
||
|
|
||
|
federated_server = update_federated_server(expected_federated_server)
|
||
|
|
||
|
expect(federated_server[:federated_server_name]).to eq(@federated_server_name)
|
||
|
expect(federated_server[:mode]).to eq(expected_federated_server[:mode])
|
||
|
expect(federated_server[:dbname]).to eq(expected_federated_server[:dbname])
|
||
|
expect(federated_server[:host]).to eq(expected_federated_server[:host])
|
||
|
expect(federated_server[:port]).to eq(expected_federated_server[:port])
|
||
|
end
|
||
|
|
||
|
it 'should unregister a federated server by name' do
|
||
|
@federated_server_name = "fs_006_from_#{@user1.username}_to_remote"
|
||
|
expected_federated_server = get_federated_server_payload()
|
||
|
federated_server = create_federated_server()
|
||
|
|
||
|
expect {
|
||
|
@service.unregister_server(federated_server_name: @federated_server_name)
|
||
|
@federated_server_name = nil
|
||
|
}.not_to raise_error
|
||
|
end
|
||
|
|
||
|
it 'should revoke access to a federated server' do
|
||
|
@federated_server_name = "fs_007_from_#{@user1.username}_to_remote"
|
||
|
expected_federated_server = get_federated_server_payload()
|
||
|
create_federated_server(federated_server_name: @federated_server_name)
|
||
|
|
||
|
expect {
|
||
|
@service.revoke_access_to_federated_server(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
db_role: @user1.database_username
|
||
|
)
|
||
|
}.not_to raise_error
|
||
|
end
|
||
|
end
|
||
|
|
||
|
describe 'remote schemas' do
|
||
|
it 'should list remote schemas of a federated server' do
|
||
|
@federated_server_name = "fs_008_from_#{@user1.username}_to_remote"
|
||
|
create_and_grant_federated_server()
|
||
|
remote_schemas = @service.list_remote_schemas(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
page: 1,
|
||
|
per_page: 10,
|
||
|
order: 'remote_schema_name',
|
||
|
direction: 'asc',
|
||
|
offset: 0
|
||
|
)
|
||
|
|
||
|
expect(remote_schemas).to include(:remote_schema_name => "public")
|
||
|
end
|
||
|
|
||
|
it 'should raise "Server [...] does not exist" error when listing remote schemas of a non registered federated server' do
|
||
|
nonregistered_federated_server_name = "fs_999_from_#{@user1.username}_to_remote"
|
||
|
expect {
|
||
|
@service.list_remote_schemas(
|
||
|
federated_server_name: nonregistered_federated_server_name,
|
||
|
page: 1,
|
||
|
per_page: 10,
|
||
|
order: 'remote_schema_name',
|
||
|
direction: 'asc',
|
||
|
offset: 0
|
||
|
)
|
||
|
}.to raise_error(Sequel::DatabaseError, /Server (.*) does not exist/)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
describe 'remote tables' do
|
||
|
before(:each) do
|
||
|
@remote_schema_name = 'aux_schema'
|
||
|
@remote_table_name = 'my_table'
|
||
|
remote_query("CREATE SCHEMA IF NOT EXISTS #{@remote_schema_name}")
|
||
|
remote_query("CREATE TABLE IF NOT EXISTS #{@remote_schema_name}.#{@remote_table_name}(id integer NOT NULL, geom geometry, geom_webmercator geometry)")
|
||
|
end
|
||
|
|
||
|
after(:each) do
|
||
|
remote_query("DROP SCHEMA #{@remote_schema_name} CASCADE")
|
||
|
end
|
||
|
|
||
|
it 'should list unregistered remote table of a federated server and schema' do
|
||
|
@federated_server_name = "fs_010_from_#{@user1.username}_to_remote"
|
||
|
federated_server = create_and_grant_federated_server()
|
||
|
pagination = { page: 1, per_page: 10, order: 'remote_table_name', direction: 'asc', offset: 0 }
|
||
|
remote_tables = @service.list_remote_tables(federated_server_name: @federated_server_name, remote_schema_name: @remote_schema_name, **pagination)
|
||
|
expect(remote_tables).to include(
|
||
|
:registered=>false,
|
||
|
:qualified_name=>nil,
|
||
|
:remote_table_name=>@remote_table_name,
|
||
|
:remote_schema_name=>@remote_schema_name,
|
||
|
:id_column_name=>nil,
|
||
|
:geom_column_name=>nil,
|
||
|
:webmercator_column_name=>nil,
|
||
|
:columns=>[
|
||
|
{:Name=>"geom", :Type=>"GEOMETRY,0"},
|
||
|
{:Name=>"geom_webmercator", :Type=>"GEOMETRY,0"},
|
||
|
{:Name=>"id", :Type=>"integer"}
|
||
|
]
|
||
|
)
|
||
|
end
|
||
|
|
||
|
it 'should list registered remote table of a federated server and schema' do
|
||
|
@federated_server_name = "fs_011_from_#{@user1.username}_to_remote"
|
||
|
register_remote_table()
|
||
|
pagination = { page: 1, per_page: 10, order: 'remote_table_name', direction: 'asc', offset: 0 }
|
||
|
remote_tables = @service.list_remote_tables(federated_server_name: @federated_server_name, remote_schema_name: @remote_schema_name, **pagination)
|
||
|
expect(remote_tables).to include(
|
||
|
:registered => true,
|
||
|
:qualified_name => "cdb_fs_#{@federated_server_name}.#{@remote_table_name}",
|
||
|
:remote_schema_name => @remote_schema_name,
|
||
|
:remote_table_name => @remote_table_name,
|
||
|
:id_column_name=> "id",
|
||
|
:geom_column_name=>"geom",
|
||
|
:webmercator_column_name=> "geom_webmercator",
|
||
|
:columns => [
|
||
|
{:Name=>"geom", :Type=>"GEOMETRY,0"},
|
||
|
{:Name=>"geom_webmercator", :Type=>"GEOMETRY,0"},
|
||
|
{:Name=>"id", :Type=>"integer"}
|
||
|
]
|
||
|
)
|
||
|
end
|
||
|
|
||
|
it 'should register a remote table of a federated server and schema' do
|
||
|
@federated_server_name = "fs_012_from_#{@user1.username}_to_remote"
|
||
|
remote_table = register_remote_table()
|
||
|
expect(remote_table[:registered]).to eq(true)
|
||
|
expect(remote_table[:qualified_name]).to eq("cdb_fs_#{@federated_server_name}.#{@remote_table_name}")
|
||
|
expect(remote_table[:remote_table_name]).to eq(@remote_table_name)
|
||
|
end
|
||
|
|
||
|
it 'should raise "non integer id_column (.*)" error when registering a remote table' do
|
||
|
@federated_server_name = "fs_0121_from_#{@user1.username}_to_remote"
|
||
|
|
||
|
expect {
|
||
|
remote_table = register_remote_table(id_column_name: 'wadus')
|
||
|
}.to raise_error(Sequel::DatabaseError, /non integer id_column (.*)/)
|
||
|
end
|
||
|
|
||
|
it 'should raise "non geometry column (.*)" error when registering a remote table with wrong "geom_column_name"' do
|
||
|
@federated_server_name = "fs_0121_from_#{@user1.username}_to_remote"
|
||
|
|
||
|
expect {
|
||
|
remote_table = register_remote_table(geom_column_name: 'wadus')
|
||
|
}.to raise_error(Sequel::DatabaseError, /non geometry column (.*)/)
|
||
|
end
|
||
|
|
||
|
it 'should raise "non geometry column (.*)" error when registering a remote table with wrong "webmercator_column_name"' do
|
||
|
@federated_server_name = "fs_0121_from_#{@user1.username}_to_remote"
|
||
|
|
||
|
expect {
|
||
|
remote_table = register_remote_table(webmercator_column_name: 'wadus')
|
||
|
}.to raise_error(Sequel::DatabaseError, /non geometry column (.*)/)
|
||
|
end
|
||
|
|
||
|
it 'should get a remote table of a federated server and schema' do
|
||
|
@federated_server_name = "fs_013_from_#{@user1.username}_to_remote"
|
||
|
register_remote_table()
|
||
|
remote_table = @service.get_remote_table(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
remote_schema_name: @remote_schema_name,
|
||
|
remote_table_name: @remote_table_name
|
||
|
)
|
||
|
expect(remote_table[:registered]).to eq(true)
|
||
|
expect(remote_table[:qualified_name]).to eq("cdb_fs_#{@federated_server_name}.#{@remote_table_name}")
|
||
|
expect(remote_table[:remote_table_name]).to eq(@remote_table_name)
|
||
|
end
|
||
|
|
||
|
it 'should update a remote table of a federated server and schema' do
|
||
|
@federated_server_name = "fs_014_from_#{@user1.username}_to_remote"
|
||
|
new_remote_table_name = 'overwitten_table_name'
|
||
|
register_remote_table()
|
||
|
attributes = get_remote_table_server_payload(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
local_table_name_override: new_remote_table_name
|
||
|
)
|
||
|
remote_table = @service.update_table(attributes)
|
||
|
expect(remote_table[:registered]).to eq(true)
|
||
|
expect(remote_table[:qualified_name]).to eq("cdb_fs_#{@federated_server_name}.#{new_remote_table_name}")
|
||
|
expect(remote_table[:remote_table_name]).to eq(@remote_table_name)
|
||
|
end
|
||
|
|
||
|
it 'should unregister a registered remote table of a federated server' do
|
||
|
@federated_server_name = "fs_015_from_#{@user1.username}_to_remote"
|
||
|
remote_table = register_remote_table()
|
||
|
expect(remote_table[:registered]).to eq(true)
|
||
|
@service.unregister_table(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
remote_schema_name: @remote_schema_name,
|
||
|
remote_table_name: @remote_table_name
|
||
|
)
|
||
|
remote_table = @service.get_remote_table(
|
||
|
federated_server_name: @federated_server_name,
|
||
|
remote_schema_name: @remote_schema_name,
|
||
|
remote_table_name: @remote_table_name
|
||
|
)
|
||
|
expect(remote_table[:registered]).to eq(false)
|
||
|
expect(remote_table[:remote_table_name]).to eq(@remote_table_name)
|
||
|
expect(remote_table[:qualified_name]).to eq(nil)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|