Skip to content
Open
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 README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# FHIR Client [![Build Status](https://travis-ci.org/fhir-crucible/fhir_client.svg?branch=master)](https://travis-ci.org/fhir-crucible/fhir_client)
# FHIR Client

Ruby FHIR client.

Expand Down
4 changes: 2 additions & 2 deletions fhir_client.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ Gem::Specification.new do |spec|
spec.add_dependency 'fhir_stu3_models', '>= 3.1.1'
spec.add_dependency 'fhir_dstu2_models', '>= 1.1.1'
spec.add_dependency 'nokogiri', '>= 1.10.4'
spec.add_dependency 'oauth2', '~> 1.1'
spec.add_dependency 'oauth2', '>= 1.1'
spec.add_dependency 'rack', '>= 1.5'
spec.add_dependency 'rest-client', '~> 2.0'
spec.add_dependency 'http', '~> 5.1'
spec.add_dependency 'tilt', '>= 1.1'

spec.add_development_dependency 'bundler', '~> 2.0'
Expand Down
212 changes: 134 additions & 78 deletions lib/fhir_client/client.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
require 'rest_client'
require 'nokogiri'
require 'http'
require 'addressable/uri'
require 'oauth2'

# Need this for request.args
module HTTP
class Request
def args
{
method: @verb,
url: @[email protected],
path: @uri.path,
headers: @headers,
payload: @body&.source
}
end
end
end


module FHIR
class Client
include FHIR::Sections::History
Expand Down Expand Up @@ -39,6 +56,7 @@ class Client
# @return
#
def initialize(base_service_url, default_format: FHIR::Formats::ResourceFormat::RESOURCE_JSON, proxy: nil)
base_service_url = "http://#{base_service_url}" unless base_service_url.start_with?("https://", "http://")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of dealing with these URLS as strings to be manipulated and mutated, what do you think about using the ruby URI object?

uri = URI.parse(base_service_url)

# Check if the scheme is missing
if uri.scheme.nil?
  # Construct a new URI with the "http" scheme
  uri = URI::HTTP.build(host: uri.host, path: uri.path)
end
@base_service_url = uri

@base_service_url = base_service_url
FHIR.logger.info "Initializing client with #{@base_service_url}"
@use_format_param = false
Expand Down Expand Up @@ -114,9 +132,7 @@ def set_no_auth
@use_oauth2_auth = false
@use_basic_auth = false
@security_headers = {}
@client = RestClient
@client.proxy = proxy unless proxy.nil?
@client
@client = @proxy ? HTTP.via(@proxy) : HTTP
end

# Set the client to use HTTP Basic Authentication
Expand All @@ -127,9 +143,7 @@ def set_basic_auth(client, secret)
@security_headers = { 'Authorization' => value }
@use_oauth2_auth = false
@use_basic_auth = true
@client = RestClient
@client.proxy = proxy unless proxy.nil?
@client
@client = @proxy ? HTTP.via(@proxy) : HTTP
end

# Set the client to use Bearer Token Authentication
Expand All @@ -139,9 +153,7 @@ def set_bearer_token(token)
@security_headers = { 'Authorization' => value }
@use_oauth2_auth = false
@use_basic_auth = true
@client = RestClient
@client.proxy = proxy unless proxy.nil?
@client
@client = @proxy ? HTTP.via(@proxy) : HTTP
end

# Set the client to use OpenID Connect OAuth2 Authentication
Expand Down Expand Up @@ -446,7 +458,7 @@ def get(path, headers = {})
if @use_oauth2_auth
# @client.refresh!
begin
response = @client.get(url, headers: headers)
response = @client.headers(headers).get(url)
rescue => e
if !e.respond_to?(:response) || e.response.nil?
# Re-raise the client error if there's no response. Otherwise, logging
Expand All @@ -471,14 +483,14 @@ def get(path, headers = {})
if url.end_with?('/metadata')
FHIR.logger.debug "GET - Request: #{req}, Response: [too large]"
else
FHIR.logger.debug "GET - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
FHIR.logger.debug "GET - Request: #{req}, Response: #{response.body.to_s.force_encoding('UTF-8')}"
end
@reply = FHIR::ClientReply.new(req, res, self)
else
headers.merge!(@security_headers) if @use_basic_auth
begin
response = @client.get(url, headers)
rescue RestClient::SSLCertificateNotVerified => sslerr
response = @client.headers(headers).get(url)
rescue OpenSSL::SSL::SSLError => sslerr
FHIR.logger.error "SSL Error: #{url}"
req = {
method: :get,
Expand All @@ -493,7 +505,6 @@ def get(path, headers = {})
body: sslerr.message
}
@reply = FHIR::ClientReply.new(req, res, self)
return @reply
rescue => e
if !e.respond_to?(:response) || e.response.nil?
# Re-raise the client error if there's no response. Otherwise, logging
Expand All @@ -506,17 +517,22 @@ def get(path, headers = {})
if url.end_with?('/metadata')
FHIR.logger.debug "GET - Request: #{response.request.to_json}, Response: [too large]"
else
FHIR.logger.debug "GET - Request: #{response.request.to_json}, Response: #{response.body.force_encoding('UTF-8')}"
FHIR.logger.debug "GET - Request: #{response.request.to_json}, Response: #{response.body.to_s.force_encoding('UTF-8')}"
end
response.request.args[:path] = response.request.args[:url].gsub(@base_service_url, '')
headers = response.headers.each_with_object({}) { |(k, v), h| h[k.to_s.tr('_', '-')] = v.to_s; h }

