cartodb-4.42/spec/services/carto/federated_tables_service_spec.rb

410 lines
19 KiB
Ruby
Raw Normal View History

2024-04-06 13:25:13 +08:00
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