Eiffel.api_client.mustache Maven / Gradle / Ivy
{{>noteinfo}}
class
API_CLIENT
create
make
feature {NONE} -- Initialization
make
do
create default_header_map.make (0)
set_base_path (default_base_path)
--set default user_agent
set_user_agent("{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{artifactVersion}}}/Eiffel{{/httpUserAgent}}");
create authentications.make (3) {{#authMethods}}{{#isBasic}}
authentications.force (create {HTTP_BASIC_AUTH}, "{{name}}")
is_basic_auth_configured := True {{/isBasic}}{{#isApiKey}}
authentications.force (create {API_KEY_AUTH}.make ({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"), "{{name}}")
is_api_key_configured := True {{/isApiKey}}{{#isOAuth}}
authentications.force (create {OAUTH},"{{name}}")
is_oauth_configured := True {{/isOAuth}}{{/authMethods}}
end
feature -- Access
default_base_path: STRING = "{{{basePath}}}"
-- default base path.
base_path: STRING
-- base path.
authentications: STRING_TABLE [AUTHENTICATION]
-- autentication table.
feature -- Status Report
is_basic_auth_configured: BOOLEAN
-- is basic auth configured.
is_api_key_configured: BOOLEAN
-- is api key auth configured.
is_oauth_configured: BOOLEAN
-- is oauth configured.
feature -- Access: Authentication
authentication_by_name (a_name: STRING): detachable AUTHENTICATION
-- Return the Authentication for the given name `a_name', Void if not found.
do
Result := authentications.at (a_name)
end
feature -- Helper: Basic Authentication
set_password (a_password: STRING_32)
-- Set basic auth `password' with `a_password'.
require
is_basic_auth_configured: is_basic_auth_configured
do
across authentications as ic loop
if attached {HTTP_BASIC_AUTH} ic.item as l_basic_auth then
l_basic_auth.set_password (a_password)
end
end
end
set_username (a_username: STRING_32)
-- Set basic auth `username' with `a_username'.
require
is_basic_auth_configured: is_basic_auth_configured
do
across authentications as ic loop
if attached {HTTP_BASIC_AUTH} ic.item as l_basic_auth then
l_basic_auth.set_user_name (a_username)
end
end
end
feature -- Helper: Api Key Authentication
set_api_key (a_api_key: STRING_32)
-- Set `api_key' authentication key with `a_api_key'.
require
is_api_key_configured: is_api_key_configured
do
across authentications as ic loop
if attached {API_KEY_AUTH} ic.item as l_api_key then
l_api_key.set_api_key (a_api_key)
end
end
end
set_api_prefix (a_api_prefix: STRING_32)
-- Set `api_key_prefix' authentication with `a_api_prefix'.
require
is_api_key_configured: is_api_key_configured
do
across authentications as ic loop
if attached {API_KEY_AUTH} ic.item as l_api_key then
l_api_key.set_api_key_prefix (a_api_prefix)
end
end
end
feature -- Helper: OAuth Authentication
set_access_token (a_token: STRING_32)
-- Set OAuth access token with `a_token'.
require
is_oauth_configured: is_oauth_configured
do
across authentications as ic loop
if attached {OAUTH} ic.item as l_oauth then
l_oauth.set_access_token (a_token)
end
end
end
feature -- Query Parameter Helpers
parameter_to_tuple (a_collection_format, a_name: STRING; a_value: detachable ANY): LIST [TUPLE [name: STRING; value: STRING]]
-- A list of tuples with name and valule.
-- collectionFormat collection format (e.g. csv, tsv)
-- name Name
-- value Value
require
valid_name: not a_name.is_empty
local
l_format: STRING
l_delimiter: STRING
l_value: STRING
do
if attached {LIST [ANY]} a_value as a_list then
-- Collection
if a_list.is_empty then
-- Return an empty list
create {ARRAYED_LIST [TUPLE [name: STRING; value: STRING]]} Result.make (0)
else
-- collection format: multi, csv, ssv, tsv, pipes.
create {ARRAYED_LIST [TUPLE [name: STRING; value: STRING]]} Result.make (a_list.count)
if a_collection_format.is_empty then
l_format := "csv" -- default: csv
else
l_format := a_collection_format
end
if l_format.is_case_insensitive_equal ("multi") then
across a_list as ic loop
Result.force ([a_name, parameter_to_string (ic.item)])
end
else
if l_format.is_case_insensitive_equal ("csv") then
l_delimiter := ","
elseif l_format.is_case_insensitive_equal ("ssv") then
l_delimiter := " "
elseif l_format.is_case_insensitive_equal ("tsv") then
l_delimiter := "\t"
elseif l_format.is_case_insensitive_equal ("pipes") then
l_delimiter := "|"
else
l_delimiter := ","
end
across a_list as ic from create l_value.make_empty
loop
l_value.append (parameter_to_string (ic.item))
l_value.append (l_delimiter)
end
l_value.remove_tail (1)
Result.force ([a_name,l_value])
end
end
else
create {ARRAYED_LIST [TUPLE [name: STRING; value: STRING]]} Result.make (1)
if attached a_value then
Result.force ([a_name,a_value.out])
else
Result.force ([a_name,""])
end
end
end
parameter_to_string (a_param: detachable ANY): STRING
-- return the string representation of the givien object `a_param'.
do
if a_param = Void then
Result := ""
else
if attached {BOOLEAN} a_param as bool then
Result := bool.out
elseif attached {NUMERIC} a_param as num then
if attached {INTEGER_64} num as i64 then
Result := i64.out
elseif attached {INTEGER_32} num as i32 then
Result := i32.out
elseif attached {INTEGER_16} num as i16 then
Result := i16.out
elseif attached {INTEGER_8} num as i8 then
Result := i8.out
elseif attached {NATURAL_64} num as n64 then
Result := n64.out
elseif attached {NATURAL_32} num as n32 then
Result := n32.out
elseif attached {NATURAL_16} num as n16 then
Result := n16.out
elseif attached {NATURAL_8} num as n8 then
Result := n8.out
elseif attached {REAL_64} num as r64 then
Result := r64.out
elseif attached {REAL_32} num as r32 then
Result := r32.out
else
check is_basic_numeric_type: False end
end
Result := num.out
elseif attached {CHARACTER_8} a_param as ch8 then
Result := ch8.out
elseif attached {CHARACTER_32} a_param as ch32 then
Result := ch32.out
elseif attached {POINTER} a_param as ptr then
Result := ptr.to_integer_32.out
elseif attached {DATE} a_param as date then
--TODO improve to support
-- date string As defined by full-date - RFC3339
Result := date.debug_output
elseif attached {DATE_TIME} a_param as date_time then
-- TODO improve to support
-- dateTime string date-time As defined by date-time - RFC3339
Result := date_time.date.debug_output
elseif attached {STRING_32} a_param as str_32 then
Result := str_32
elseif attached {STRING_8} a_param as str_8 then
Result := str_8
else
-- Unsupported Object type.
Result := ""
end
end
end
feature -- Status Report
is_valid_uri (a_uri: STRING): BOOLEAN
-- Is `a_uri' a valid uri?
local
l_uri: URI
do
create l_uri.make_from_string (a_uri)
Result := l_uri.is_valid
end
feature --Helper: Http Client
select_header_accept (a_accept: ARRAY [STRING]): detachable STRING
-- Select the Accept header's value from the given accepts array.
do
a_accept.compare_objects
if a_accept.has ("application/json") then
Result := "application/json"
end
end
select_header_content_type (a_content_types: ARRAY [STRING]): STRING
-- Select the Content-Type header's value from the given array.
-- at the moment `application/json'
do
a_content_types.compare_objects
if a_content_types.is_empty then
Result := "application/json"
elseif a_content_types.has ("application/json") then
Result := "application/json"
else
Result := a_content_types.at (1)
end
end
update_params_for_auth (a_auth_names: ARRAY [STRING]; a_query_params: LIST [TUPLE [name: STRING; value: STRING]]; a_header_params: STRING_TABLE [STRING] )
-- Update query and header parameters based on authentication settings.
-- a_auth_names:The authentications to apply.
-- a_queryParams List of query parameters.
-- a_headerParams Map of header parameters
do
across a_auth_names as ic loop
if attached authentications.at (ic.item) as l_auth then
l_auth.apply_to_params (a_query_params, a_header_params)
end
end
end
accepts_request_body (a_method: STRING): BOOLEAN
-- Does the method `a_method' accepts a request body?
do
if
a_method.is_case_insensitive_equal_general ("POST") or else
a_method.is_case_insensitive_equal_general ("PUT") or else
a_method.is_case_insensitive_equal_general ("PATCH") or else
a_method.is_case_insensitive_equal_general ("DELETE")
then
Result := True
end
end
url_encode (a_val: STRING): STRING
-- Url encode `a_val'
do
Result := (create {URL_ENCODER}).encoded_string (a_val)
end
feature -- HTTP client: call api
call_api (a_path: STRING_8; a_method: STRING_8; a_request: API_CLIENT_REQUEST; a_serializer: detachable FUNCTION [TUPLE [STRING, ANY], STRING]; a_deserializer: detachable FUNCTION [TUPLE [STRING, STRING, TYPE [detachable ANY]], detachable ANY] ): API_CLIENT_RESPONSE
-- Execute an HTTP request with the given options.
-- Relative path `a_path'
-- Method `a_method' "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE"
-- A request `a_request' wth
-- The query parameters: `query_params'.
-- The Header parameters: `header_params'.
-- The Request Body: `body' could be Void, object to be serialized using the serializer function `a_serializer' with a given content_type.
-- this function will need to be added by the user with the supported content types in the target API class.
-- The authentications to apply `auth_names'.
-- `a_deserializer': is a deserializer function that will need to be added by the user to map the string response to the target object with
-- with a content type.
local
l_url: STRING_8
l_response: HTTP_CLIENT_RESPONSE
l_context_executor: HTTP_CLIENT_REQUEST_CONTEXT
l_content_type: STRING
l_error: API_ERROR
do
--! TODO remove hardcoded values!!!.
--! Maybe we can create an object REQUEST_PARAMETERS
-- call_api (a_param: REQUEST_PARAMETERS)
-- create context executor per request.
create l_context_executor.make
update_params_for_auth (a_request.auth_names, a_request.query_params, a_request.header_params)
l_url := build_url (a_path, a_request.query_params)
add_header_params (l_context_executor, a_request.header_params)
if attached a_request.header_params.at ("Content-Type") as ll_content_type then
l_content_type := ll_content_type
else
l_content_type := "application/json"
end
if not accepts_request_body (a_method) then
-- do nothing
elseif l_content_type.is_case_insensitive_equal ("application/x-www-form-urlencoded") then
add_form_data (l_context_executor, a_request.form_params)
elseif l_content_type.is_case_insensitive_equal ("multipart/form-data") then
-- add_mulipart_data (l_context_executor, a_form_params, l_content_type)
-- here we need a way to identify files.
elseif a_request.body = Void then
if a_method.is_case_insensitive_equal ("DELETE") then
-- DELETE no need a request body.
else
-- use an empty request body (for POST, PUT and PATCH)
l_context_executor.set_upload_data ("")
end
else
if
attached a_request.body as l_body and then
attached a_serializer as l_custom_serializer
then
-- Serialize an object `a_body' to content-type `l_type'
-- if not support by default you will need to extend
-- the target API with the corresponding implementation.
l_context_executor.set_upload_data((create {API_SERIALIZER}).serializer (l_custom_serializer, l_content_type, l_body))
end
end
if
a_method.is_case_insensitive_equal ("GET")
then
l_response := execute_get (l_context_executor, l_url)
elseif a_method.is_case_insensitive_equal ("POST") then
l_response := execute_post (l_context_executor, l_url, Void)
else
create l_error.make ("Method [" + a_method + "] not supported")
create Result.make (l_response, l_error, a_deserializer)
end
create Result.make (l_response, l_error, a_deserializer)
end
build_url (a_path: STRING_8; a_query_params: LIST [TUPLE [name: STRING; value: STRING]]): STRING_8
-- Build a relatative url to `base_path' with `a_path' and a list of
-- query parameters `a_query_params'.
local
l_query: STRING
do
if a_query_params.is_empty then
Result := a_path
else
Result := a_path
create l_query.make_empty
across a_query_params as ic loop
l_query.append (ic.item.name)
l_query.append_character ('=')
l_query.append (ic.item.value)
l_query.append_character ('&')
end
l_query.remove_tail (1)
if Result.has ('?') then
Result.append_character ('&')
Result.append (l_query)
else
Result.append_character ('?')
Result.append (l_query)
end
end
end
add_header_params (a_content_executor:HTTP_CLIENT_REQUEST_CONTEXT; a_header_params: STRING_TABLE [STRING])
-- Set header parameters `a_header_params' to the request context executor `a_content_executor', including default headers.
do
-- headers
across a_header_params as ic loop
a_content_executor.add_header (ic.key.as_string_8, ic.item)
end
-- default headers
across default_header_map as ic loop
if not a_header_params.has (ic.key) then
a_content_executor.add_header (ic.key.as_string_8, ic.item)
end
end
end
add_form_data (a_content_executor:HTTP_CLIENT_REQUEST_CONTEXT; a_form_params: STRING_TABLE [ANY])
-- Set form parameters `a_form_params' to the request context executor `a_content_executor'.
do
-- form params
across a_form_params as ic loop
if attached {STRING} ic.item as l_item then
a_content_executor.add_form_parameter (ic.key, l_item)
end
end
end
add_multipart_data (a_content_executor:HTTP_CLIENT_REQUEST_CONTEXT; a_form_params: STRING_TABLE [ANY]; a_content_type: STRING)
-- Set form parameters `a_form_params' to the request context executor `a_content_executor'.
do
-- multipart form params
across a_form_params as ic loop
if attached {FILE} ic.item as l_item then
a_content_executor.add_header ("Content-Type", a_content_type )
a_content_executor.set_upload_filename (l_item.path.absolute_path.name)
a_content_executor.add_form_parameter (ic.key, l_item.path.name)
end
end
end
feature -- HTTP client: Change Element
set_base_path (a_path: STRING)
-- Set `base_path' with `a_path'.
require
is_valid_uri: is_valid_uri (a_path)
do
base_path := a_path
end
set_user_agent (a_agent: STRING_8)
-- Set user agent with `a_agent`.
do
add_header ("User-Agent", a_agent)
end
add_header (k: READABLE_STRING_8; v: READABLE_STRING_8)
-- Add http header line `k:v'.
do
default_header_map.force (v, k)
end
feature {NONE} -- Implementation
default_header_map: STRING_TABLE [STRING]
-- default header map.
http_session: detachable HTTP_CLIENT_SESSION
-- http client session.
get_http_session
-- Initialize http client session.
local
h: DEFAULT_HTTP_CLIENT
b: like base_path
do
create h
b := base_path
if b = Void then
b := ""
end
b.left_adjust
b.right_justify
if attached {HTTP_CLIENT_SESSION} h.new_session (b) as sess then
http_session := sess
sess.set_timeout (-1)
sess.set_connect_timeout (-1)
sess.set_is_insecure (True)
sess.set_any_auth_type
debug ("curl")
sess.set_is_debug (True)
end
debug ("proxy8888")
sess.set_proxy ("127.0.0.1", 8888) --| inspect traffic with http://www.fiddler2.com/
end
end
end
feature -- HTTP client helpers
execute_get (context_executor: HTTP_CLIENT_REQUEST_CONTEXT; command_name: READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
do
get_http_session
if attached http_session as sess then
Result := sess.get (command_name, context_executor)
end
end
execute_post (context_executor: HTTP_CLIENT_REQUEST_CONTEXT; command_name: READABLE_STRING_8; data: detachable READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
do
get_http_session
if attached http_session as sess then
Result := sess.post (command_name, context_executor, data)
end
end
execute_delete (context_executor: HTTP_CLIENT_REQUEST_CONTEXT; command_name: READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
do
get_http_session
if attached http_session as sess then
Result := sess.delete (command_name, context_executor)
end
end
execute_put (context_executor: HTTP_CLIENT_REQUEST_CONTEXT; command_name: READABLE_STRING_8; data: detachable READABLE_STRING_8): detachable HTTP_CLIENT_RESPONSE
do
get_http_session
if attached http_session as sess then
Result := sess.put (command_name, context_executor, data)
end
end
end
© 2015 - 2024 Weber Informatics LLC | Privacy Policy