lusid-sdk 2.0.50b0__py3-none-any.whl → 2.0.470__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of lusid-sdk might be problematic. Click here for more details.

Files changed (332) hide show
  1. lusid/__init__.py +1117 -14
  2. lusid/api/__init__.py +69 -0
  3. lusid/api/abor_api.py +513 -179
  4. lusid/api/abor_configuration_api.py +25 -24
  5. lusid/api/allocations_api.py +12 -20
  6. lusid/api/blocks_api.py +6 -6
  7. lusid/api/calendars_api.py +16 -24
  8. lusid/api/chart_of_accounts_api.py +1745 -441
  9. lusid/api/compliance_api.py +71 -62
  10. lusid/api/configuration_recipe_api.py +1198 -56
  11. lusid/api/corporate_action_sources_api.py +8 -8
  12. lusid/api/custom_entities_api.py +6 -6
  13. lusid/api/cut_label_definitions_api.py +9 -17
  14. lusid/api/data_types_api.py +16 -32
  15. lusid/api/executions_api.py +6 -6
  16. lusid/api/funds_api.py +1119 -0
  17. lusid/api/instrument_event_types_api.py +1287 -0
  18. lusid/api/instruments_api.py +12 -20
  19. lusid/api/legacy_compliance_api.py +12 -12
  20. lusid/api/legal_entities_api.py +12 -12
  21. lusid/api/order_graph_api.py +12 -12
  22. lusid/api/order_instructions_api.py +6 -6
  23. lusid/api/order_management_api.py +495 -14
  24. lusid/api/orders_api.py +16 -24
  25. lusid/api/packages_api.py +6 -6
  26. lusid/api/persons_api.py +16 -24
  27. lusid/api/placements_api.py +12 -12
  28. lusid/api/portfolio_groups_api.py +6 -6
  29. lusid/api/portfolios_api.py +23 -39
  30. lusid/api/property_definitions_api.py +205 -0
  31. lusid/api/queryable_keys_api.py +211 -0
  32. lusid/api/quotes_api.py +20 -36
  33. lusid/api/reconciliations_api.py +410 -1978
  34. lusid/api/schemas_api.py +7 -15
  35. lusid/api/scopes_api.py +151 -0
  36. lusid/api/scripted_translation_api.py +30 -28
  37. lusid/api/staging_rule_set_api.py +885 -0
  38. lusid/api/transaction_configuration_api.py +22 -22
  39. lusid/api/transaction_portfolios_api.py +282 -577
  40. lusid/api_client.py +5 -3
  41. lusid/configuration.py +1 -1
  42. lusid/extensions/__init__.py +10 -7
  43. lusid/extensions/api_client.py +3 -1
  44. lusid/extensions/api_client_factory.py +156 -45
  45. lusid/extensions/api_configuration.py +124 -15
  46. lusid/extensions/configuration_loaders.py +2 -4
  47. lusid/extensions/proxy_config.py +8 -5
  48. lusid/extensions/socket_keep_alive.py +14 -15
  49. lusid/extensions/tcp_keep_alive_connector.py +93 -46
  50. lusid/models/__init__.py +1028 -13
  51. lusid/models/abor.py +9 -2
  52. lusid/models/abor_configuration.py +8 -8
  53. lusid/models/abor_configuration_request.py +9 -9
  54. lusid/models/abor_request.py +1 -1
  55. lusid/models/account.py +6 -1
  56. lusid/models/accumulation_event.py +104 -0
  57. lusid/models/address_key_compliance_parameter.py +5 -12
  58. lusid/models/address_key_list_compliance_parameter.py +3 -3
  59. lusid/models/address_key_option_definition.py +3 -1
  60. lusid/models/amortisation_event.py +4 -6
  61. lusid/models/{underlying_leg.py → asset_leg.py} +15 -15
  62. lusid/models/basket.py +3 -3
  63. lusid/models/block_and_order_id_request.py +78 -0
  64. lusid/models/block_and_orders.py +83 -0
  65. lusid/models/block_and_orders_create_request.py +77 -0
  66. lusid/models/block_and_orders_request.py +134 -0
  67. lusid/models/blocked_order_request.py +130 -0
  68. lusid/models/bond.py +13 -6
  69. lusid/models/bond_coupon_event.py +97 -0
  70. lusid/models/bond_default_event.py +8 -18
  71. lusid/models/bond_principal_event.py +97 -0
  72. lusid/models/book_transactions_request.py +97 -0
  73. lusid/models/bool_compliance_parameter.py +3 -3
  74. lusid/models/bool_list_compliance_parameter.py +3 -3
  75. lusid/models/branch_step.py +101 -0
  76. lusid/models/cap_floor.py +3 -3
  77. lusid/models/cash_dividend_event.py +32 -10
  78. lusid/models/cash_election.py +91 -0
  79. lusid/models/cash_flow_event.py +5 -7
  80. lusid/models/cash_perpetual.py +3 -3
  81. lusid/models/cds_flow_conventions.py +1 -1
  82. lusid/models/cds_index.py +4 -4
  83. lusid/models/check_step.py +110 -0
  84. lusid/models/cleardown_module_details.py +95 -0
  85. lusid/models/cleardown_module_request.py +117 -0
  86. lusid/models/cleardown_module_response.py +139 -0
  87. lusid/models/cleardown_module_rule.py +94 -0
  88. lusid/models/{reconciliation_run_break.py → cleardown_module_rules_updated_response.py} +30 -36
  89. lusid/models/close_event.py +3 -3
  90. lusid/models/close_period_diary_entry_request.py +149 -0
  91. lusid/models/complete_portfolio.py +8 -1
  92. lusid/models/complex_bond.py +4 -4
  93. lusid/models/complex_market_data.py +6 -5
  94. lusid/models/compliance_parameter.py +8 -5
  95. lusid/models/compliance_parameter_type.py +3 -0
  96. lusid/models/compliance_rule_breakdown.py +16 -8
  97. lusid/models/compliance_rule_breakdown_request.py +12 -4
  98. lusid/models/compliance_rule_result_v2.py +85 -0
  99. lusid/models/compliance_step.py +99 -0
  100. lusid/models/compliance_step_type.py +42 -0
  101. lusid/models/compliance_summary_rule_result.py +12 -15
  102. lusid/models/compliance_summary_rule_result_request.py +12 -15
  103. lusid/models/compliance_template_variation.py +12 -2
  104. lusid/models/component_transaction.py +92 -0
  105. lusid/models/composite_dispersion.py +30 -5
  106. lusid/models/compounding.py +4 -4
  107. lusid/models/configuration_recipe.py +10 -19
  108. lusid/models/constant_volatility_surface.py +102 -0
  109. lusid/models/contract_for_difference.py +3 -3
  110. lusid/models/create_derived_property_definition_request.py +3 -3
  111. lusid/models/create_derived_transaction_portfolio_request.py +10 -3
  112. lusid/models/create_property_definition_request.py +12 -5
  113. lusid/models/create_staging_rule_set_request.py +91 -0
  114. lusid/models/create_trade_tickets_response.py +87 -0
  115. lusid/models/create_transaction_portfolio_request.py +16 -3
  116. lusid/models/credit_default_swap.py +4 -4
  117. lusid/models/credit_spread_curve_data.py +4 -4
  118. lusid/models/custom_entity_definition.py +8 -2
  119. lusid/models/custom_entity_type.py +8 -2
  120. lusid/models/cut_label_definition.py +7 -1
  121. lusid/models/data_type.py +7 -1
  122. lusid/models/data_type_summary.py +8 -2
  123. lusid/models/date_time_compliance_parameter.py +3 -3
  124. lusid/models/date_time_list_compliance_parameter.py +3 -3
  125. lusid/models/{upsert_reconciliation_run_request.py → day_month.py} +15 -15
  126. lusid/models/decimal_compliance_parameter.py +3 -3
  127. lusid/models/decimal_list_compliance_parameter.py +3 -3
  128. lusid/models/dialect.py +9 -3
  129. lusid/models/diary_entry.py +1 -1
  130. lusid/models/diary_entry_request.py +1 -1
  131. lusid/models/discount_factor_curve_data.py +3 -3
  132. lusid/models/dividend_option_event.py +129 -0
  133. lusid/models/dividend_reinvestment_event.py +124 -0
  134. lusid/models/election_specification.py +73 -0
  135. lusid/models/eligibility_calculation.py +71 -0
  136. lusid/models/empty_model_options.py +3 -3
  137. lusid/models/equity.py +8 -6
  138. lusid/models/equity_curve_by_prices_data.py +3 -3
  139. lusid/models/equity_model_options.py +3 -3
  140. lusid/models/equity_option.py +3 -3
  141. lusid/models/equity_swap.py +4 -4
  142. lusid/models/equity_vol_surface_data.py +3 -3
  143. lusid/models/exchange_traded_option.py +3 -3
  144. lusid/models/exercise_event.py +5 -7
  145. lusid/models/exotic_instrument.py +3 -3
  146. lusid/models/expiry_event.py +91 -0
  147. lusid/models/filter_predicate_compliance_parameter.py +91 -0
  148. lusid/models/filter_step.py +101 -0
  149. lusid/models/fixed_leg.py +3 -3
  150. lusid/models/fixed_schedule.py +4 -9
  151. lusid/models/flexible_loan.py +105 -0
  152. lusid/models/float_schedule.py +20 -12
  153. lusid/models/floating_leg.py +3 -3
  154. lusid/models/flow_convention_name.py +1 -1
  155. lusid/models/flow_conventions.py +1 -1
  156. lusid/models/forward_rate_agreement.py +3 -3
  157. lusid/models/from_recipe.py +81 -0
  158. lusid/models/fund.py +182 -0
  159. lusid/models/fund_properties.py +115 -0
  160. lusid/models/fund_request.py +165 -0
  161. lusid/models/fund_share_class.py +99 -0
  162. lusid/models/funding_leg.py +3 -3
  163. lusid/models/funding_leg_options.py +3 -3
  164. lusid/models/future.py +3 -3
  165. lusid/models/fx_conventions.py +73 -0
  166. lusid/models/fx_forward.py +8 -6
  167. lusid/models/fx_forward_curve_by_quote_reference.py +4 -4
  168. lusid/models/fx_forward_curve_data.py +3 -3
  169. lusid/models/fx_forward_model_options.py +3 -3
  170. lusid/models/fx_forward_pips_curve_data.py +3 -3
  171. lusid/models/fx_forward_settlement_event.py +136 -0
  172. lusid/models/fx_forward_tenor_curve_data.py +4 -4
  173. lusid/models/fx_forward_tenor_pips_curve_data.py +4 -4
  174. lusid/models/fx_linked_notional_schedule.py +108 -0
  175. lusid/models/fx_option.py +3 -3
  176. lusid/models/fx_rate_schedule.py +3 -3
  177. lusid/models/fx_swap.py +4 -4
  178. lusid/models/fx_vol_surface_data.py +3 -3
  179. lusid/models/{reconciliation_run.py → get_recipe_composer_response.py} +15 -15
  180. lusid/models/group_by_selector_compliance_parameter.py +91 -0
  181. lusid/models/group_by_step.py +101 -0
  182. lusid/models/group_filter_predicate_compliance_parameter.py +91 -0
  183. lusid/models/group_filter_step.py +110 -0
  184. lusid/models/group_of_market_data_key_rules.py +79 -0
  185. lusid/models/index_convention.py +1 -1
  186. lusid/models/index_model_options.py +3 -3
  187. lusid/models/inflation_index_conventions.py +2 -2
  188. lusid/models/inflation_leg.py +3 -3
  189. lusid/models/inflation_linked_bond.py +3 -3
  190. lusid/models/inflation_swap.py +4 -4
  191. lusid/models/informational_error_event.py +3 -3
  192. lusid/models/informational_event.py +4 -6
  193. lusid/models/instrument_event.py +13 -5
  194. lusid/models/instrument_event_configuration.py +74 -0
  195. lusid/models/instrument_event_holder.py +12 -3
  196. lusid/models/instrument_event_type.py +8 -0
  197. lusid/models/instrument_leg.py +3 -3
  198. lusid/models/instrument_list_compliance_parameter.py +3 -3
  199. lusid/models/instrument_payment_diary_leg.py +5 -3
  200. lusid/models/instrument_resolution_detail.py +105 -0
  201. lusid/models/instrument_type.py +2 -0
  202. lusid/models/interest_rate_swap.py +4 -4
  203. lusid/models/interest_rate_swaption.py +3 -3
  204. lusid/models/intermediate_compliance_step.py +110 -0
  205. lusid/models/ir_vol_cube_data.py +3 -3
  206. lusid/models/journal_entry_line.py +34 -3
  207. lusid/models/journal_entry_lines_query_parameters.py +1 -1
  208. lusid/models/label_value_set.py +1 -1
  209. lusid/models/leg_definition.py +16 -3
  210. lusid/models/lineage_member.py +87 -0
  211. lusid/models/lock_period_diary_entry_request.py +91 -0
  212. lusid/models/lusid_instrument.py +7 -5
  213. lusid/models/lusid_trade_ticket.py +8 -1
  214. lusid/models/market_context.py +17 -2
  215. lusid/models/market_data_type.py +1 -0
  216. lusid/models/maturity_event.py +91 -0
  217. lusid/models/model_options.py +5 -6
  218. lusid/models/model_options_type.py +0 -1
  219. lusid/models/model_selection.py +3 -3
  220. lusid/models/move_orders_to_different_blocks_request.py +77 -0
  221. lusid/models/moved_order_to_different_block_response.py +85 -0
  222. lusid/models/movement_type.py +2 -0
  223. lusid/models/multi_currency_amounts.py +71 -0
  224. lusid/models/opaque_market_data.py +3 -3
  225. lusid/models/opaque_model_options.py +3 -3
  226. lusid/models/open_event.py +3 -3
  227. lusid/models/optionality_schedule.py +3 -3
  228. lusid/models/order_graph_block.py +4 -2
  229. lusid/models/order_graph_block_order_detail.py +16 -2
  230. lusid/models/output_transaction.py +9 -2
  231. lusid/models/paged_resource_list_of_cleardown_module_response.py +113 -0
  232. lusid/models/{paged_resource_list_of_reconciliation_run_break.py → paged_resource_list_of_cleardown_module_rule.py} +11 -11
  233. lusid/models/{paged_resource_list_of_reconciliation_run.py → paged_resource_list_of_fund.py} +11 -11
  234. lusid/models/paged_resource_list_of_property_definition.py +113 -0
  235. lusid/models/paged_resource_list_of_staging_rule_set.py +113 -0
  236. lusid/models/paged_resource_list_of_transaction_template.py +113 -0
  237. lusid/models/paged_resource_list_of_transaction_template_specification.py +113 -0
  238. lusid/models/participation_request.py +3 -9
  239. lusid/models/performance_returns_metric.py +1 -1
  240. lusid/models/period_diary_entries_reopened_response.py +104 -0
  241. lusid/models/place_blocks_request.py +77 -0
  242. lusid/models/portfolio.py +15 -2
  243. lusid/models/portfolio_details.py +15 -2
  244. lusid/models/portfolio_group_id_compliance_parameter.py +3 -3
  245. lusid/models/portfolio_group_id_list_compliance_parameter.py +3 -3
  246. lusid/models/portfolio_holding.py +27 -2
  247. lusid/models/portfolio_id_compliance_parameter.py +3 -3
  248. lusid/models/portfolio_id_list_compliance_parameter.py +3 -3
  249. lusid/models/posting_module_rule.py +29 -4
  250. lusid/models/pricing_model.py +2 -1
  251. lusid/models/property_definition.py +17 -4
  252. lusid/models/property_definition_search_result.py +3 -3
  253. lusid/models/property_domain.py +3 -0
  254. lusid/models/property_key_compliance_parameter.py +3 -3
  255. lusid/models/property_key_list_compliance_parameter.py +3 -3
  256. lusid/models/queryable_key.py +124 -0
  257. lusid/models/raw_vendor_event.py +5 -7
  258. lusid/models/re_open_period_diary_entry_request.py +84 -0
  259. lusid/models/recipe_block.py +87 -0
  260. lusid/models/recipe_composer.py +100 -0
  261. lusid/models/{reconciliation_break_id.py → recipe_value.py} +22 -23
  262. lusid/models/recombine_step.py +101 -0
  263. lusid/models/reference_instrument.py +3 -3
  264. lusid/models/relative_date_offset.py +71 -0
  265. lusid/models/repo.py +3 -3
  266. lusid/models/reset_event.py +4 -6
  267. lusid/models/resource_list_of_block_and_orders.py +113 -0
  268. lusid/models/resource_list_of_get_recipe_composer_response.py +113 -0
  269. lusid/models/resource_list_of_moved_order_to_different_block_response.py +113 -0
  270. lusid/models/resource_list_of_queryable_key.py +113 -0
  271. lusid/models/schedule.py +6 -5
  272. lusid/models/schedule_type.py +1 -0
  273. lusid/models/script_map_reference.py +94 -0
  274. lusid/models/security_election.py +86 -0
  275. lusid/models/set_share_class_instruments_request.py +79 -0
  276. lusid/models/side_definition.py +1 -8
  277. lusid/models/sides_definition_request.py +1 -8
  278. lusid/models/simple_cash_flow_loan.py +3 -3
  279. lusid/models/simple_instrument.py +3 -3
  280. lusid/models/staging_rule.py +90 -0
  281. lusid/models/staging_rule_approval_criteria.py +81 -0
  282. lusid/models/staging_rule_match_criteria.py +95 -0
  283. lusid/models/staging_rule_set.py +103 -0
  284. lusid/models/step_schedule.py +3 -3
  285. lusid/models/stock_split_event.py +3 -3
  286. lusid/models/string_compliance_parameter.py +3 -3
  287. lusid/models/string_list_compliance_parameter.py +3 -3
  288. lusid/models/template_field.py +77 -0
  289. lusid/models/term_deposit.py +3 -3
  290. lusid/models/total_return_swap.py +16 -16
  291. lusid/models/transaction_configuration_movement_data.py +3 -3
  292. lusid/models/transaction_configuration_movement_data_request.py +3 -3
  293. lusid/models/transaction_currency_and_amount.py +81 -0
  294. lusid/models/transaction_field_map.py +97 -0
  295. lusid/models/transaction_price.py +3 -3
  296. lusid/models/transaction_price_and_type.py +81 -0
  297. lusid/models/transaction_price_type.py +1 -0
  298. lusid/models/transaction_property_map.py +80 -0
  299. lusid/models/transaction_template.py +100 -0
  300. lusid/models/transaction_template_request.py +79 -0
  301. lusid/models/transaction_template_specification.py +99 -0
  302. lusid/models/transaction_type_alias.py +0 -7
  303. lusid/models/transaction_type_calculation.py +1 -1
  304. lusid/models/transition_event.py +3 -3
  305. lusid/models/translation_context.py +75 -0
  306. lusid/models/translation_script.py +9 -3
  307. lusid/models/trial_balance.py +46 -11
  308. lusid/models/trial_balance_query_parameters.py +15 -6
  309. lusid/models/trigger_event.py +3 -3
  310. lusid/models/units_ratio.py +71 -0
  311. lusid/models/update_staging_rule_set_request.py +91 -0
  312. lusid/models/{compliance_run_summary.py → upsert_compliance_run_summary_result.py} +8 -8
  313. lusid/models/upsert_dialect_request.py +79 -0
  314. lusid/models/upsert_instrument_event_request.py +12 -3
  315. lusid/models/upsert_quote_request.py +1 -1
  316. lusid/models/upsert_recipe_composer_request.py +73 -0
  317. lusid/models/upsert_recipe_request.py +3 -9
  318. lusid/models/upsert_translation_script_request.py +75 -0
  319. lusid/models/valuation_schedule.py +10 -3
  320. lusid/models/weighted_instrument.py +13 -2
  321. lusid/models/weighted_instrument_in_line_lookup_identifiers.py +89 -0
  322. lusid/models/yield_curve_data.py +3 -3
  323. lusid/rest.py +1 -1
  324. {lusid_sdk-2.0.50b0.dist-info → lusid_sdk-2.0.470.dist-info}/METADATA +245 -48
  325. {lusid_sdk-2.0.50b0.dist-info → lusid_sdk-2.0.470.dist-info}/RECORD +326 -235
  326. {lusid_sdk-2.0.50b0.dist-info → lusid_sdk-2.0.470.dist-info}/WHEEL +1 -1
  327. lusid/extensions/api_client_builder.py +0 -138
  328. lusid/models/configuration_recipe_snippet.py +0 -139
  329. lusid/models/je_lines_query_parameters.py +0 -105
  330. lusid/models/look_up_pricing_model_options.py +0 -93
  331. lusid/models/reconciliation_run_id.py +0 -85
  332. lusid/models/upsert_reconciliation_break_request.py +0 -98
