Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ Layout/LineLength:
- 'packages/forest_admin_rpc_agent/lib/forest_admin_rpc_agent/routes/sse.rb'

RSpec/MultipleMemoizedHelpers:
Max: 10
Max: 15

Security/Eval:
Exclude:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ def initialize(options, introspection)
@charts = introspection[:charts]
@rpc_relations = introspection[:rpc_relations]

native_query_connections = introspection[:nativeQueryConnections] || []
@live_query_connections = native_query_connections.to_h { |conn| [conn[:name], conn[:name]] }

@schema = { charts: @charts }
end

Expand All @@ -33,5 +36,17 @@ def render_chart(caller, name)

client.call_rpc(url, method: :post, payload: { chart: name, caller: caller.to_h })
end

def execute_native_query(connection_name, query, binds)
client = RpcClient.new(@options[:uri], ForestAdminRpcAgent::Facades::Container.cache(:auth_secret))
url = 'forest/rpc/native-query'

ForestAdminRpcAgent::Facades::Container.logger.log(
'Debug',
"Forwarding native query for connection '#{connection_name}' to the Rpc agent on #{url}."
)

client.call_rpc(url, method: :post, payload: { connection_name: connection_name, query: query, binds: binds })
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ module ForestAdminDatasourceRpc
it 'add charts' do
expect(datasource.schema[:charts]).to include('appointments')
end

it 'stores native query connections' do
datasource_with_connections = described_class.new(
{ uri: 'http://localhost' },
introspection.merge(nativeQueryConnections: [{ name: 'primary' }, { name: 'secondary' }])
)
expect(datasource_with_connections.live_query_connections).to eq({ 'primary' => 'primary', 'secondary' => 'secondary' })
end

it 'handles missing nativeQueryConnections gracefully' do
expect(datasource.live_query_connections).to eq({})
end
end

context 'when call render_chart' do
Expand All @@ -40,5 +52,20 @@ module ForestAdminDatasourceRpc
end
end
end

context 'when call execute_native_query' do
it 'forward the call to the server with all parameters' do
query = 'SELECT * FROM users WHERE id = ?'
binds = [1]

datasource.execute_native_query('primary', query, binds)

expect(rpc_client).to have_received(:call_rpc) do |url, options|
expect(url).to eq('forest/rpc/native-query')
expect(options[:method]).to eq(:post)
expect(options[:payload]).to eq({ connection_name: 'primary', query: query, binds: binds })
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require 'jsonapi-serializers'

module ForestAdminRpcAgent
module Routes
class NativeQuery < BaseRoute
def initialize
super('rpc/native-query', 'post', 'rpc_native_query')
end

def handle_request(args)
return '{}' unless args[:params]['connection_name'] && args[:params]['query']

connection_name = args[:params]['connection_name']
query = args[:params]['query']
binds = args[:params]['binds'] || []
datasource = ForestAdminRpcAgent::Facades::Container.datasource

datasource.execute_native_query(connection_name, query, binds).to_json
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@ def initialize
def handle_request(_params)
agent = ForestAdminRpcAgent::Agent.instance
schema = agent.customizer.schema
schema[:collections] = agent.customizer.datasource(ForestAdminRpcAgent::Facades::Container.logger)
.collections
.map { |_name, collection| collection.schema.merge({ name: collection.name }) }
.sort_by { |collection| collection[:name] }
datasource = agent.customizer.datasource(ForestAdminRpcAgent::Facades::Container.logger)

schema[:collections] = datasource.collections
.map { |_name, collection| collection.schema.merge({ name: collection.name }) }
.sort_by { |collection| collection[:name] }

connections = []
agent.customizer.datasources.each do |root_datasource|
connections = connections.union(
root_datasource.live_query_connections.keys.map { |connection_name| { name: connection_name } }
)
end
schema[:nativeQueryConnections] = connections

schema.to_json
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ module Routes

# rubocop:disable Style/OpenStructUse
# rubocop:disable RSpec/VerifiedDoubles
# rubocop:disable RSpec/MultipleMemoizedHelpers
describe '#encode_file_element' do
context 'when element is of type "File"' do
let(:file_element) { OpenStruct.new(type: 'File', value: double('File')) }
Expand Down Expand Up @@ -165,7 +164,6 @@ module Routes
end
# rubocop:enable Style/OpenStructUse
# rubocop:enable RSpec/VerifiedDoubles
# rubocop:enable RSpec/MultipleMemoizedHelpers
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
require 'spec_helper'
require 'faraday'

module ForestAdminRpcAgent
module Routes
include ForestAdminDatasourceRpc

describe NativeQuery do
let(:route) { described_class.new }
let(:connection_name) { 'primary' }
let(:query) { 'SELECT * FROM users WHERE id = ?' }
let(:binds) { [1] }
let(:params) do
{
'connection_name' => connection_name,
'query' => query,
'binds' => binds
}
end

let(:query_result) { [{ 'id' => 1, 'name' => 'John Doe' }] }
let(:datasource) { instance_double(ForestAdminDatasourceToolkit::Datasource) }

before do
allow(ForestAdminRpcAgent::Facades::Container).to receive(:datasource).and_return(datasource)
allow(datasource).to receive(:execute_native_query).and_return(query_result)
end

describe '#handle_request' do
context 'when connection_name and query are provided' do
it 'executes the native query and returns the result' do
result = route.handle_request(params: params)
expect(result).to eq(query_result.to_json)
expect(datasource).to have_received(:execute_native_query).with(connection_name, query, binds)
end
end

context 'when binds is not provided' do
it 'defaults to empty array' do
params_without_binds = params.except('binds')
route.handle_request(params: params_without_binds)
expect(datasource).to have_received(:execute_native_query).with(connection_name, query, [])
end
end

context 'when connection_name is missing' do
it 'returns an empty JSON object' do
result = route.handle_request(params: { 'query' => query })
expect(result).to eq('{}')
expect(datasource).not_to have_received(:execute_native_query)
end
end

context 'when query is missing' do
it 'returns an empty JSON object' do
result = route.handle_request(params: { 'connection_name' => connection_name })
expect(result).to eq('{}')
expect(datasource).not_to have_received(:execute_native_query)
end
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ module Routes
let(:collection_order) { instance_double(Collection, name: 'orders', schema: { fields: ['id', 'total'] }) }
let(:collections) { { 'users' => collection_user, 'orders' => collection_order } }
let(:schema) { { some_key: 'some_value' } }
let(:datasource_with_connections) do
instance_double(ForestAdminDatasourceToolkit::Datasource, live_query_connections: { 'primary' => 'primary' })
end
let(:expected_schema) do
{
some_key: 'some_value',
collections: [
{ fields: ['id', 'total'], name: 'orders' },
{ fields: ['id', 'email'], name: 'users' }
],
nativeQueryConnections: [
{ name: 'primary' }
]
}.to_json
end
Expand All @@ -28,7 +34,11 @@ module Routes
allow(ForestAdminRpcAgent::Agent).to receive(:instance).and_return(agent)
allow(agent).to receive(:customizer).and_return(customizer)
allow(ForestAdminRpcAgent::Facades::Container).to receive(:logger).and_return(logger)
allow(customizer).to receive_messages(schema: schema, datasource: datasource)
allow(customizer).to receive_messages(
schema: schema,
datasource: datasource,
datasources: [datasource_with_connections]
)
allow(datasource).to receive(:collections).and_return(collections)
end

Expand Down