test-test-sink-test-test 0.0.2.pre.alpha.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +151 -0
- data/lib/sink/base_client.rb +404 -0
- data/lib/sink/base_model.rb +226 -0
- data/lib/sink/client.rb +391 -0
- data/lib/sink/fake_page.rb +44 -0
- data/lib/sink/models/address.rb +55 -0
- data/lib/sink/models/allof_base_parent.rb +18 -0
- data/lib/sink/models/allof_multiple_inline_entries.rb +15 -0
- data/lib/sink/models/api_status.rb +18 -0
- data/lib/sink/models/array_float_items_response.rb +6 -0
- data/lib/sink/models/array_missing_items_response.rb +18 -0
- data/lib/sink/models/array_object_items.rb +18 -0
- data/lib/sink/models/array_object_items_response.rb +18 -0
- data/lib/sink/models/array_recursion.rb +6 -0
- data/lib/sink/models/balance.rb +18 -0
- data/lib/sink/models/basic_shared_model_object.rb +23 -0
- data/lib/sink/models/body_param_top_level_all_of_response.rb +28 -0
- data/lib/sink/models/body_param_top_level_any_of_response.rb +6 -0
- data/lib/sink/models/body_param_top_level_one_of_response.rb +6 -0
- data/lib/sink/models/body_param_union_overlapping_prop_response.rb +19 -0
- data/lib/sink/models/card.rb +211 -0
- data/lib/sink/models/card_list_response.rb +36 -0
- data/lib/sink/models/card_provision_foo_response.rb +18 -0
- data/lib/sink/models/child_inlined_response_response.rb +18 -0
- data/lib/sink/models/child_model.rb +36 -0
- data/lib/sink/models/class_.rb +18 -0
- data/lib/sink/models/client.rb +23 -0
- data/lib/sink/models/company.rb +18 -0
- data/lib/sink/models/company_payment.rb +18 -0
- data/lib/sink/models/complex_query_array_query_response.rb +26 -0
- data/lib/sink/models/config_tool_model_ref_from_nested_response_body_response.rb +32 -0
- data/lib/sink/models/currency.rb +23 -0
- data/lib/sink/models/decorator_test_keep_me_response.rb +18 -0
- data/lib/sink/models/docstring_leading_double_quote_response.rb +20 -0
- data/lib/sink/models/docstring_trailing_double_quote_response.rb +32 -0
- data/lib/sink/models/documents.rb +23 -0
- data/lib/sink/models/eeoc.rb +18 -0
- data/lib/sink/models/employment_data.rb +18 -0
- data/lib/sink/models/enum_basic_response.rb +62 -0
- data/lib/sink/models/envelope_inline_response_response.rb +18 -0
- data/lib/sink/models/envelope_wrapped_array_response.rb +6 -0
- data/lib/sink/models/export.rb +19 -0
- data/lib/sink/models/file_create_multipart_response.rb +18 -0
- data/lib/sink/models/file_everything_multipart_response.rb +19 -0
- data/lib/sink/models/file_no_file_multipart_response.rb +18 -0
- data/lib/sink/models/file_with_optional_param_response.rb +18 -0
- data/lib/sink/models/funding_account.rb +100 -0
- data/lib/sink/models/import.rb +18 -0
- data/lib/sink/models/interface.rb +18 -0
- data/lib/sink/models/keep_this_resource_keep_this_method_response.rb +19 -0
- data/lib/sink/models/make_ambiguous_schemas_explicit_make_ambiguous_schemas_explicit_response.rb +45 -0
- data/lib/sink/models/make_ambiguous_schemas_looser_make_ambiguous_schemas_looser_response.rb +45 -0
- data/lib/sink/models/map_nullable_items_response.rb +28 -0
- data/lib/sink/models/method_config_skipped_tests_all_response.rb +19 -0
- data/lib/sink/models/method_config_skipped_tests_go_response.rb +19 -0
- data/lib/sink/models/method_config_skipped_tests_java_response.rb +19 -0
- data/lib/sink/models/method_config_skipped_tests_kotlin_response.rb +19 -0
- data/lib/sink/models/method_config_skipped_tests_node_and_python_response.rb +19 -0
- data/lib/sink/models/method_config_skipped_tests_node_response.rb +19 -0
- data/lib/sink/models/method_config_skipped_tests_python_response.rb +19 -0
- data/lib/sink/models/method_config_skipped_tests_ruby_response.rb +19 -0
- data/lib/sink/models/model_from_nested_path.rb +39 -0
- data/lib/sink/models/model_from_nested_response_body_ref.rb +18 -0
- data/lib/sink/models/model_from_schemas_ref.rb +18 -0
- data/lib/sink/models/model_from_schemas_ref_openapi_uri.rb +18 -0
- data/lib/sink/models/model_from_schemas_ref_openapi_uri_jmespath.rb +19 -0
- data/lib/sink/models/model_from_schemas_ref_openapi_uri_jsonpath.rb +19 -0
- data/lib/sink/models/model_level_1.rb +22 -0
- data/lib/sink/models/model_level_2.rb +22 -0
- data/lib/sink/models/model_level_3.rb +22 -0
- data/lib/sink/models/model_referenced_in_parent_and_child.rb +19 -0
- data/lib/sink/models/model_with_nested_model.rb +24 -0
- data/lib/sink/models/my_model.rb +18 -0
- data/lib/sink/models/name_child_prop_import_clash_response.rb +37 -0
- data/lib/sink/models/name_properties_common_conflicts_response.rb +84 -0
- data/lib/sink/models/name_properties_illegal_go_identifiers_response.rb +19 -0
- data/lib/sink/models/name_properties_illegal_javascript_identifiers_response.rb +6 -0
- data/lib/sink/models/name_response_property_clashes_model_import_response.rb +24 -0
- data/lib/sink/models/name_response_shadows_pydantic_response.rb +24 -0
- data/lib/sink/models/nested_request_model_a.rb +18 -0
- data/lib/sink/models/nested_request_model_b.rb +18 -0
- data/lib/sink/models/nested_request_model_c.rb +31 -0
- data/lib/sink/models/object_missing_items_response.rb +18 -0
- data/lib/sink/models/object_mixed_known_and_unknown_response.rb +19 -0
- data/lib/sink/models/object_multiple_array_properties_same_ref_response.rb +73 -0
- data/lib/sink/models/object_multiple_properties_same_model_response.rb +29 -0
- data/lib/sink/models/object_multiple_properties_same_ref_response.rb +68 -0
- data/lib/sink/models/object_skipped_props.rb +38 -0
- data/lib/sink/models/object_two_dimensional_array_primitive_property_response.rb +34 -0
- data/lib/sink/models/object_with_any_of_null_property.rb +31 -0
- data/lib/sink/models/object_with_child_ref.rb +23 -0
- data/lib/sink/models/object_with_one_of_null_property.rb +31 -0
- data/lib/sink/models/object_with_union_properties.rb +23 -0
- data/lib/sink/models/openapi_format_array_type_one_entry_response.rb +19 -0
- data/lib/sink/models/openapi_format_array_type_one_entry_with_null_response.rb +19 -0
- data/lib/sink/models/openapi_special_used_used_as_property_name_response.rb +19 -0
- data/lib/sink/models/page_cursor_shared_ref_pagination.rb +23 -0
- data/lib/sink/models/parent_model_with_child_ref.rb +28 -0
- data/lib/sink/models/path_param_colon_suffix_response.rb +18 -0
- data/lib/sink/models/path_param_file_extension_response.rb +18 -0
- data/lib/sink/models/path_param_multiple_response.rb +18 -0
- data/lib/sink/models/path_param_query_param_response.rb +18 -0
- data/lib/sink/models/path_param_singular_response.rb +18 -0
- data/lib/sink/models/primitive_strings_response.rb +18 -0
- data/lib/sink/models/private.rb +18 -0
- data/lib/sink/models/public.rb +18 -0
- data/lib/sink/models/recursion_create_envelope_response.rb +19 -0
- data/lib/sink/models/response_allof_simple_response.rb +23 -0
- data/lib/sink/models/response_array_object_with_union_properties_response.rb +6 -0
- data/lib/sink/models/response_array_response_response.rb +6 -0
- data/lib/sink/models/response_missing_required_response.rb +24 -0
- data/lib/sink/models/response_nested_array_response.rb +36 -0
- data/lib/sink/models/response_object_all_properties_response.rb +65 -0
- data/lib/sink/models/response_object_no_properties_response.rb +14 -0
- data/lib/sink/models/response_object_with_additional_properties_prop_response.rb +32 -0
- data/lib/sink/models/response_object_with_heavily_nested_union_response.rb +19 -0
- data/lib/sink/models/response_only_read_only_properties_response.rb +25 -0
- data/lib/sink/models/responses_allof_cross_object.rb +23 -0
- data/lib/sink/models/return_.rb +18 -0
- data/lib/sink/models/root_response.rb +31 -0
- data/lib/sink/models/self_recursion.rb +23 -0
- data/lib/sink/models/shared_cursor_nested_response_prop_meta.rb +33 -0
- data/lib/sink/models/shared_self_recursion.rb +23 -0
- data/lib/sink/models/shipping_address.rb +85 -0
- data/lib/sink/models/simple_allof.rb +28 -0
- data/lib/sink/models/simple_object.rb +32 -0
- data/lib/sink/models/skip_this_resource_i_never_appear_response.rb +19 -0
- data/lib/sink/models/streaming_basic_response.rb +23 -0
- data/lib/sink/models/streaming_nested_params_response.rb +23 -0
- data/lib/sink/models/streaming_no_discriminator_response.rb +24 -0
- data/lib/sink/models/streaming_query_param_discriminator_response.rb +24 -0
- data/lib/sink/models/streaming_with_unrelated_default_param_response.rb +24 -0
- data/lib/sink/models/type_dates_response.rb +38 -0
- data/lib/sink/models/type_datetimes_response.rb +38 -0
- data/lib/sink/models/union_discriminated_variant_a.rb +27 -0
- data/lib/sink/models/union_discriminated_variant_b.rb +27 -0
- data/lib/sink/models/union_response_discriminated_by_property_name_response.rb +6 -0
- data/lib/sink/models/union_response_discriminated_with_basic_mapping_response.rb +6 -0
- data/lib/sink/models/union_type_mixed_types_response.rb +6 -0
- data/lib/sink/models/union_type_nullable_union_response.rb +6 -0
- data/lib/sink/models/union_type_objects_response.rb +6 -0
- data/lib/sink/models/union_type_super_mixed_types_response.rb +6 -0
- data/lib/sink/models/union_type_unknown_variant_response.rb +6 -0
- data/lib/sink/models/version_1_30_name_create_response.rb +18 -0
- data/lib/sink/models/widget.rb +19 -0
- data/lib/sink/models/write_only_response_simple_response.rb +21 -0
- data/lib/sink/page_cursor.rb +54 -0
- data/lib/sink/page_cursor_from_headers.rb +54 -0
- data/lib/sink/page_cursor_id.rb +50 -0
- data/lib/sink/page_cursor_nested_items.rb +84 -0
- data/lib/sink/page_cursor_nested_object_ref.rb +71 -0
- data/lib/sink/page_cursor_shared_ref.rb +72 -0
- data/lib/sink/page_cursor_top_level_array.rb +51 -0
- data/lib/sink/page_cursor_url.rb +51 -0
- data/lib/sink/page_cursor_with_reverse.rb +58 -0
- data/lib/sink/page_offset.rb +54 -0
- data/lib/sink/page_offset_no_start_field.rb +50 -0
- data/lib/sink/page_offset_total_count.rb +58 -0
- data/lib/sink/page_page_number.rb +58 -0
- data/lib/sink/page_page_number_without_current_page_response.rb +50 -0
- data/lib/sink/pooled_net_requester.rb +72 -0
- data/lib/sink/request_options.rb +89 -0
- data/lib/sink/resources/body_params/objects.rb +34 -0
- data/lib/sink/resources/body_params/unions.rb +34 -0
- data/lib/sink/resources/body_params.rb +630 -0
- data/lib/sink/resources/cards.rb +508 -0
- data/lib/sink/resources/casing/eeoc.rb +34 -0
- data/lib/sink/resources/casing.rb +16 -0
- data/lib/sink/resources/clients.rb +30 -0
- data/lib/sink/resources/company/payments.rb +30 -0
- data/lib/sink/resources/company.rb +18 -0
- data/lib/sink/resources/complex_queries.rb +60 -0
- data/lib/sink/resources/config_tools/parent_with_skip_node_python/child_only_skip_python.rb +99 -0
- data/lib/sink/resources/config_tools/parent_with_skip_node_python.rb +101 -0
- data/lib/sink/resources/config_tools.rb +40 -0
- data/lib/sink/resources/decorator_tests/keep_this_resource.rb +29 -0
- data/lib/sink/resources/decorator_tests/languages.rb +59 -0
- data/lib/sink/resources/decorator_tests/skip_this_resource.rb +28 -0
- data/lib/sink/resources/decorator_tests.rb +38 -0
- data/lib/sink/resources/deeply_nested/level_one/level_two/level_three.rb +33 -0
- data/lib/sink/resources/deeply_nested/level_one/level_two.rb +35 -0
- data/lib/sink/resources/deeply_nested/level_one.rb +33 -0
- data/lib/sink/resources/deeply_nested.rb +16 -0
- data/lib/sink/resources/docstrings.rb +82 -0
- data/lib/sink/resources/empty_body.rb +60 -0
- data/lib/sink/resources/envelopes.rb +74 -0
- data/lib/sink/resources/files.rb +101 -0
- data/lib/sink/resources/header_params.rb +81 -0
- data/lib/sink/resources/invalid_schemas/arrays.rb +26 -0
- data/lib/sink/resources/invalid_schemas/objects.rb +26 -0
- data/lib/sink/resources/invalid_schemas.rb +20 -0
- data/lib/sink/resources/make_ambiguous_schemas_explicit.rb +26 -0
- data/lib/sink/resources/make_ambiguous_schemas_looser.rb +26 -0
- data/lib/sink/resources/method_config.rb +167 -0
- data/lib/sink/resources/mixed_params/duplicates.rb +79 -0
- data/lib/sink/resources/mixed_params.rb +88 -0
- data/lib/sink/resources/model_referenced_in_parent_and_child/child.rb +26 -0
- data/lib/sink/resources/model_referenced_in_parent_and_child.rb +28 -0
- data/lib/sink/resources/names/can_cause_clashes/employment_data.rb +16 -0
- data/lib/sink/resources/names/can_cause_clashes/response.rb +32 -0
- data/lib/sink/resources/names/can_cause_clashes.rb +23 -0
- data/lib/sink/resources/names/documents.rb +28 -0
- data/lib/sink/resources/names/openapi_specials.rb +26 -0
- data/lib/sink/resources/names/params.rb +52 -0
- data/lib/sink/resources/names/reserved_names/import.rb +31 -0
- data/lib/sink/resources/names/reserved_names/methods.rb +40 -0
- data/lib/sink/resources/names/reserved_names/public/class_.rb +30 -0
- data/lib/sink/resources/names/reserved_names/public/interface.rb +30 -0
- data/lib/sink/resources/names/reserved_names/public/private.rb +30 -0
- data/lib/sink/resources/names/reserved_names/public.rb +40 -0
- data/lib/sink/resources/names/reserved_names.rb +46 -0
- data/lib/sink/resources/names.rb +170 -0
- data/lib/sink/resources/openapi_formats.rb +53 -0
- data/lib/sink/resources/pagination_tests/cursor.rb +57 -0
- data/lib/sink/resources/pagination_tests/cursor_id.rb +34 -0
- data/lib/sink/resources/pagination_tests/cursor_url.rb +34 -0
- data/lib/sink/resources/pagination_tests/fake_pages.rb +33 -0
- data/lib/sink/resources/pagination_tests/items_types.rb +34 -0
- data/lib/sink/resources/pagination_tests/nested_items.rb +34 -0
- data/lib/sink/resources/pagination_tests/offset.rb +74 -0
- data/lib/sink/resources/pagination_tests/page_number.rb +54 -0
- data/lib/sink/resources/pagination_tests/page_number_without_current_page_response.rb +55 -0
- data/lib/sink/resources/pagination_tests/refs.rb +55 -0
- data/lib/sink/resources/pagination_tests/response_headers.rb +34 -0
- data/lib/sink/resources/pagination_tests/schema_types.rb +54 -0
- data/lib/sink/resources/pagination_tests/top_level_arrays.rb +34 -0
- data/lib/sink/resources/pagination_tests.rb +64 -0
- data/lib/sink/resources/parent/child.rb +30 -0
- data/lib/sink/resources/parent.rb +18 -0
- data/lib/sink/resources/path_params.rb +210 -0
- data/lib/sink/resources/positional_params.rb +237 -0
- data/lib/sink/resources/query_params.rb +166 -0
- data/lib/sink/resources/recursion/shared_responses.rb +26 -0
- data/lib/sink/resources/recursion.rb +68 -0
- data/lib/sink/resources/resource_refs/paginated_model_first_ref.rb +14 -0
- data/lib/sink/resources/resource_refs/paginated_model_second_ref.rb +14 -0
- data/lib/sink/resources/resource_refs/parent/child.rb +31 -0
- data/lib/sink/resources/resource_refs/parent.rb +32 -0
- data/lib/sink/resources/resource_refs.rb +24 -0
- data/lib/sink/resources/resources.rb +26 -0
- data/lib/sink/resources/responses/union_types.rb +98 -0
- data/lib/sink/resources/responses.rb +319 -0
- data/lib/sink/resources/shared_query_params.rb +48 -0
- data/lib/sink/resources/streaming.rb +106 -0
- data/lib/sink/resources/testing.rb +24 -0
- data/lib/sink/resources/tests.rb +26 -0
- data/lib/sink/resources/tools.rb +33 -0
- data/lib/sink/resources/types/allofs.rb +14 -0
- data/lib/sink/resources/types/arrays.rb +62 -0
- data/lib/sink/resources/types/enums.rb +95 -0
- data/lib/sink/resources/types/maps.rb +29 -0
- data/lib/sink/resources/types/objects.rb +105 -0
- data/lib/sink/resources/types/primitives.rb +34 -0
- data/lib/sink/resources/types/read_only_params.rb +34 -0
- data/lib/sink/resources/types/unions.rb +126 -0
- data/lib/sink/resources/types/write_only_responses.rb +28 -0
- data/lib/sink/resources/types.rb +95 -0
- data/lib/sink/resources/undocumented_resource.rb +47 -0
- data/lib/sink/resources/version_1_30_names.rb +41 -0
- data/lib/sink/resources/widgets.rb +30 -0
- data/lib/sink/util.rb +78 -0
- data/lib/sink/version.rb +5 -0
- data/lib/sink.rb +276 -0
- metadata +322 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b0b3135f8e32ef8791093777d9a9bd263cc2ebeae00c1720ede5f137b49619e1
|
4
|
+
data.tar.gz: d0e5de4d073038141ab8c9340ff81c582b021a645dbe5a544145d5d08e2c88a6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ab5b68f79778de0967482cad9b1025353f9dac4fa0f750b5355c90296249b3eaad465c3994eae926a2a8e4750b0551ec496954ddd855ebe6482fee9f18317c0b
|
7
|
+
data.tar.gz: 12865e7a6723f2df54e89c011e2b5cd35a671939795d5b39d91d4e8b63d3e33b43d2c31b3bedaf6464e63e0cc42807c604a4072697870519d3a70b560a804312
|
data/README.md
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
# Sink Ruby API library
|
2
|
+
|
3
|
+
The Sink Ruby library provides convenient access to the Sink REST API from any Ruby 3.0+
|
4
|
+
application.
|
5
|
+
|
6
|
+
It is generated with [Stainless](https://www.stainlessapi.com/).
|
7
|
+
|
8
|
+
## Documentation
|
9
|
+
|
10
|
+
Documentation for the most recent version of this gem can be found [on RubyDoc](https://rubydoc.info/github/stainless-sdks/sink-ruby-public).
|
11
|
+
|
12
|
+
The underlying REST API documentation can be found on [stainlessapi.com](https://stainlessapi.com).
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
To use this gem during the beta, install directly from GitHub with Bundler by
|
17
|
+
adding the following to your application's `Gemfile`:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem "sink", git: "https://github.com/stainless-sdks/sink-ruby-public", branch: "main"
|
21
|
+
```
|
22
|
+
|
23
|
+
To fetch an initial copy of the gem:
|
24
|
+
|
25
|
+
```sh
|
26
|
+
bundle install
|
27
|
+
```
|
28
|
+
|
29
|
+
To update the version used by your application when updates are pushed to
|
30
|
+
GitHub:
|
31
|
+
|
32
|
+
```sh
|
33
|
+
bundle update sink
|
34
|
+
```
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require "sink"
|
40
|
+
|
41
|
+
sink = Sink::Client.new(
|
42
|
+
user_token: "My User Token", # defaults to ENV["SINK_CUSTOM_API_KEY_ENV"]
|
43
|
+
environment: "sandbox", # defaults to "production"
|
44
|
+
username: "Robert",
|
45
|
+
some_number_arg_required_no_default: 0,
|
46
|
+
some_number_arg_required_no_default_no_env: 0,
|
47
|
+
required_arg_no_env: "<example>"
|
48
|
+
)
|
49
|
+
|
50
|
+
card = sink.cards.create(
|
51
|
+
type: "SINGLE_USE",
|
52
|
+
exp_month: "08",
|
53
|
+
not_: "TEST",
|
54
|
+
shipping_address: {
|
55
|
+
"address1" => "180 Varick St",
|
56
|
+
"city" => "New York",
|
57
|
+
"country" => "USA",
|
58
|
+
"first_name" => "Jason",
|
59
|
+
"last_name" => "Mimosa",
|
60
|
+
"state" => "NY",
|
61
|
+
"postal_code" => "H0H0H0"
|
62
|
+
}
|
63
|
+
)
|
64
|
+
|
65
|
+
puts(card.token)
|
66
|
+
```
|
67
|
+
|
68
|
+
### Errors
|
69
|
+
|
70
|
+
When the library is unable to connect to the API, or if the API returns a
|
71
|
+
non-success status code (i.e., 4xx or 5xx response), a subclass of
|
72
|
+
`Sink::HTTP::Error` will be thrown:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
begin
|
76
|
+
sink.cards.create(type: "an_incorrect_type")
|
77
|
+
rescue Sink::HTTP::Error => e
|
78
|
+
puts(e.code) # 400
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
Error codes are as followed:
|
83
|
+
|
84
|
+
| Cause | Error Type |
|
85
|
+
| ---------------- | -------------------------- |
|
86
|
+
| HTTP 400 | `BadRequestError` |
|
87
|
+
| HTTP 401 | `AuthenticationError` |
|
88
|
+
| HTTP 403 | `PermissionDeniedError` |
|
89
|
+
| HTTP 404 | `NotFoundError` |
|
90
|
+
| HTTP 409 | `ConflictError` |
|
91
|
+
| HTTP 422 | `UnprocessableEntityError` |
|
92
|
+
| HTTP 429 | `RateLimitError` |
|
93
|
+
| HTTP >=500 | `InternalServerError` |
|
94
|
+
| Other HTTP error | `APIStatusError` |
|
95
|
+
| Timeout | `APITimeoutError` |
|
96
|
+
| Network error | `APIConnectionError` |
|
97
|
+
|
98
|
+
### Retries
|
99
|
+
|
100
|
+
Certain errors will be automatically retried 1 times by default, with a short
|
101
|
+
exponential backoff. Connection errors (for example, due to a network
|
102
|
+
connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, >=500 Internal errors,
|
103
|
+
and timeouts will all be retried by default.
|
104
|
+
|
105
|
+
You can use the `max_retries` option to configure or disable this:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
# Configure the default for all requests:
|
109
|
+
sink = Sink::Client.new(
|
110
|
+
max_retries: 0, # default is 1
|
111
|
+
username: "Robert",
|
112
|
+
some_number_arg_required_no_default: 0,
|
113
|
+
some_number_arg_required_no_default_no_env: 0,
|
114
|
+
required_arg_no_env: "<example>"
|
115
|
+
)
|
116
|
+
|
117
|
+
# Or, configure per-request:
|
118
|
+
sink.cards.provision_foo("my card token", digital_wallet: "GOOGLE_PAY", max_retries: 5)
|
119
|
+
```
|
120
|
+
|
121
|
+
### Timeouts
|
122
|
+
|
123
|
+
By default, requests will time out after 60 seconds.
|
124
|
+
Timeouts are applied separately to the initial connection and the overall request time,
|
125
|
+
so in some cases a request could wait 2\*timeout seconds before it fails.
|
126
|
+
|
127
|
+
You can use the `timeout` option to configure or disable this:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
# Configure the default for all requests:
|
131
|
+
sink = Sink::Client.new(
|
132
|
+
timeout: nil, # default is 60
|
133
|
+
username: "Robert",
|
134
|
+
some_number_arg_required_no_default: 0,
|
135
|
+
some_number_arg_required_no_default_no_env: 0,
|
136
|
+
required_arg_no_env: "<example>"
|
137
|
+
)
|
138
|
+
|
139
|
+
# Or, configure per-request:
|
140
|
+
sink.cards.create(type: "DIGITAL", timeout: 5)
|
141
|
+
```
|
142
|
+
|
143
|
+
## Versioning
|
144
|
+
|
145
|
+
This package follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions. As the
|
146
|
+
library is in initial development and has a major version of `0`, APIs may change
|
147
|
+
at any time.
|
148
|
+
|
149
|
+
## Requirements
|
150
|
+
|
151
|
+
Ruby 3.0 or higher.
|
@@ -0,0 +1,404 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sink
|
4
|
+
# @!visibility private
|
5
|
+
class BaseClient
|
6
|
+
attr_accessor :requester
|
7
|
+
|
8
|
+
# @param base_url [String]
|
9
|
+
# @param timeout [Integer, nil]
|
10
|
+
# @param headers [Hash{String => String}]
|
11
|
+
# @param max_retries [Integer]
|
12
|
+
# @param idempotency_header [String, nil]
|
13
|
+
def initialize(
|
14
|
+
base_url:,
|
15
|
+
timeout: nil,
|
16
|
+
headers: {},
|
17
|
+
max_retries: 0,
|
18
|
+
idempotency_header: nil
|
19
|
+
)
|
20
|
+
self.requester = PooledNetRequester.new
|
21
|
+
base_url_parsed = URI.parse(base_url)
|
22
|
+
@headers = Util.normalized_headers(
|
23
|
+
{
|
24
|
+
"X-Stainless-Lang" => "ruby",
|
25
|
+
"X-Stainless-Package-Version" => Sink::VERSION,
|
26
|
+
"X-Stainless-Runtime" => RUBY_ENGINE,
|
27
|
+
"X-Stainless-Runtime-Version" => RUBY_ENGINE_VERSION,
|
28
|
+
"Accept" => "application/json"
|
29
|
+
},
|
30
|
+
headers
|
31
|
+
)
|
32
|
+
@host = base_url_parsed.host
|
33
|
+
@scheme = base_url_parsed.scheme
|
34
|
+
@port = base_url_parsed.port
|
35
|
+
@base_path = self.class.normalize_path(base_url_parsed.path)
|
36
|
+
@max_retries = max_retries
|
37
|
+
@timeout = timeout
|
38
|
+
@idempotency_header = idempotency_header
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Hash{String => String}]
|
42
|
+
def auth_headers
|
43
|
+
{}
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_request(req, opts)
|
47
|
+
if (body = req[:body])
|
48
|
+
# Body can be at least a Hash or Array, just check for Hash shape for now.
|
49
|
+
if body.is_a?(Hash)
|
50
|
+
body.each_key do |k|
|
51
|
+
unless k.is_a?(Symbol)
|
52
|
+
raise ArgumentError, "Request body keys must be Symbols, got #{k.inspect}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
unless opts.is_a?(Hash) || opts.is_a?(Sink::RequestOption)
|
59
|
+
raise ArgumentError, "Request `opts` must be a Hash or RequestOptions, got #{opts.inspect}"
|
60
|
+
end
|
61
|
+
opts.to_h.each_key do |k|
|
62
|
+
unless k.is_a?(Symbol)
|
63
|
+
raise ArgumentError, "Request `opts` keys must be Symbols, got #{k.inspect}"
|
64
|
+
end
|
65
|
+
unless (valid_keys = Sink::RequestOptions.options).include?(k)
|
66
|
+
raise ArgumentError, "Request `opts` keys must be one of #{valid_keys}, got #{k.inspect}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.normalize_path(path)
|
72
|
+
path.gsub(/\/+/, "/")
|
73
|
+
end
|
74
|
+
|
75
|
+
def resolve_uri_elements(req)
|
76
|
+
from_args =
|
77
|
+
if req[:url]
|
78
|
+
uri = req[:url].is_a?(URI::Generic) ? req[:url] : URI.parse(req[:url])
|
79
|
+
{
|
80
|
+
host: uri.host,
|
81
|
+
scheme: uri.scheme,
|
82
|
+
path: uri.path,
|
83
|
+
query: CGI.parse(uri.query || ""),
|
84
|
+
port: uri.port
|
85
|
+
}
|
86
|
+
else
|
87
|
+
from_req = req.slice(:host, :scheme, :path, :port, :query)
|
88
|
+
from_req[:path] = self.class.normalize_path("/#{@base_path}/#{from_req[:path]}")
|
89
|
+
from_req
|
90
|
+
end
|
91
|
+
|
92
|
+
uri_components = {host: @host, scheme: @scheme, port: @port}.merge(from_args)
|
93
|
+
|
94
|
+
if req[:extra_query]
|
95
|
+
uri_components[:query] = Util.deep_merge(uri_components[:query], req[:extra_query], concat: true)
|
96
|
+
end
|
97
|
+
|
98
|
+
uri_components
|
99
|
+
end
|
100
|
+
|
101
|
+
def prep_request(options)
|
102
|
+
method = options.fetch(:method)
|
103
|
+
|
104
|
+
headers = Util.normalized_headers(@headers, auth_headers, options[:headers], options[:extra_headers])
|
105
|
+
if @idempotency_header && !headers[@idempotency_header] && ![:get, :head, :options].include?(method)
|
106
|
+
headers[@idempotency_header.to_s.downcase] = options[:idempotency_key] || generate_idempotency_key
|
107
|
+
end
|
108
|
+
if !headers.key?("x-stainless-retry-count")
|
109
|
+
headers["x-stainless-retry-count"] = "0"
|
110
|
+
end
|
111
|
+
headers.compact!
|
112
|
+
headers.transform_values!(&:to_s)
|
113
|
+
|
114
|
+
body =
|
115
|
+
case method
|
116
|
+
when :post, :put, :patch, :delete
|
117
|
+
body = options[:body]
|
118
|
+
if body
|
119
|
+
if headers["content-type"] == "application/json"
|
120
|
+
JSON.dump(body)
|
121
|
+
else
|
122
|
+
body
|
123
|
+
end
|
124
|
+
end
|
125
|
+
else
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
if options[:extra_body]
|
129
|
+
body = Util.deep_merge(body, options[:extra_body])
|
130
|
+
end
|
131
|
+
|
132
|
+
url_elements = resolve_uri_elements(options)
|
133
|
+
|
134
|
+
{method: method, headers: headers, body: body}.merge(url_elements)
|
135
|
+
end
|
136
|
+
|
137
|
+
def generate_idempotency_key
|
138
|
+
"stainless-ruby-retry-#{SecureRandom.uuid}"
|
139
|
+
end
|
140
|
+
|
141
|
+
def should_retry?(response)
|
142
|
+
should_retry_header = response["x-should-retry"]
|
143
|
+
|
144
|
+
case should_retry_header
|
145
|
+
when "true"
|
146
|
+
true
|
147
|
+
when "false"
|
148
|
+
false
|
149
|
+
else
|
150
|
+
response_code = response.code.to_i
|
151
|
+
# retry on:
|
152
|
+
# 408: timeouts
|
153
|
+
# 409: locks
|
154
|
+
# 429: rate limits
|
155
|
+
# 500+: unknown errors
|
156
|
+
[408, 409, 429].include?(response_code) || response_code >= 500
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def make_status_error(message:, body:, response:)
|
161
|
+
raise NotImplementedError
|
162
|
+
end
|
163
|
+
|
164
|
+
def make_status_error_from_response(response)
|
165
|
+
err_body =
|
166
|
+
begin
|
167
|
+
JSON.parse(response.body)
|
168
|
+
rescue StandardError
|
169
|
+
response
|
170
|
+
end
|
171
|
+
|
172
|
+
# We include the body in the error message as well as returning it
|
173
|
+
# since logging error messages is a common and quick way to assess what's
|
174
|
+
# wrong with a response.
|
175
|
+
message = "Error code: #{response.code}; Response: #{response.body}"
|
176
|
+
|
177
|
+
make_status_error(message: message, body: err_body, response: response)
|
178
|
+
end
|
179
|
+
|
180
|
+
def header_based_retry(response)
|
181
|
+
# Note the `retry-after-ms` header may not be standard, but is a good idea and we'd like proactive support for it.
|
182
|
+
retry_after_millis = Float(response["retry-after-ms"], exception: false)
|
183
|
+
if retry_after_millis
|
184
|
+
retry_after = retry_after_millis / 1000.0
|
185
|
+
elsif response["retry-after"]
|
186
|
+
retry_after = Float(response["retry-after"], exception: false)
|
187
|
+
if retry_after.nil?
|
188
|
+
begin
|
189
|
+
base = Time.now
|
190
|
+
if response["x-stainless-mock-sleep-base"]
|
191
|
+
base = Time.httpdate(response["x-stainless-mock-sleep-base"])
|
192
|
+
end
|
193
|
+
retry_after = Time.httpdate(response["retry-after"]) - base
|
194
|
+
rescue StandardError # rubocop:disable Lint/SuppressedException
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
retry_after
|
199
|
+
rescue StandardError # rubocop:disable Lint/SuppressedException
|
200
|
+
end
|
201
|
+
|
202
|
+
def send_request(request, max_retries:, timeout:, redirect_count:)
|
203
|
+
delay = 0.6
|
204
|
+
max_delay = 8.0
|
205
|
+
# Don't send the current retry count in the headers if the caller modified the header defaults.
|
206
|
+
should_send_retry_count = request[:headers]["x-stainless-retry-count"] == "0"
|
207
|
+
retries = 0
|
208
|
+
loop do # rubocop:disable Metrics/BlockLength
|
209
|
+
if should_send_retry_count
|
210
|
+
request[:headers]["x-stainless-retry-count"] = retries.to_s
|
211
|
+
end
|
212
|
+
|
213
|
+
begin
|
214
|
+
response = @requester.execute(request, timeout: timeout)
|
215
|
+
status = response.code.to_i
|
216
|
+
|
217
|
+
if status < 300
|
218
|
+
return response
|
219
|
+
elsif status < 400
|
220
|
+
begin
|
221
|
+
prev_uri = URI.parse(Util.uri_from_req(request, absolute: true))
|
222
|
+
location = URI.join(prev_uri, response["location"])
|
223
|
+
rescue ArgumentError
|
224
|
+
message = "server responded with status #{status} but no valid location header"
|
225
|
+
raise HTTP::APIConnectionError.new(message: message, request: request)
|
226
|
+
end
|
227
|
+
# from whatwg fetch spec
|
228
|
+
if redirect_count == 20
|
229
|
+
message = "failed to complete the request within 20 redirects"
|
230
|
+
raise HTTP::APIConnectionError.new(message: message, request: request)
|
231
|
+
end
|
232
|
+
if location.scheme != "http" && location.scheme != "https"
|
233
|
+
message = "tried to redirect to a non-http URL"
|
234
|
+
raise HTTP::APIConnectionError.new(message: message, request: request)
|
235
|
+
end
|
236
|
+
request = request.merge(resolve_uri_elements({url: location}))
|
237
|
+
# from whatwg fetch spec
|
238
|
+
if ([301, 302].include?(status) && request[:method] == :post) || (status == 303)
|
239
|
+
request[:method] = request[:method] == :head ? :head : :get
|
240
|
+
request[:body] = nil
|
241
|
+
request[:headers] = request[:headers].reject do |k|
|
242
|
+
%w[content-encoding content-language content-location content-type content-length].include?(k)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
# from undici
|
246
|
+
if Sink::Util.uri_origin(prev_uri) != Sink::Util.uri_origin(location)
|
247
|
+
request[:headers] = request[:headers].reject do |k|
|
248
|
+
%w[authorization cookie proxy-authorization host].include?(k)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
return send_request(
|
252
|
+
request,
|
253
|
+
max_retries: max_retries,
|
254
|
+
timeout: timeout,
|
255
|
+
redirect_count: redirect_count + 1
|
256
|
+
)
|
257
|
+
end
|
258
|
+
rescue Net::HTTPBadResponse
|
259
|
+
if retries >= max_retries
|
260
|
+
message = "failed to complete the request within #{max_retries} retries"
|
261
|
+
raise HTTP::APIConnectionError.new(message: message, request: request)
|
262
|
+
end
|
263
|
+
rescue Timeout::Error
|
264
|
+
if retries >= max_retries
|
265
|
+
message = "failed to complete the request within #{max_retries} retries"
|
266
|
+
raise HTTP::APITimeoutError.new(message: message, request: request)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
if (response && !should_retry?(response)) || retries >= max_retries
|
271
|
+
raise make_status_error_from_response(response)
|
272
|
+
end
|
273
|
+
|
274
|
+
retries += 1
|
275
|
+
base_delay = header_based_retry(response)
|
276
|
+
if base_delay
|
277
|
+
delay = base_delay
|
278
|
+
else
|
279
|
+
base_delay = (delay * (2**retries))
|
280
|
+
jitter_factor = 1 - (0.25 * rand)
|
281
|
+
delay = (base_delay * jitter_factor).clamp(0, max_delay)
|
282
|
+
end
|
283
|
+
|
284
|
+
if response&.key?("x-stainless-mock-sleep")
|
285
|
+
request[:headers]["x-stainless-mock-slept"] = delay
|
286
|
+
else
|
287
|
+
sleep(delay)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# Execute the request specified by req + opts. This is the method that all
|
293
|
+
# resource methods call into.
|
294
|
+
# Params req & opts are kept separate up until this point so that we can
|
295
|
+
# validate opts as it was given to us by the user.
|
296
|
+
def request(req, opts)
|
297
|
+
validate_request(req, opts)
|
298
|
+
options = Util.deep_merge(req, opts)
|
299
|
+
request_args = prep_request(options)
|
300
|
+
response = send_request(
|
301
|
+
request_args,
|
302
|
+
max_retries: opts.fetch(:max_retries, @max_retries),
|
303
|
+
timeout: opts.fetch(:timeout, @timeout),
|
304
|
+
redirect_count: 0
|
305
|
+
)
|
306
|
+
raw_data =
|
307
|
+
case response.content_type
|
308
|
+
when "application/json"
|
309
|
+
begin
|
310
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
311
|
+
req[:unwrap] ? data[req[:unwrap]] : data
|
312
|
+
rescue JSON::ParserError
|
313
|
+
response.body
|
314
|
+
end
|
315
|
+
# TODO: parsing other response types
|
316
|
+
else
|
317
|
+
response.body
|
318
|
+
end
|
319
|
+
|
320
|
+
if (page = req[:page])
|
321
|
+
model = req.fetch(:model)
|
322
|
+
page.new(model, raw_data, response, self, req, opts)
|
323
|
+
elsif (model = req[:model])
|
324
|
+
Converter.convert(model, raw_data)
|
325
|
+
else
|
326
|
+
raw_data
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# @return [String]
|
331
|
+
def inspect
|
332
|
+
base_url = Util.uri_from_req(
|
333
|
+
{host: @host, scheme: @scheme, port: @port, path: @base_path},
|
334
|
+
absolute: true
|
335
|
+
)
|
336
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)} base_url=#{base_url.inspect} max_retries=#{@max_retries.inspect} timeout=#{@timeout.inspect}>"
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
class Error < StandardError
|
341
|
+
end
|
342
|
+
|
343
|
+
module HTTP
|
344
|
+
class Error < Sink::Error
|
345
|
+
end
|
346
|
+
|
347
|
+
class ResponseError < Error
|
348
|
+
attr_reader :response, :body
|
349
|
+
|
350
|
+
# @!attribute [r] code
|
351
|
+
# @return [Integer]
|
352
|
+
attr_reader :code
|
353
|
+
|
354
|
+
def initialize(message:, response:, body:)
|
355
|
+
super(message)
|
356
|
+
@response = response
|
357
|
+
@body = body
|
358
|
+
@code = response.code.to_i
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
class RequestError < Error
|
363
|
+
attr_reader :request
|
364
|
+
|
365
|
+
def initialize(message:, request:)
|
366
|
+
super(message)
|
367
|
+
@request = request
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
class BadRequestError < ResponseError
|
372
|
+
end
|
373
|
+
|
374
|
+
class AuthenticationError < ResponseError
|
375
|
+
end
|
376
|
+
|
377
|
+
class PermissionDeniedError < ResponseError
|
378
|
+
end
|
379
|
+
|
380
|
+
class NotFoundError < ResponseError
|
381
|
+
end
|
382
|
+
|
383
|
+
class ConflictError < ResponseError
|
384
|
+
end
|
385
|
+
|
386
|
+
class UnprocessableEntityError < ResponseError
|
387
|
+
end
|
388
|
+
|
389
|
+
class RateLimitError < ResponseError
|
390
|
+
end
|
391
|
+
|
392
|
+
class InternalServerError < ResponseError
|
393
|
+
end
|
394
|
+
|
395
|
+
class APIStatusError < ResponseError
|
396
|
+
end
|
397
|
+
|
398
|
+
class APIConnectionError < RequestError
|
399
|
+
end
|
400
|
+
|
401
|
+
class APITimeoutError < RequestError
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|