lusid/api_client.py CHANGED
@@ -214,6 +214,8 @@ class ApiClient:
214
214
  url += "?" + url_query
215
215
 
216
216
  try:
217
+ # if returning http_data_only then we need to deserialise response.
218
+ _preload_content = True if _return_http_data_only else _preload_content
217
219
  # perform request and return response
218
220
  response_data = await self.request(
219
221
  method, url,
@@ -260,8 +262,8 @@ class ApiClient:
260
262
  else:
261
263
  return ApiResponse(status_code = response_data.status,
262
264
  data = return_data,
263
- headers = response_data.getheaders(),
264
- raw_data = response_data.data)
265
+ headers = response_data.getheaders() if _preload_content else response_data.headers,
266
+ raw_data = response_data.data if _preload_content else response_data)
265
267
 
266
268
  def sanitize_for_serialization(self, obj):
267
269
  """Builds a JSON POST object.
@@ -544,7 +546,7 @@ class ApiClient:
544
546
  if k in collection_formats:
545
547
  collection_format = collection_formats[k]
546
548
  if collection_format == 'multi':
547
- new_params.extend((k, value) for value in v)
549
+ new_params.extend((k, quote(str(value))) for value in v)
548
550
  else:
549
551
  if collection_format == 'ssv':
550
552
  delimiter = ' '
lusid/configuration.py CHANGED
@@ -373,7 +373,7 @@ class Configuration:
373
373
  return "Python SDK Debug Report:\n"\
374
374
  "OS: {env}\n"\
375
375
  "Python Version: {pyversion}\n"\
376
- "Version of the API: 0.11.6001\n"\
376
+ "Version of the API: 0.11.6417\n"\
377
377
  "SDK Package Version: {package_version}".\
378
378
  format(env=sys.platform, pyversion=sys.version, package_version=package_version)
379
379
 
@@ -1,15 +1,18 @@
1
- from lusid.extensions.api_client_builder import build_client
2
1
  from lusid.extensions.api_client_factory import SyncApiClientFactory, ApiClientFactory
3
2
  from lusid.extensions.configuration_loaders import (
4
3
  ConfigurationLoader,
5
4
  SecretsFileConfigurationLoader,
6
5
  EnvironmentVariablesConfigurationLoader,
7
6
  ArgsConfigurationLoader,
8
- get_api_configuration,
9
7
  )
10
- from lusid.extensions.api_configuration import ApiConfiguration
11
- from lusid.extensions.retry import RetryingRestWrapper, RetryingRestWrapperAsync
12
- from lusid.extensions.proxy_config import ProxyConfig
13
- from lusid.extensions.refreshing_token import RefreshingToken
14
8
  from lusid.extensions.api_client import SyncApiClient
15
- from lusid.extensions.rest import RESTClientObject
9
+
10
+ __all__ = [
11
+ "SyncApiClientFactory",
12
+ "ApiClientFactory",
13
+ "ConfigurationLoader",
14
+ "SecretsFileConfigurationLoader",
15
+ "EnvironmentVariablesConfigurationLoader",
16
+ "ArgsConfigurationLoader",
17
+ "SyncApiClient"
18
+ ]
@@ -213,6 +213,8 @@ class SyncApiClient:
213
213
  url += "?" + url_query
214
214
 
215
215
  try:
216
+ # if returning http_data_only then we need to deserialise response.
217
+ _preload_content = True if _return_http_data_only else _preload_content
216
218
  # perform request and return response
217
219
  response_data = self.request(
218
220
  method, url,
@@ -543,7 +545,7 @@ class SyncApiClient:
543
545
  if k in collection_formats:
544
546
  collection_format = collection_formats[k]
545
547
  if collection_format == 'multi':
546
- new_params.extend((k, value) for value in v)
548
+ new_params.extend((k, quote(str(value))) for value in v)
547
549
  else:
548
550
  if collection_format == 'ssv':
549
551
  delimiter = ' '
@@ -1,51 +1,117 @@
1
1
  from __future__ import annotations
2
+ from lusid.api_client import ApiClient
3
+ from lusid.extensions.retry import (
4
+ RetryingRestWrapper,
5
+ RetryingRestWrapperAsync
6
+ )
7
+ from lusid.extensions.api_client import SyncApiClient
2
8
  from lusid.extensions.configuration_loaders import (
3
9
  ConfigurationLoader,
4
10
  default_config_loaders,
11
+ get_api_configuration
12
+ )
13
+ from lusid.extensions.socket_keep_alive import keep_alive_socket_options
14
+ from lusid.extensions.tcp_keep_alive_connector import (
15
+ TcpKeepAliveConnector,
16
+ TCPKeepAliveHTTPConnectionPool,
17
+ TCPKeepAliveHTTPSConnectionPool,
5
18
  )
6
- from lusid.extensions.api_client_builder import build_client
7
- from lusid.extensions.configuration_loaders import get_api_configuration
19
+ from aiohttp import ClientSession
8
20
  import logging
9
- from typing import TypeVar, Type, Iterable
21
+ from typing import Any, Callable, Optional, Tuple, TypeVar, Type, Iterable, Union
22
+ import os
23
+ from requests import Response
10
24
 
11
25
  logger = logging.getLogger(__name__)
12
26
 
13
27
  T = TypeVar("T")
14
28
 
15
29
 
30
+ def set_additional_api_client_headers(
31
+ api_client: Union[ApiClient, SyncApiClient],
32
+ app_name: Optional[str] = None,
33
+ correlation_id: Optional[str] = None,
34
+ ) -> None:
35
+ """Sets additional headers for additional debugging info in LUSID
36
+
37
+ Parameters
38
+ ----------
39
+ api_client : Union[ApiClient, SyncApiClient]
40
+ api client to set headers on
41
+ app_name : Optional[str], optional
42
+ name of the application in LUSID, by default None
43
+ correlation_id : Optional[str], optional
44
+ correlation id to track requests in LUSID, by default None
45
+ """
46
+ try:
47
+ # set the application name if specified
48
+ if app_name is not None:
49
+ api_client.set_default_header("X-LUSID-Application", app_name)
50
+
51
+ # set a correlation id for all requests initiated with this ApiClient
52
+ corr_id = correlation_id or os.getenv("FBN_CORRELATION_ID")
53
+ if corr_id is not None:
54
+ api_client.set_default_header("CorrelationId", corr_id)
55
+ except AttributeError:
56
+ logger.exception("api_client must be either an ApiClient or a SyncApiClient")
57
+ raise
58
+
59
+
16
60
  class SyncApiClientFactory:
17
61
  def __init__(
18
62
  self,
19
63
  config_loaders: Iterable[ConfigurationLoader] = default_config_loaders,
20
- id_provider_response_handler=None,
21
- tcp_keep_alive=None,
22
- correlation_id=None,
23
- app_name=None
64
+ id_provider_response_handler: Callable[[Response], None] = None,
65
+ tcp_keep_alive: bool = True,
66
+ socket_options: Optional[
67
+ Union[Tuple[Any, Any, Any], Tuple[Any, Any, None, int]]
68
+ ] = keep_alive_socket_options(),
69
+ correlation_id: Optional[str] = None,
70
+ app_name: Optional[str] = None,
24
71
  ):
25
- """Create an ApiClientFactory which can build api objects with a configured ApiClient object
72
+ """Create an ApiClientFactory which can build
73
+ api objects with a configured ApiClient object
26
74
 
27
75
  Parameters
28
76
  ----------
29
77
  config_loaders : Iterable[ConfigurationLoader], optional
30
- An Iterable of ConfigurationLoaders we can load configuration from.
31
- Config settings are updated by each loader (last write wins), by default default_config_loaders
32
- id_provider_response_handler : _type_, optional
33
- A function that is called when a response is received from the token_url., by default None
34
- tcp_keep_alive : _type_, optional
35
- Should ApiClient be configured to send tcp keep alives., by default None
36
- correlation_id : _type_, optional
37
- A correlation ID that can be sent with each request., by default None
38
- app_name : _type_, optional
39
- The name of the application in LUSID., by default None
78
+ An Iterable of ConfigurationLoaders we can load configuration from.
79
+ Config settings are updated by each loader (last write wins),
80
+ by default default_config_loaders,
81
+ by default default_config_loaders
82
+ id_provider_response_handler : Callable[[Response], None], optional
83
+ A function that is called when a response is received from the token_url,
84
+ by default None
85
+ tcp_keep_alive : bool, optional
86
+ Should ApiClient be configured to send tcp keep alives, by default True
87
+ socket_options : Optional[ Union[Tuple[Any, Any, Any], Tuple[Any, Any, None, int]] ], optional
88
+ Set of socket options that should be applied to each connection,
89
+ by default keep_alive_socket_options
90
+ correlation_id : Optional[str], optional
91
+ A correlation ID that can be sent with each request, by default None
92
+ app_name : Optional[str], optional
93
+ The name of the application in LUSID, by default None
40
94
  """
