Skip to content

Commit

Permalink
FI-2330: Request tags (#407)
Browse files Browse the repository at this point in the history
* add migration for tags

* add/update entities and repositories to persist tags

* WIP

* update sql for finding requests via tags

* add basic unit tests for tagged requests

* add spec to verify that only requests from most recent results are returned

* fix sql

* add tags to request dsl methods

* add example tests to demo suite

* update docs

* fix linting errors

* change add_tag params
  • Loading branch information
Jammjammjamm committed Dec 4, 2023
1 parent 3e853a2 commit b2dd4fc
Show file tree
Hide file tree
Showing 15 changed files with 365 additions and 73 deletions.
15 changes: 15 additions & 0 deletions dev_suites/dev_demo_ig_stu1/groups/demo_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -330,5 +330,20 @@ class DemoGroup < Inferno::TestGroup
test 'read from scratch' do
run { assert scratch[:abc] == 'xyz' }
end

test 'tag a request' do
run do
fhir_read :patient, patient_id, client: :this_client_name, tags: ['example_tag_1', 'example_tag_2']
end
end

test 'load a tagged request' do
run do
tagged_requests = load_tagged_requests('example_tag_1', 'example_tag_2')

assert tagged_requests.length == 1, 'Incorrect number of requests loaded'
assert request.id == tagged_requests.first.id, 'Incorrect request loaded'
end
end
end
end
18 changes: 18 additions & 0 deletions lib/inferno/db/migrations/009_add_request_tags.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Sequel.migration do
change do
create_table :tags do
column :id, String, primary_key: true, null: false, size: 36
column :name, String, size: 255, null: false

index :name, unique: true
end

create_table :requests_tags do
foreign_key :tags_id, :tags, index: true, type: String, null: false, size: 36, key: [:id]
foreign_key :requests_id, :requests, index: true, type: Integer, null: false, key: [:index]

index [:tags_id, :requests_id], unique: true
index [:requests_id, :tags_id], unique: true
end
end
end
19 changes: 19 additions & 0 deletions lib/inferno/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
Integer :version, :default=>0, :null=>false
end

create_table(:tags, :ignore_index_errors=>true) do
String :id, :size=>36, :null=>false
String :name, :size=>255, :null=>false

primary_key [:id]

index [:name], :unique=>true
end

create_table(:test_sessions) do
String :id, :size=>36, :null=>false
String :test_suite_id, :size=>255, :null=>false
Expand Down Expand Up @@ -121,5 +130,15 @@
index [:requests_id]
index [:results_id]
end

create_table(:requests_tags, :ignore_index_errors=>true) do
foreign_key :tags_id, :tags, :type=>String, :size=>36, :null=>false, :key=>[:id]
foreign_key :requests_id, :requests, :null=>false, :key=>[:index]

index [:requests_id]
index [:requests_id, :tags_id], :unique=>true
index [:tags_id]
index [:tags_id, :requests_id], :unique=>true
end
end
end
74 changes: 50 additions & 24 deletions lib/inferno/dsl/fhir_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,18 @@ def body_to_path(body)
# other tests
# @param headers [Hash] custom headers for this operation
# @param operation_method [Symbol] indicates which request type to use for the operation
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def fhir_operation(path, body: nil, client: :default, name: nil, headers: {}, operation_method: :post)
store_request_and_refresh_token(fhir_client(client), name) do
def fhir_operation(
path,
body: nil,
client: :default,
name: nil,
headers: {},
operation_method: :post,
tags: []
)
store_request_and_refresh_token(fhir_client(client), name, tags) do
tcp_exception_handler do
operation_headers = fhir_client(client).fhir_headers
operation_headers.merge!('Content-Type' => 'application/fhir+json') if body.present?
Expand All @@ -127,9 +136,10 @@ def fhir_operation(path, body: nil, client: :default, name: nil, headers: {}, op
# @param client [Symbol]
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def fhir_get_capability_statement(client: :default, name: nil)
store_request_and_refresh_token(fhir_client(client), name) do
def fhir_get_capability_statement(client: :default, name: nil, tags: [])
store_request_and_refresh_token(fhir_client(client), name, tags) do
tcp_exception_handler do
fhir_client(client).conformance_statement
fhir_client(client).reply
Expand All @@ -143,9 +153,10 @@ def fhir_get_capability_statement(client: :default, name: nil)
# @param client [Symbol]
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def fhir_create(resource, client: :default, name: nil)
store_request_and_refresh_token(fhir_client(client), name) do
def fhir_create(resource, client: :default, name: nil, tags: [])
store_request_and_refresh_token(fhir_client(client), name, tags) do
tcp_exception_handler do
fhir_client(client).create(resource)
end
Expand All @@ -159,9 +170,10 @@ def fhir_create(resource, client: :default, name: nil)
# @param client [Symbol]
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def fhir_read(resource_type, id, client: :default, name: nil)
store_request_and_refresh_token(fhir_client(client), name) do
def fhir_read(resource_type, id, client: :default, name: nil, tags: [])
store_request_and_refresh_token(fhir_client(client), name, tags) do
tcp_exception_handler do
fhir_client(client).read(fhir_class_from_resource_type(resource_type), id)
end
Expand All @@ -176,9 +188,10 @@ def fhir_read(resource_type, id, client: :default, name: nil)
# @param client [Symbol]
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def fhir_vread(resource_type, id, version_id, client: :default, name: nil)
store_request_and_refresh_token(fhir_client(client), name) do
def fhir_vread(resource_type, id, version_id, client: :default, name: nil, tags: [])
store_request_and_refresh_token(fhir_client(client), name, tags) do
tcp_exception_handler do
fhir_client(client).vread(fhir_class_from_resource_type(resource_type), id, version_id)
end
Expand All @@ -192,9 +205,10 @@ def fhir_vread(resource_type, id, version_id, client: :default, name: nil)
# @param client [Symbol]
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def fhir_update(resource, id, client: :default, name: nil)
store_request_and_refresh_token(fhir_client(client), name) do
def fhir_update(resource, id, client: :default, name: nil, tags: [])
store_request_and_refresh_token(fhir_client(client), name, tags) do
tcp_exception_handler do
fhir_client(client).update(resource, id)
end
Expand All @@ -209,9 +223,10 @@ def fhir_update(resource, id, client: :default, name: nil)
# @param client [Symbol]
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def fhir_patch(resource_type, id, patchset, client: :default, name: nil)
store_request_and_refresh_token(fhir_client(client), name) do
def fhir_patch(resource_type, id, patchset, client: :default, name: nil, tags: [])
store_request_and_refresh_token(fhir_client(client), name, tags) do
tcp_exception_handler do
fhir_client(client).partial_update(fhir_class_from_resource_type(resource_type), id, patchset)
end
Expand All @@ -225,9 +240,10 @@ def fhir_patch(resource_type, id, patchset, client: :default, name: nil)
# @param client [Symbol]
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def fhir_history(resource_type = nil, id = nil, client: :default, name: nil)
store_request_and_refresh_token(fhir_client(client), name) do
def fhir_history(resource_type = nil, id = nil, client: :default, name: nil, tags: [])
store_request_and_refresh_token(fhir_client(client), name, tags) do
tcp_exception_handler do
if id
fhir_client(client).resource_instance_history(fhir_class_from_resource_type(resource_type), id)
Expand All @@ -248,16 +264,24 @@ def fhir_history(resource_type = nil, id = nil, client: :default, name: nil)
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param search_method [Symbol] Use `:post` to search via POST
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def fhir_search(resource_type = nil, client: :default, params: {}, name: nil, search_method: :get)
def fhir_search(
resource_type = nil,
client: :default,
params: {},
name: nil,
search_method: :get,
tags: []
)
search =
if search_method == :post
{ body: params }
else
{ parameters: params }
end

store_request_and_refresh_token(fhir_client(client), name) do
store_request_and_refresh_token(fhir_client(client), name, tags) do
tcp_exception_handler do
if resource_type
fhir_client(client)
Expand All @@ -276,9 +300,10 @@ def fhir_search(resource_type = nil, client: :default, params: {}, name: nil, se
# @param client [Symbol]
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def fhir_delete(resource_type, id, client: :default, name: nil)
store_request('outgoing', name) do
def fhir_delete(resource_type, id, client: :default, name: nil, tags: [])
store_request('outgoing', name, tags) do
tcp_exception_handler do
fhir_client(client).destroy(fhir_class_from_resource_type(resource_type), id)
end
Expand All @@ -291,9 +316,10 @@ def fhir_delete(resource_type, id, client: :default, name: nil)
# @param client [Symbol]
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def fhir_transaction(bundle = nil, client: :default, name: nil)
store_request('outgoing', name) do
def fhir_transaction(bundle = nil, client: :default, name: nil, tags: [])
store_request('outgoing', name, tags) do
tcp_exception_handler do
fhir_client(client).transaction_bundle = bundle if bundle.present?
fhir_client(client).end_transaction
Expand All @@ -312,8 +338,8 @@ def fhir_class_from_resource_type(resource_type)
# expired. It's combined with `store_request` so that all of the fhir
# request methods don't have to be wrapped twice.
# @private
def store_request_and_refresh_token(client, name, &block)
store_request('outgoing', name) do
def store_request_and_refresh_token(client, name, tags, &block)
store_request('outgoing', name, tags) do
perform_refresh(client) if client.need_to_refresh? && client.able_to_refresh?
block.call
end
Expand Down
20 changes: 12 additions & 8 deletions lib/inferno/dsl/http_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ def http_clients
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param headers [Hash] Input headers here
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def get(url = '', client: :default, name: nil, headers: nil)
store_request('outgoing', name) do
def get(url = '', client: :default, name: nil, headers: nil, tags: [])
store_request('outgoing', name, tags) do
tcp_exception_handler do
client = http_client(client)

Expand Down Expand Up @@ -103,9 +104,10 @@ def connection
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param headers [Hash] Input headers here
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def post(url = '', body: nil, client: :default, name: nil, headers: nil)
store_request('outgoing', name) do
def post(url = '', body: nil, client: :default, name: nil, headers: nil, tags: [])
store_request('outgoing', name, tags) do
tcp_exception_handler do
client = http_client(client)

Expand All @@ -129,9 +131,10 @@ def post(url = '', body: nil, client: :default, name: nil, headers: nil)
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param headers [Hash] Input headers here
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def delete(url = '', client: :default, name: :nil, headers: nil)
store_request('outgoing', name) do
def delete(url = '', client: :default, name: :nil, headers: nil, tags: [])
store_request('outgoing', name, tags) do
tcp_exception_handler do
client = http_client(client)

Expand Down Expand Up @@ -159,8 +162,9 @@ def delete(url = '', client: :default, name: :nil, headers: nil)
# @param name [Symbol] Name for this request to allow it to be used by
# other tests
# @param headers [Hash] Input headers here
# @param tags [Array<String>] a list of tags to assign to the request
# @return [Inferno::Entities::Request]
def stream(block, url = '', limit = 100, client: :default, name: nil, headers: nil)
def stream(block, url = '', limit = 100, client: :default, name: nil, headers: nil, tags: [])
streamed = []

collector = proc do |chunk, bytes|
Expand All @@ -169,7 +173,7 @@ def stream(block, url = '', limit = 100, client: :default, name: nil, headers: n
block.call(chunk, bytes)
end

store_request('outgoing', name) do
store_request('outgoing', name, tags) do
tcp_exception_handler do
client = http_client(client)

Expand Down
18 changes: 15 additions & 3 deletions lib/inferno/dsl/request_storage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,36 @@ def resource
request&.resource
end

# Returns requests which match all of the given tags
#
# @param tags [String]
# @return [Inferno::Entities::Request]
def load_tagged_requests(*tags)
return [] if tags.blank?

Repositories::Requests.new.tagged_requests(test_session_id, tags).tap do |tagged_requests|
requests.concat(tagged_requests)
end
end

# @private
def named_request(name)
requests.find { |request| request.name == self.class.config.request_name(name.to_sym) }
end

# @private
def store_request(direction, name = nil, &block)
def store_request(direction, name, tags, &block)
response = block.call

name = self.class.config.request_name(name)
request =
if response.is_a? FHIR::ClientReply
Entities::Request.from_fhir_client_reply(
response, direction:, name:, test_session_id:
response, direction:, name:, test_session_id:, tags:
)
else
Entities::Request.from_http_response(
response, direction:, name:, test_session_id:
response, direction:, name:, test_session_id:, tags:
)
end

Expand Down
8 changes: 7 additions & 1 deletion lib/inferno/dsl/resume_test_route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ def test_run_identifier_block
self.class.singleton_class.instance_variable_get(:@test_run_identifier_block)
end

# @private
def tags
self.class.singleton_class.instance_variable_get(:@tags)
end

# @private
def find_test_run(test_run_identifier)
test_runs_repo.find_latest_waiting_by_identifier(test_run_identifier)
Expand All @@ -44,7 +49,8 @@ def persist_request(request, test_run, waiting_result, test)
request.to_hash.merge(
test_session_id: test_run.test_session_id,
result_id: waiting_result.id,
name: test.config.request_name(test.incoming_request_name)
name: test.config.request_name(test.incoming_request_name),
tags:
)
)
end
Expand Down
6 changes: 4 additions & 2 deletions lib/inferno/dsl/runnable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ def suite
#
# @see Inferno::DSL::Results#wait
# @example
# resume_test_route :get, '/launch' do
# resume_test_route :get, '/launch', tags: ['launch'] do
# request.query_parameters['iss']
# end
#
Expand All @@ -341,15 +341,17 @@ def suite
# [Any of the path options available in Hanami
# Router](https://github.com/hanami/router/tree/f41001d4c3ee9e2d2c7bb142f74b43f8e1d3a265#a-beautiful-dsl)
# can be used here.
# @param tags [Array<String>] a list of tags to assign to the request
# @yield This method takes a block which must return the identifier
# defined when a test was set to wait for the test run that hit this
# route. The block has access to the `request` method which returns a
# {Inferno::Entities::Request} object with the information for the
# incoming request.
# @return [void]
def resume_test_route(method, path, &block)
def resume_test_route(method, path, tags: [], &block)
route_class = Class.new(ResumeTestRoute) do |klass|
klass.singleton_class.instance_variable_set(:@test_run_identifier_block, block)
klass.singleton_class.instance_variable_set(:@tags, tags)
end

route(method, path, route_class)
Expand Down
Loading

0 comments on commit b2dd4fc

Please sign in to comment.