req = {
method: :get,
url: url,
path: url.gsub(@base_service_url, ''),
headers: headers,
payload: nil
}
res = {
code: response.code,
headers: scrubbed_response_headers(headers),
headers: scrubbed_response_headers(response.headers.to_hash),
body: response.body
}

@reply = FHIR::ClientReply.new(response.request.args, res, self)
@reply = FHIR::ClientReply.new(req, res, self)
end
end

Expand All @@ -528,12 +544,12 @@ def post(path, resource, headers)
if @use_oauth2_auth
# @client.refresh!
begin
response = @client.post(url, headers: headers, body: payload)
response = @client.headers(headers).post(url, json: payload)
rescue => e
if !e.respond_to?(:response) || e.response.nil?
# Re-raise the client error if there's no response. Otherwise, logging
# and other things break below!
FHIR.logger.error "POST - Request: #{url} failed! No response from server: #{e}"
FHIR.logger.debug "POST - Request: #{url} failed! No response from server: #{e}"
raise # Re-raise the same error we caught.
end
response = e.response if e.response
Expand All @@ -550,20 +566,29 @@ def post(path, resource, headers)
headers: response.headers,
body: response.body
}
FHIR.logger.debug "POST - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
FHIR.logger.debug "POST - Request: #{req}, Response: #{response.body.to_s.force_encoding('UTF-8')}"
@reply = FHIR::ClientReply.new(req, res, self)
@reply
else
headers.merge!(@security_headers) if @use_basic_auth
@client.post(url, payload, headers) do |resp, request, result|
FHIR.logger.debug "POST - Request: #{request.to_json}\nResponse:\nResponse Headers: #{scrubbed_response_headers(result.each_key {})} \nResponse Body: #{resp.force_encoding('UTF-8')}"
request.args[:path] = url.gsub(@base_service_url, '')
res = {
code: result.code,
headers: scrubbed_response_headers(result.each_key {}),
body: resp
}
@reply = FHIR::ClientReply.new(request.args, res, self)
end

response = @client.headers(headers).post(url, json: payload)

FHIR.logger.debug "POST - Request: #{response.request.to_json}\nResponse:\nResponse Headers: #{scrubbed_response_headers(headers)} \nResponse Body: #{response.body.to_s.force_encoding('UTF-8')}"

req = {
method: :post,
url: response.headers.to_hash["Location"],
path: url.gsub(@base_service_url, ''),
headers: headers,
payload: payload
}
res = {
code: response.code,
headers: scrubbed_response_headers(response.headers.to_hash),
body: response.body
}
@reply = FHIR::ClientReply.new(req, res, self)
end
end

Expand All @@ -575,7 +600,7 @@ def put(path, resource, headers)
if @use_oauth2_auth
# @client.refresh!
begin
response = @client.put(url, headers: headers, body: payload)
response = @client.headers(headers).put(url, body: payload)
rescue => e
if !e.respond_to?(:response) || e.response.nil?
# Re-raise the client error if there's no response. Otherwise, logging
Expand All @@ -597,20 +622,27 @@ def put(path, resource, headers)
headers: response.headers,
body: response.body
}
FHIR.logger.debug "PUT - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
FHIR.logger.debug "PUT - Request: #{req}, Response: #{response.body.to_s.force_encoding('UTF-8')}"
@reply = FHIR::ClientReply.new(req, res, self)
else
headers.merge!(@security_headers) if @use_basic_auth
@client.put(url, payload, headers) do |resp, request, result|
FHIR.logger.debug "PUT - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
request.args[:path] = url.gsub(@base_service_url, '')
res = {
code: result.code,
headers: scrubbed_response_headers(result.each_key {}),
body: resp
}
@reply = FHIR::ClientReply.new(request.args, res, self)
end

response = @client.headers(headers).put(url, json: payload)

FHIR.logger.debug "PUT - Request: #{response.request.to_json}, Response: #{response.body.to_s.force_encoding('UTF-8')}"
req = {
method: :put,
url: response.headers.to_hash["Location"],
path: url.gsub(@base_service_url, ''),
headers: headers,
payload: payload
}
res = {
code: response.code,
headers: scrubbed_response_headers(response.headers.to_hash),
body: response.body
}
@reply = FHIR::ClientReply.new(req, res, self)
end
end