41
95
  api_config = get_api_configuration(config_loaders=config_loaders)
42
- self.__api_client = build_client(
43
- api_config,
44
- build_async_client=False,
45
- id_provider_response_handler=id_provider_response_handler,
96
+ api_client_config = api_config.build_api_client_config(
46
97
  tcp_keep_alive=tcp_keep_alive,
47
- correlation_id=correlation_id,
48
- app_name=app_name
98
+ socket_options=socket_options,
99
+ id_provider_response_handler=id_provider_response_handler
100
+ )
101
+ self.__api_client = SyncApiClient(
102
+ configuration=api_client_config,
103
+ )
104
+
105
+ rest_client_wrapper = RetryingRestWrapper
106
+ rc = self.__api_client.rest_client
107
+ if tcp_keep_alive:
108
+ rc.pool_manager.pool_classes_by_scheme = {"http": TCPKeepAliveHTTPConnectionPool, "https": TCPKeepAliveHTTPSConnectionPool}
109
+
110
+ wrapped_rest_client = rest_client_wrapper(rc)
111
+ self.__api_client.rest_client = wrapped_rest_client
112
+
113
+ set_additional_api_client_headers(
114
+ self.__api_client, app_name=app_name, correlation_id=correlation_id
49
115
  )
50
116
 
51
117
  def __enter__(self):
@@ -79,35 +145,80 @@ class ApiClientFactory:
79
145
  def __init__(
80
146
  self,
81
147
  config_loaders: Iterable[ConfigurationLoader] = default_config_loaders,
82
- id_provider_response_handler=None,
83
- tcp_keep_alive=None,
84
- correlation_id=None,
85
- app_name=None
148
+ id_provider_response_handler: Callable[[Response], None] = None,
149
+ tcp_keep_alive: bool = True,
150
+ socket_options: Optional[
151
+ Union[Tuple[Any, Any, Any], Tuple[Any, Any, None, int]]
152
+ ] = keep_alive_socket_options(),
153
+ correlation_id: Optional[str] = None,
154
+ app_name: Optional[str] = None,
155
+ client_session: Optional[ClientSession] = None,
156
+ trace_configs: Optional[List[TraceConfig]] = None
86
157
  ):
87
- """Create an ApiClientFactory which can build api objects with a configured ApiClient object
158
+ """Create an ApiClientFactory which can build api
159
+ objects with a configured ApiClient object
88
160
 
89
161
  Parameters
90
162
  ----------
91
163
  config_loaders : Iterable[ConfigurationLoader], optional
92
- An Iterable of ConfigurationLoaders we can load configuration from.
93
- Config settings are updated by each loader (last write wins), by default default_config_loaders
94
- id_provider_response_handler : _type_, optional
95
- A function that is called when a response is received from the token_url., by default None
96
- tcp_keep_alive : _type_, optional
97
- Should ApiClient be configured to send tcp keep alives., by default None
98
- correlation_id : _type_, optional
99
- A correlation ID that can be sent with each request., by default None
100
- app_name : _type_, optional
101
- The name of the application in LUSID., by default None
164
+ An Iterable of ConfigurationLoaders we can load configuration from.
165
+ Config settings are updated by each loader (last write wins),
166
+ by default default_config_loaders
167
+ id_provider_response_handler : Callable[[Response], None], optional
168
+ A function that is called when a response is received from the token_url,
169
+ by default None
170
+ tcp_keep_alive : bool, optional
171
+ Should ApiClient be configured to send tcp keep alives, by default True
172
+ socket_options : Optional[ Union[Tuple[Any, Any, Any], Tuple[Any, Any, None, int]] ], optional
173
+ Set of socket options that should be applied to each connection,
174
+ by default keep_alive_socket_options
175
+ correlation_id : Optional[str], optional
176
+ A correlation ID that can be sent with each request, by default None
177
+ app_name : Optional[str], optional
178
+ The name of the application in LUSID, by default None
179
+ client_session : Optional[ClientSession], optional
180
+ An aiohttp.ClientSession, pass this to re-use
181
+ connections across different ApiFactories,
182
+ by default None
183
+ trace_configs: Optional[List[TraceConfig]], optional
184
+ A list of aiohttp TraceConfigs, used to set up request tracing.
185
+ by default None
102
186
  """
103
187
  api_config = get_api_configuration(config_loaders=config_loaders)
104
- self.__api_client = build_client(
105
- api_config,
106
- build_async_client=True,
107
- id_provider_response_handler=id_provider_response_handler,
188
+ api_client_config = api_config.build_api_client_config(
108
189
  tcp_keep_alive=tcp_keep_alive,
109
- correlation_id=correlation_id,
110
- app_name=app_name
190
+ socket_options=socket_options,
191
+ id_provider_response_handler=id_provider_response_handler
192
+ )
193
+ self.__api_client = ApiClient(
194
+ configuration=api_client_config,
195
+ )
196
+ rc = self.__api_client.rest_client
197
+ try:
198
+ if client_session is not None:
199
+ connector = client_session.connector
200
+ # by default take explicitly passed trace_config param
201
+ # otherwise copy from session.
202
+ trace_configs = trace_configs or client_session.trace_configs
203
+ else:
204
+ connector = rc.pool_manager.connector
205
+ if tcp_keep_alive:
206
+ connector = TcpKeepAliveConnector(connector=connector, socket_options=socket_options)
207
+ # dereference connector so existing session closes correctly
208
+ rc.pool_manager._connector = None
209
+ rc.pool_manager = ClientSession(
210
+ connector=connector,
211
+ trust_env=True,
212
+ trace_configs=trace_configs
213
+ )
214
+ except AttributeError:
215
+ logger.exception("client_session must be an aiohttp.ClientSession"
216
+ " object with an initialised TCP Connector")
217
+ rest_client_wrapper = RetryingRestWrapperAsync
218
+ wrapped_rest_client = rest_client_wrapper(rc)
219
+ self.__api_client.rest_client = wrapped_rest_client
220
+ set_additional_api_client_headers(
221
+ self.__api_client, app_name=app_name, correlation_id=correlation_id
111
222
  )
112
223
 
113
224
  async def __aenter__(self):
@@ -1,10 +1,28 @@
1
1
  import re
2
+ import logging
3
+ from typing import Optional, Union, Tuple, Any, Callable
4
+ from lusid.configuration import Configuration
5
+ from lusid.extensions.refreshing_token import RefreshingToken
6
+ from lusid.extensions.socket_keep_alive import keep_alive_socket_options
7
+ from lusid.extensions.proxy_config import ProxyConfig
8
+ from requests import Response
9
+ logger = logging.getLogger(__name__)
2
10
 
3
11
 
4
12
  class ApiConfiguration:
5
-
6
- def __init__(self, token_url=None, api_url=None, username=None, password=None, client_id=None, client_secret=None,
7
- app_name=None, certificate_filename=None, proxy_config=None, access_token=None):
13
+ def __init__(
14
+ self,
15
+ token_url=None,
16
+ api_url=None,
17
+ username=None,
18
+ password=None,
19
+ client_id=None,
20
+ client_secret=None,
21
+ app_name=None,
22
+ certificate_filename=None,
23
+ proxy_config:Optional[ProxyConfig]=None,
24
+ access_token=None,
25
+ ):
8
26
  """
9
27
  The configuration required to access LUSID, read more at https://support.finbourne.com/getting-started-with-apis-sdks
10
28
 
@@ -42,12 +60,19 @@ class ApiConfiguration:
42
60
  :param url: The url to format
43
61
  :return: An Okta token url (if the input is an Okta issuer url). The original url otherwise.
44
62
  """
45
- if (url is not None and
46
- # and it's an Okta oauth2 URL
47
- re.search(r'^http(s)?:\/\/.*\.okta\.com\/oauth2\/.+', url, flags=re.IGNORECASE) is not None and
48
- # and it's missing the token suffix
49
- re.search(r'\/v\d+\/token$', url, flags=re.IGNORECASE) is None):
50
- return url.rstrip('/') + '/v1/token'
63
+ if (
64
+ url is not None
65
+ and
66
+ # and it's an Okta oauth2 URL
67
+ re.search(
68
+ r"^http(s)?:\/\/.*\.okta\.com\/oauth2\/.+", url, flags=re.IGNORECASE
69
+ )
70
+ is not None
71
+ and
72
+ # and it's missing the token suffix
73
+ re.search(r"\/v\d+\/token$", url, flags=re.IGNORECASE) is None
74
+ ):
75
+ return url.rstrip("/") + "/v1/token"
51
76
  return url
52
77
 
53
78
  self.__token_url = format_token_url(value)
@@ -116,10 +141,94 @@ class ApiConfiguration:
116
141
  def proxy_config(self, value):
117
142
  self.__proxy_config = value
118
143
 
119
- @property
120
- def access_token(self):
121
- return self.__access_token
144
+ def __setattr__(self, name, value):
145
+ if name == "access_token":
146
+ self.__access_token = value
147
+ super(ApiConfiguration, self).__setattr__(name, value)
148
+
149
+ def get_access_token(
150
+ self, id_provider_response_handler: Callable[[Response], None] = None
151
+ ) -> Union[str, RefreshingToken]:
152
+ """Gets either the set personal access token, or a RefreshingToken using OIDC parameters
122
153
 
123
- @access_token.setter
124
- def access_token(self, value):
125
- self.__access_token = value
154
+ Returns
155
+ -------
156
+ Union[str, RefreshingToken]
157
+ Token that can be used to authenticate to LUSID
158
+
159
+ """
160
+ try:
161
+ if self.__access_token is not None:
162
+ return self.__access_token
163
+ logger.debug(
164
+ "Access token not provided, \
165
+ will attempt to set up client using OIDC parameters"
166
+ )
167
+ return RefreshingToken(
168
+ api_configuration=self,
169
+ id_provider_response_handler=id_provider_response_handler,
170
+ )
171
+ except AttributeError:
172
+ logger.exception(
173
+ "Could not retrieve access token - "
174
+ "ensure api_config is an ApiConfiguration object"
175
+ )
176
+ raise
177
+ except ValueError:
178
+ logger.exception(
179
+ "Could not retrieve access token - "
180
+ "ensure fields required to authenticate are set"
181
+ )
182
+ raise
183
+
184
+ def build_api_client_config(
185
+ self,
186
+ tcp_keep_alive: bool = True,
187
+ socket_options: Optional[
188
+ Union[Tuple[Any, Any, Any], Tuple[Any, Any, None, int]]
189
+ ] = keep_alive_socket_options(),
190
+ id_provider_response_handler: Optional[Callable[[Response], None]] = None,
191
+ ) -> Configuration:
192
+ """Builds lusid.Configuration for initialising an api client.
193
+
194
+ Parameters
195
+ ----------
196
+ tcp_keep_alive : bool, optional
197
+ Should socket options for tcp keep alive pings be set, by default True
198
+ socket_options : Optional[ Union[Tuple[Any, Any, Any], Tuple[Any, Any, None, int]] ], optional
199
+ A set of custom options to configure on connections, by default keep_alive_socket_options()
200
+ id_provider_response_handler : Optional[Callable[[Response], None]], optional
201
+ A function to run on response from the identity provider, by default None
202
+
203
+ Returns
204
+ -------
205
+ Configuration
206
+ config which can be used to initialise an api client
207
+ """
208
+ access_token = self.get_access_token(
209
+ id_provider_response_handler=id_provider_response_handler
210
+ )
211
+ try:
212
+ if self.api_url is None:
213
+ logger.error("Api Url must have a value")
214
+ raise ValueError("Api Url must have a value")
215
+ if access_token is None:
216
+ logger.error("Access token must have a value")
217
+ raise ValueError("Access token must have a value")
218
+ config = Configuration(
219
+ access_token=access_token,
220
+ host=self.api_url,
221
+ ssl_ca_cert=self.certificate_filename,
222
+ )
223
+ if tcp_keep_alive:
224
+ config.socket_options = socket_options or keep_alive_socket_options()
225
+ # Set the proxy for lusid if needed
226
+ if self.proxy_config is not None:
227
+ config.proxy = self.proxy_config.address
228
+ config.proxy_headers = self.proxy_config.headers
229
+ return config
230
+ except (AttributeError, ValueError):
231
+ logger.exception(
232
+ "Unable to build api client, required configuration not provided"
233
+ )
234
+ raise
@@ -24,7 +24,7 @@ ENVIRONMENT_CONFIG_KEYS = {
24
24
 
25
25
  SECRETS_FILE_CONFIG_KEYS = {
26
26
  "token_url": "tokenUrl",
27
- "api_url": "apiUrl",
27
+ "api_url": "lusidUrl",
28
28
  "username": "username",
29
29
  "password": "password",
30
30
  "client_id": "clientId",
@@ -191,9 +191,7 @@ def get_api_configuration(config_loaders: Iterable[ConfigurationLoader]) -> ApiC
191
191
  proxy_username = config.pop("proxy_username", None)
192
192
  proxy_password = config.pop("proxy_password", None)
193
193
  # If the proxy address is missing ensure that no proxy is used in the ApiConfiguration
194
- if all(
195
- (item is not None for item in (proxy_address, proxy_password, proxy_username))
196
- ):
194
+ if proxy_address is not None:
197
195
  config["proxy_config"] = ProxyConfig(
198
196
  address=proxy_address, username=proxy_username, password=proxy_password
199
197
  )
@@ -1,5 +1,5 @@
1
1
  from urllib3 import make_headers
2
- from typing import Any
2
+ from typing import Dict
3
3
 
4
4
 
5
5
  class ProxyConfig:
@@ -56,7 +56,7 @@ class ProxyConfig:
56
56
  return {"http": proxy_url, "https": proxy_url}
57
57
 
58
58
  @property
59
- def headers(self) -> Any:
59
+ def headers(self) -> Dict[str, str]:
60
60
  """Return Proxy auth headers
61
61
 
62
62
  Returns
@@ -64,6 +64,9 @@ class ProxyConfig:
64
64
  Any
65
65
  Proxy auth headers
66
66
  """
67
- return make_headers(
68
- proxy_basic_auth=f"{self.__username}:{self.__password}"
69
- )
67
+ if self.__username is not None and self.__password is not None:
68
+ return make_headers(
69
+ proxy_basic_auth=f"{self.__username}:{self.__password}"
70
+ )
71
+ else:
72
+ return {}
@@ -5,6 +5,15 @@ from urllib3.connection import HTTPConnection
5
5
 
6
6
  __logger__ = logging.getLogger(__name__)
7
7
 
8
+ # The content to send on Mac OS in the TCP Keep Alive probe
9
+ TCP_KEEPALIVE = 0x10
10
+ # The maximum time to keep the connection idle before sending probes
11
+ TCP_KEEP_IDLE = 60
12
+ # The interval between probes
13
+ TCP_KEEPALIVE_INTERVAL = 60
14
+ # The maximum number of failed probes before terminating the connection
15
+ TCP_KEEP_CNT = 3
16
+
8
17
 
9
18
  def keep_alive_socket_options() -> Sequence:
10
19
  """Returns default socket options for all platforms for
@@ -15,15 +24,6 @@ def keep_alive_socket_options() -> Sequence:
15
24
  Sequence
16
25
  Set of socket options.
17
26
  """
18
- # The content to send on Mac OS in the TCP Keep Alive probe
19
- TCP_KEEPALIVE = 0x10
20
- # The maximum time to keep the connection idle before sending probes
21
- TCP_KEEP_IDLE = 60
22
- # The interval between probes
23
- TCP_KEEPALIVE_INTERVAL = 60
24
- # The maximum number of failed probes before terminating the connection
25
- TCP_KEEP_CNT = 3
26
-
27
27
  try:
28
28
  # linux and some windows runtimes
29
29
  return HTTPConnection.default_socket_options + [
@@ -35,18 +35,17 @@ def keep_alive_socket_options() -> Sequence:
35
35
  except AttributeError:
36
36
  pass
37
37
  try:
38
- # other windows runtimes
38
+ # darwin
39
39
  return HTTPConnection.default_socket_options + [
40
- socket.SIO_KEEPALIVE_VALS,
41
- (1, TCP_KEEP_IDLE * 1000, TCP_KEEPALIVE_INTERVAL * 1000),
40
+ (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
41
+ (socket.IPPROTO_TCP, TCP_KEEPALIVE, TCP_KEEPALIVE_INTERVAL),
42
42
  ]
43
43
  except AttributeError:
44
44
  pass
45
45
  try:
46
- # darwin
46
+ # windows
47
47
  return HTTPConnection.default_socket_options + [
48
- (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
49
- (socket.IPPROTO_TCP, TCP_KEEPALIVE, TCP_KEEPALIVE_INTERVAL),
48
+ (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
50
49
  ]
51
50
  except AttributeError:
52
51
  __logger__.exception("Unable to set TCP Keep-alive socket options")