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