Expand All @@ -622,7 +654,7 @@ def patch(path, patchset, headers)
if @use_oauth2_auth
# @client.refresh!
begin
response = @client.patch(url, headers: headers, body: payload)
response = @client.headers(headers).patch(url, json: payload)
rescue => e
if !e.respond_to?(:response) || e.response.nil?
# Re-raise the client error if there's no response. Otherwise, logging
Expand All @@ -649,16 +681,22 @@ def patch(path, patchset, headers)
else
headers.merge!(@security_headers) if @use_basic_auth
begin
@client.patch(url, payload, headers) do |resp, request, result|
FHIR.logger.debug "PATCH - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
request.args[:path] = url.gsub(@base_service_url, '')
res = {
code: result.code,
headers: scrubbed_response_headers(result.each_key {}),
body: resp
}
@reply = FHIR::ClientReply.new(request.args, res, self)
end
response = @client.headers(headers).patch(url, json: payload)

FHIR.logger.debug "PATCH - Request: #{response.request.to_json}, Response: #{response.body.to_s.force_encoding('UTF-8')}"
req = {
method: :patch,
url: response.headers.to_hash["Location"],
path: url.gsub(@base_service_url, ''),
headers: headers,
payload: payload
}
res = {
code: response.code,
headers: scrubbed_response_headers(response.headers.to_hash),
body: response.body.to_s
}
@reply = FHIR::ClientReply.new(req, res, self)
rescue => e
if !e.respond_to?(:response) || e.response.nil?
# Re-raise the client error if there's no response. Otherwise, logging
Expand Down Expand Up @@ -690,7 +728,7 @@ def delete(path, headers)
if @use_oauth2_auth
# @client.refresh!
begin
response = @client.delete(url, headers: headers)
response = @client.headers(headers).delete(url)
rescue => e
if !e.respond_to?(:response) || e.response.nil?
# Re-raise the client error if there's no response. Otherwise, logging
Expand All @@ -716,33 +754,51 @@ def delete(path, headers)
@reply = FHIR::ClientReply.new(req, res, self)
else
headers.merge!(@security_headers) if @use_basic_auth
@client.delete(url, headers) do |resp, request, result|
FHIR.logger.debug "DELETE - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
request.args[:path] = url.gsub(@base_service_url, '')
res = {
code: result.code,
headers: scrubbed_response_headers(result.each_key {}),
body: resp
}
@reply = FHIR::ClientReply.new(request.args, res, self)
end

response = @client.headers(headers).delete(url)
response_headers = response.headers.each_with_object({}) { |(k, v), h| h[k.to_s.tr('_', '-')] = v.to_s; h }

req = {
method: :delete,
url: response.headers.to_hash["Location"],
path: url.gsub(@base_service_url, ''),
headers: headers,
payload: nil
}
res = {
code: result.code,
headers: scrubbed_response_headers(response.headers.to_hash),
body: resp
}

@reply = FHIR::ClientReply.new(req, res, self)
end
end

def head(path, headers)
headers.merge!(@security_headers) unless @security_headers.blank?
url = URI(build_url(path)).to_s
FHIR.logger.info "HEADING: #{url}"
RestClient.head(url, headers) do |response, request, result|
FHIR.logger.debug "HEAD - Request: #{request.to_json}, Response: #{response.force_encoding('UTF-8')}"
request.args[:path] = url.gsub(@base_service_url, '')
res = {
code: result.code,
headers: scrubbed_response_headers(result.each_key {}),
body: response
}
@reply = FHIR::ClientReply.new(request.args, res, self)
end

response = @client.headers(headers).head(url)
response_headers = response.headers.each_with_object({}) { |(k, v), h| h[k.to_s.tr('_', '-')] = v.to_s; h }

req = {
method: :head,
url: url,
path: url.gsub(@base_service_url, ''),
headers: headers,
payload: nil
}
res = {
code: result.code,
headers: scrubbed_response_headers(headers),
body: resp
}

FHIR.logger.debug "HEAD - Request: #{request.to_json}, Response: #{response.force_encoding('UTF-8')}"

@reply = FHIR::ClientReply.new(req, res, self)
end

def build_url(path)
Expand Down
2 changes: 1 addition & 1 deletion lib/fhir_client/model/client_reply.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def version
end

def self_link
(@response[:headers]['content-location'] || @response[:headers]['location']) unless @response.nil? || @response[:headers].nil?
(@response[:headers]['content-location'] || @response[:headers]['Location']) unless @response.nil? || @response[:headers].nil?
end

def body
Expand Down
2 changes: 1 addition & 1 deletion lib/fhir_client/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module FHIR
class Client
VERSION = '5.0.3'.freeze
VERSION = '6.0.0'.freeze
end
end
Loading