plainrouter-sdk 0.5.0 → 0.6.1
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 +4 -4
- data/README.md +7 -0
- data/lib/plainrouter/client.rb +343 -3
- data/lib/plainrouter/closed_input_objects.json +3896 -0
- data/lib/plainrouter/closed_input_validator.rb +246 -0
- data/lib/plainrouter/integer_input_fields.json +579 -0
- data/lib/plainrouter/openapi/api/action_read_api_api.rb +71 -31
- data/lib/plainrouter/openapi/api/action_rule_api_api.rb +350 -0
- data/lib/plainrouter/openapi/api/ad_test_api_api.rb +13 -9
- data/lib/plainrouter/openapi/api/agent_context_api.rb +93 -0
- data/lib/plainrouter/openapi/api/creative_api.rb +241 -0
- data/lib/plainrouter/openapi/api/deployment_plan_api.rb +257 -2
- data/lib/plainrouter/openapi/api/draft_batch_file_import_api.rb +142 -0
- data/lib/plainrouter/openapi/api/launch_policy_api.rb +242 -0
- data/lib/plainrouter/openapi/api/meta_inventory_exposure_api.rb +133 -0
- data/lib/plainrouter/openapi/api_client.rb +4 -0
- data/lib/plainrouter/openapi/api_model_base.rb +2 -0
- data/lib/plainrouter/openapi/configuration.rb +34 -0
- data/lib/plainrouter/openapi/models/action_batch_read_data.rb +42 -4
- data/lib/plainrouter/openapi/models/action_dry_run_read_dry_run.rb +42 -4
- data/lib/plainrouter/openapi/models/action_list_read_meta.rb +33 -4
- data/lib/plainrouter/openapi/models/action_proposal_input.rb +72 -1
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner.rb +1 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of1.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of10.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of10_params.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of11.rb +3 -1
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of12.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of12_params.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of13.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of13_params.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of2.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of3.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of4.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of4_target_entity.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of5.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of6.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of7.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of8.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of9.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of_params.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of_target_entity.rb +2 -0
- data/lib/plainrouter/openapi/models/action_proposal_input_evidence_inner.rb +4 -2
- data/lib/plainrouter/openapi/models/action_proposal_read_proposal.rb +42 -4
- data/lib/plainrouter/openapi/models/action_read_item.rb +11 -1
- data/lib/plainrouter/openapi/models/action_read_item_counted_evidence.rb +319 -0
- data/lib/plainrouter/openapi/models/action_read_item_counted_evidence_counted.rb +174 -0
- data/lib/plainrouter/openapi/models/action_read_item_counted_evidence_coverage.rb +162 -0
- data/lib/plainrouter/openapi/models/action_read_item_counted_evidence_meta_reported.rb +198 -0
- data/lib/plainrouter/openapi/models/{action_proposal_input_actions_inner_any_of11_params.rb → action_read_item_counted_evidence_target.rb} +41 -24
- data/lib/plainrouter/openapi/models/action_read_item_counted_evidence_window.rb +225 -0
- data/lib/plainrouter/openapi/models/action_receipt_unavailable.rb +164 -0
- data/lib/plainrouter/openapi/models/action_receipt_unavailable_error.rb +214 -0
- data/lib/plainrouter/openapi/models/action_rule_detail_read.rb +164 -0
- data/lib/plainrouter/openapi/models/action_rule_detail_read_rule.rb +402 -0
- data/lib/plainrouter/openapi/models/action_rule_detail_read_rule_current_revision.rb +388 -0
- data/lib/plainrouter/openapi/models/action_rule_detail_read_rule_runs_inner.rb +396 -0
- data/lib/plainrouter/openapi/models/action_rule_detail_read_rule_runs_inner_decisions_inner.rb +382 -0
- data/lib/plainrouter/openapi/models/action_rule_detail_read_rule_window.rb +242 -0
- data/lib/plainrouter/openapi/models/action_rule_write_read.rb +164 -0
- data/lib/plainrouter/openapi/models/action_rule_write_read_rule.rb +373 -0
- data/lib/plainrouter/openapi/models/actions_api_decision_receipt404_response.rb +103 -0
- data/lib/plainrouter/openapi/models/actions_rules_index200_response.rb +164 -0
- data/lib/plainrouter/openapi/models/actions_rules_index200_response_rules.rb +196 -0
- data/lib/plainrouter/openapi/models/actions_rules_index200_response_rules_data_inner.rb +344 -0
- data/lib/plainrouter/openapi/models/actions_rules_index200_response_rules_data_inner_account.rb +157 -0
- data/lib/plainrouter/openapi/models/actions_rules_index200_response_rules_data_inner_counts.rb +156 -0
- data/lib/plainrouter/openapi/models/actions_rules_index403_response.rb +103 -0
- data/lib/plainrouter/openapi/models/actions_rules_index403_response_any_of.rb +214 -0
- data/lib/plainrouter/openapi/models/actions_rules_store_request.rb +317 -0
- data/lib/plainrouter/openapi/models/actions_rules_store_request_definition.rb +105 -0
- data/lib/plainrouter/openapi/models/actions_rules_store_request_definition_any_of.rb +317 -0
- data/lib/plainrouter/openapi/models/actions_rules_store_request_definition_any_of1.rb +361 -0
- data/lib/plainrouter/openapi/models/actions_rules_store_request_definition_any_of2.rb +361 -0
- data/lib/plainrouter/openapi/models/actions_rules_store_request_definition_any_of_cost_ratio.rb +104 -0
- data/lib/plainrouter/openapi/models/actions_rules_store_request_window.rb +103 -0
- data/lib/plainrouter/openapi/models/actions_rules_update_request.rb +267 -0
- data/lib/plainrouter/openapi/models/actions_rules_update_request_definition.rb +105 -0
- data/lib/plainrouter/openapi/models/actions_rules_update_request_definition_any_of.rb +317 -0
- data/lib/plainrouter/openapi/models/actions_rules_update_request_definition_any_of1.rb +361 -0
- data/lib/plainrouter/openapi/models/actions_rules_update_request_definition_any_of2.rb +361 -0
- data/lib/plainrouter/openapi/models/ad_test_list_read_tests.rb +1 -1
- data/lib/plainrouter/openapi/models/agent_context200_response.rb +214 -0
- data/lib/plainrouter/openapi/models/agent_context200_response_agent.rb +268 -0
- data/lib/plainrouter/openapi/models/agent_context200_response_platform_ad_account.rb +292 -0
- data/lib/plainrouter/openapi/models/agent_context200_response_platform_ad_account_workspace.rb +190 -0
- data/lib/plainrouter/openapi/models/agent_context401_response.rb +103 -0
- data/lib/plainrouter/openapi/models/agent_context_auth_error.rb +190 -0
- data/lib/plainrouter/openapi/models/creative_intake_read.rb +1 -1
- data/lib/plainrouter/openapi/models/creative_mcp_grant_operation.rb +44 -0
- data/lib/plainrouter/openapi/models/creative_mcp_preview.rb +283 -0
- data/lib/plainrouter/openapi/models/creative_mcp_refusal.rb +45 -0
- data/lib/plainrouter/openapi/models/creative_mcp_tool.rb +44 -0
- data/lib/plainrouter/openapi/models/delivery_status.rb +2 -1
- data/lib/plainrouter/openapi/models/deployment_plan_read.rb +42 -4
- data/lib/plainrouter/openapi/models/destination.rb +73 -9
- data/lib/plainrouter/openapi/models/destination_config.rb +103 -0
- data/lib/plainrouter/openapi/models/destination_status.rb +2 -1
- data/lib/plainrouter/openapi/models/draft_batch_mode.rb +41 -0
- data/lib/plainrouter/openapi/models/get_account_inventory200_response.rb +341 -0
- data/lib/plainrouter/openapi/models/get_account_inventory200_response_summary.rb +158 -0
- data/lib/plainrouter/openapi/models/get_account_inventory404_response.rb +103 -0
- data/lib/plainrouter/openapi/models/get_account_inventory404_response_any_of.rb +188 -0
- data/lib/plainrouter/openapi/models/get_event200_response_deliveries_inner.rb +25 -1
- data/lib/plainrouter/openapi/models/get_event200_response_event_deliveries_inner.rb +25 -1
- data/lib/plainrouter/openapi/models/get_inventory_metrics200_response.rb +26 -5
- data/lib/plainrouter/openapi/models/get_inventory_metrics200_response_pagination_value.rb +202 -0
- data/lib/plainrouter/openapi/models/launch_creatives_index200_response.rb +164 -0
- data/lib/plainrouter/openapi/models/launch_creatives_index200_response_creatives.rb +196 -0
- data/lib/plainrouter/openapi/models/{creative_intake_read_creative.rb → launch_creatives_index200_response_creatives_data_inner.rb} +4 -4
- data/lib/plainrouter/openapi/models/{creative_intake_read_creative_tags.rb → launch_creatives_index200_response_creatives_data_inner_tags.rb} +1 -1
- data/lib/plainrouter/openapi/models/launch_creatives_status200_response.rb +166 -0
- data/lib/plainrouter/openapi/models/launch_plans_copy422_response.rb +103 -0
- data/lib/plainrouter/openapi/models/launch_plans_index200_response_plans.rb +107 -4
- data/lib/plainrouter/openapi/models/launch_plans_index200_response_plans_next_page_arguments.rb +104 -0
- data/lib/plainrouter/openapi/models/launch_plans_index200_response_plans_next_page_arguments_any_of.rb +190 -0
- data/lib/plainrouter/openapi/models/launch_plans_index200_response_plans_next_page_arguments_any_of1.rb +190 -0
- data/lib/plainrouter/openapi/models/launch_plans_store201_response.rb +166 -0
- data/lib/plainrouter/openapi/models/launch_plans_validate422_response.rb +103 -0
- data/lib/plainrouter/openapi/models/launch_policies_index200_response.rb +164 -0
- data/lib/plainrouter/openapi/models/launch_policies_index200_response_policies.rb +453 -0
- data/lib/plainrouter/openapi/models/{list_events200_response_events_links_inner.rb → launch_policies_index200_response_policies_links_inner.rb} +3 -3
- data/lib/plainrouter/openapi/models/launch_policies_store201_response.rb +166 -0
- data/lib/plainrouter/openapi/models/launch_policy.rb +346 -0
- data/lib/plainrouter/openapi/models/launcher_file_import403_response.rb +216 -0
- data/lib/plainrouter/openapi/models/launcher_file_import422_response.rb +103 -0
- data/lib/plainrouter/openapi/models/launcher_file_import422_response_any_of.rb +242 -0
- data/lib/plainrouter/openapi/models/launcher_file_import422_response_any_of_syntactic_row_failures_inner.rb +190 -0
- data/lib/plainrouter/openapi/models/list_events200_response_events.rb +1 -1
- data/lib/plainrouter/openapi/models/plan_execute_rejected_error.rb +2 -2
- data/lib/plainrouter/openapi/models/plan_validate_rejected.rb +164 -0
- data/lib/plainrouter/openapi/models/plan_validate_rejected_error.rb +191 -0
- data/lib/plainrouter/openapi/models/proposal_unavailable.rb +164 -0
- data/lib/plainrouter/openapi/models/proposal_unavailable_error.rb +214 -0
- data/lib/plainrouter/openapi/models/set_destination_test_mode200_response_destination.rb +67 -1
- data/lib/plainrouter/openapi/models/{deployment_plan.rb → store_deployment_plan_request.rb} +185 -208
- data/lib/plainrouter/openapi/models/store_deployment_plan_request_ad_set_spec.rb +220 -0
- data/lib/plainrouter/openapi/models/store_deployment_plan_request_copy.rb +266 -0
- data/lib/plainrouter/openapi/models/store_deployment_plan_request_creative_overrides_inner.rb +217 -0
- data/lib/plainrouter/openapi/models/store_launch_policy_request.rb +260 -0
- data/lib/plainrouter/openapi/models/update_creative_status_request.rb +188 -0
- data/lib/plainrouter/openapi/models/update_deployment_plan_request.rb +511 -0
- data/lib/plainrouter/openapi/version.rb +1 -1
- data/lib/plainrouter/openapi.rb +88 -5
- data/lib/plainrouter/proposal_errors.rb +31 -0
- data/lib/plainrouter/version.rb +1 -1
- data/lib/plainrouter.rb +1 -0
- metadata +94 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a5520d3b56ecefc97c7aad741a1e8491e25c71f643cc7661a88360e659eba0e8
|
|
4
|
+
data.tar.gz: e3f6836fc2fb695602513a2cc77c1ea06c59325635a6976e96f8f3594336c43d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fc2ada06b63f54534980b71b367c77c96a9c3471c18391b6404d6aeed3b118234bd25d7ef23b12c65fdc0ea90336d19c621da2dc530faf6431aca87d625ccacb
|
|
7
|
+
data.tar.gz: 8d936747bea708f07a5472a5a228660271cf3f9ff1d6962e7eef444dbda4c5a3bcbe55de5b6af673b7e7e27f4b725724ba8fce899b8d3c9dfc050d33698a6ec0
|
data/README.md
CHANGED
|
@@ -45,6 +45,8 @@ example = sandbox.sandbox.get_sandbox
|
|
|
45
45
|
|
|
46
46
|
For response metadata, append `_with_http_info` to a generated operation. Models, API errors, configuration, and every generated operation are available in the deliberately separate `PlainRouter::OpenAPI` namespace.
|
|
47
47
|
|
|
48
|
+
`PlainRouter::Client` defaults to `Accept: application/json`; override with `accept: "..."`. API redirects raise `OpenAPI::ApiError` with `code` and `response_headers`, without following. Every generated operation is available through the curated groups: `actions`, `action_dry_run`, `action_reads`, `action_rules`, `ad_tests`, `agent_context`, `cookie_declarations`, `creatives`, `events`, `inventory`, `operations`, `plans`, and `sandbox`. The generated namespace remains available for direct access without this policy.
|
|
49
|
+
|
|
48
50
|
## Development
|
|
49
51
|
|
|
50
52
|
From `packages/ruby`:
|
|
@@ -64,3 +66,8 @@ scripts/check-ruby-generated.sh
|
|
|
64
66
|
Releases use `ruby-v<version>` tags so they remain separate from the npm and Python package tags in this repository. RubyGems publication is performed only by the trusted `publish-ruby.yml` workflow.
|
|
65
67
|
|
|
66
68
|
Licensed under Apache-2.0.
|
|
69
|
+
|
|
70
|
+
Per-call `header_params`, including `Accept`, remain supported and merge names
|
|
71
|
+
case-insensitively, e.g. `client.operations.list_events(header_params: { "accept" => "text/html" })`.
|
|
72
|
+
They override `accept:`. A caller Faraday connection's configured Accept is kept
|
|
73
|
+
when `accept:` is omitted; an exact `*/*` counts as unset at that boundary.
|
data/lib/plainrouter/client.rb
CHANGED
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
require "uri"
|
|
4
4
|
require "date"
|
|
5
|
+
require "json"
|
|
6
|
+
require "thread"
|
|
5
7
|
|
|
6
8
|
module PlainRouter
|
|
7
9
|
# Compact entry point for the Plainrouter API.
|
|
8
10
|
class Client
|
|
11
|
+
INTEGER_INPUT_FIELDS = JSON.parse(File.read(File.join(__dir__, "integer_input_fields.json")))
|
|
12
|
+
INTEGER_INPUT_GUARD_CACHE = {}
|
|
13
|
+
INTEGER_INPUT_GUARD_CACHE_LOCK = Mutex.new
|
|
14
|
+
private_constant :INTEGER_INPUT_FIELDS
|
|
15
|
+
private_constant :INTEGER_INPUT_GUARD_CACHE
|
|
16
|
+
private_constant :INTEGER_INPUT_GUARD_CACHE_LOCK
|
|
17
|
+
|
|
9
18
|
module EventsValidation
|
|
10
19
|
# Keep this literal identical to app/Domains/Signals/Data/ConsentDecision.php::CAPTURED_AT_PATTERN.
|
|
11
20
|
CAPTURED_AT_PATTERN = '/\A(?<datetime>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(?<fraction>\d{1,6}))?(?<offset>Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)\z/'
|
|
@@ -76,27 +85,358 @@ module PlainRouter
|
|
|
76
85
|
end
|
|
77
86
|
end
|
|
78
87
|
|
|
79
|
-
|
|
88
|
+
class RejectRedirects < Faraday::Middleware
|
|
89
|
+
def on_complete(env)
|
|
90
|
+
return unless (300...400).cover?(env.status)
|
|
91
|
+
|
|
92
|
+
raise OpenAPI::ApiError.new(code: env.status, response_headers: env.response_headers, response_body: env.body)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Distinguish generator defaults from caller values without changing operation signatures.
|
|
97
|
+
DefaultAccept = Class.new(String)
|
|
98
|
+
private_constant :DefaultAccept
|
|
99
|
+
|
|
100
|
+
class JsonApiClient < OpenAPI::ApiClient
|
|
101
|
+
def initialize(configuration, accept:)
|
|
102
|
+
super(configuration)
|
|
103
|
+
@accept = accept
|
|
104
|
+
@default_headers = Faraday::Utils::Headers.new(@default_headers)
|
|
105
|
+
@default_headers["Accept"] = accept || "application/json"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def select_header_accept(accepts)
|
|
109
|
+
DefaultAccept.new(super(accepts) || "application/json")
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def build_request(http_method, path, request, opts = {})
|
|
113
|
+
validate_integer_inputs(http_method, path, opts)
|
|
114
|
+
|
|
115
|
+
headers = Faraday::Utils::Headers.new
|
|
116
|
+
(opts[:header_params] || {}).each do |name, value|
|
|
117
|
+
next if name.casecmp?("Accept") && value.is_a?(DefaultAccept)
|
|
118
|
+
|
|
119
|
+
headers[name] = value
|
|
120
|
+
end
|
|
121
|
+
unless headers.key?("Accept")
|
|
122
|
+
caller_accept = request.headers["Accept"]
|
|
123
|
+
caller_accept = nil if caller_accept == "*/*"
|
|
124
|
+
headers["Accept"] = @accept || caller_accept || @default_headers["Accept"] || "application/json"
|
|
125
|
+
end
|
|
126
|
+
super(http_method, path, request, opts.merge(header_params: headers))
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def build_request_url(path, opts = {})
|
|
130
|
+
return super unless path == "/mcp/launcher/import"
|
|
131
|
+
|
|
132
|
+
@config.base_url(opts[:operation]).sub(%r{/+\z}, "").delete_suffix("/api/v1") + path
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
private
|
|
136
|
+
|
|
137
|
+
def validate_integer_inputs(http_method, path, opts)
|
|
138
|
+
method_path, metadata = integer_input_operation(http_method, path)
|
|
139
|
+
return unless metadata
|
|
140
|
+
|
|
141
|
+
request_bodies = [opts[:body], opts[:form_params]].map { |body| parsed_request_body(body) }
|
|
142
|
+
metadata.fetch("body").each do |segments|
|
|
143
|
+
request_bodies.each do |request_body|
|
|
144
|
+
field_path = boolean_integer_body_path(request_body, segments)
|
|
145
|
+
refuse_boolean_integer(field_path) if field_path
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
query_params = opts[:query_params] || {}
|
|
150
|
+
metadata.fetch("query").each do |name|
|
|
151
|
+
present, value = hash_value(query_params, name)
|
|
152
|
+
next unless present
|
|
153
|
+
|
|
154
|
+
field_path = boolean_integer_value_path(value, name)
|
|
155
|
+
refuse_boolean_integer(field_path) if field_path
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
integer_path_names = metadata.fetch("path")
|
|
159
|
+
return if integer_path_names.empty?
|
|
160
|
+
|
|
161
|
+
template = method_path.split(" ", 2).last
|
|
162
|
+
path_values = integer_path_values(template, path)
|
|
163
|
+
integer_path_names.each do |name|
|
|
164
|
+
value = path_values[name]
|
|
165
|
+
next unless value == "true" || value == "false"
|
|
166
|
+
|
|
167
|
+
refuse_boolean_integer(name)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def integer_input_operation(http_method, path)
|
|
172
|
+
method = http_method.to_s.upcase
|
|
173
|
+
path = path.to_s
|
|
174
|
+
INTEGER_INPUT_FIELDS.each do |method_path, metadata|
|
|
175
|
+
entry_method, template = method_path.split(" ", 2)
|
|
176
|
+
next unless entry_method == method
|
|
177
|
+
next unless path_matches_template?(template, path)
|
|
178
|
+
|
|
179
|
+
return [method_path, metadata]
|
|
180
|
+
end
|
|
181
|
+
[nil, nil]
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def path_matches_template?(template, path)
|
|
185
|
+
pattern = Regexp.escape(template).gsub(/\\\{[^}]+\\\}/, "[^/]+")
|
|
186
|
+
Regexp.new("\\A#{pattern}\\z").match?(path)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def integer_path_values(template, path)
|
|
190
|
+
template_parts = template.split("/")
|
|
191
|
+
path_parts = path.split("/")
|
|
192
|
+
return {} unless template_parts.length == path_parts.length
|
|
80
193
|
|
|
81
|
-
|
|
194
|
+
template_parts.zip(path_parts).each_with_object({}) do |(template_part, path_part), values|
|
|
195
|
+
match = /\A\{([^}]+)\}\z/.match(template_part)
|
|
196
|
+
values[match[1]] = URI::DEFAULT_PARSER.unescape(path_part) if match
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def parsed_request_body(body)
|
|
201
|
+
value = body.respond_to?(:to_hash) ? body.to_hash : body
|
|
202
|
+
return value unless value.is_a?(String)
|
|
203
|
+
|
|
204
|
+
JSON.parse(value)
|
|
205
|
+
rescue JSON::ParserError
|
|
206
|
+
nil
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def boolean_integer_body_path(value, segments, field_path = "")
|
|
210
|
+
return field_path if segments.empty? && boolean_value?(value)
|
|
211
|
+
return nil if segments.empty?
|
|
212
|
+
|
|
213
|
+
segment, *rest = segments
|
|
214
|
+
if segment == "*"
|
|
215
|
+
entries = if value.is_a?(Array)
|
|
216
|
+
value.each_with_index.map { |item, index| ["#{field_path}[#{index}]", item] }
|
|
217
|
+
elsif value.is_a?(Hash)
|
|
218
|
+
value.map do |key, item|
|
|
219
|
+
item_path = field_path.empty? ? key.to_s : "#{field_path}.#{key}"
|
|
220
|
+
[item_path, item]
|
|
221
|
+
end
|
|
222
|
+
else
|
|
223
|
+
return nil
|
|
224
|
+
end
|
|
225
|
+
entries.each do |item_path, item|
|
|
226
|
+
found = boolean_integer_body_path(item, rest, item_path)
|
|
227
|
+
return found if found
|
|
228
|
+
end
|
|
229
|
+
return nil
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
return nil unless value.is_a?(Hash)
|
|
233
|
+
|
|
234
|
+
present, child = hash_value(value, segment)
|
|
235
|
+
return nil unless present
|
|
236
|
+
|
|
237
|
+
child_path = field_path.empty? ? segment : "#{field_path}.#{segment}"
|
|
238
|
+
boolean_integer_body_path(child, rest, child_path)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def boolean_integer_value_path(value, field_path)
|
|
242
|
+
return field_path if boolean_value?(value)
|
|
243
|
+
|
|
244
|
+
if value.is_a?(Array)
|
|
245
|
+
value.each_with_index do |item, index|
|
|
246
|
+
found = boolean_integer_value_path(item, "#{field_path}[#{index}]")
|
|
247
|
+
return found if found
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
nil
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def hash_value(hash, name)
|
|
254
|
+
return [true, hash[name]] if hash.key?(name)
|
|
255
|
+
|
|
256
|
+
symbol = name.to_sym
|
|
257
|
+
return [true, hash[symbol]] if hash.key?(symbol)
|
|
258
|
+
|
|
259
|
+
[false, nil]
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def boolean_value?(value)
|
|
263
|
+
value.equal?(true) || value.equal?(false)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def refuse_boolean_integer(field_path)
|
|
267
|
+
raise ArgumentError,
|
|
268
|
+
"#{field_path} must not be a boolean; expected an integer or numeric string."
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def normalize_generated_operation_options(method_path, options)
|
|
272
|
+
return options unless options.is_a?(Hash)
|
|
273
|
+
|
|
274
|
+
metadata = INTEGER_INPUT_FIELDS.fetch(method_path)
|
|
275
|
+
option_fields = metadata.fetch("query") + metadata.fetch("body").filter_map do |segments|
|
|
276
|
+
segments.first if segments.length == 1
|
|
277
|
+
end
|
|
278
|
+
normalized = options.dup
|
|
279
|
+
option_fields.uniq.each do |name|
|
|
280
|
+
present, value = hash_value(normalized, name)
|
|
281
|
+
next unless present && value.is_a?(String) && /\A[+-]?\d+\z/.match?(value)
|
|
282
|
+
|
|
283
|
+
key = normalized.key?(name) ? name : name.to_sym
|
|
284
|
+
normalized[key] = value.to_i
|
|
285
|
+
end
|
|
286
|
+
normalized
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def validate_generated_operation_inputs(method_path, argument_names, argument_values)
|
|
290
|
+
metadata = INTEGER_INPUT_FIELDS.fetch(method_path)
|
|
291
|
+
arguments = argument_names.zip(argument_values).to_h
|
|
292
|
+
options = arguments["opts"] || arguments["options"] || {}
|
|
293
|
+
|
|
294
|
+
metadata.fetch("body").each do |segments|
|
|
295
|
+
body_candidates = arguments.filter_map do |name, value|
|
|
296
|
+
next if %w[opts options].include?(name)
|
|
297
|
+
next unless value.is_a?(Hash) || value.is_a?(Array) || value.respond_to?(:to_hash) || value.is_a?(String)
|
|
298
|
+
|
|
299
|
+
parsed_request_body(value)
|
|
300
|
+
end
|
|
301
|
+
body_candidates.concat(
|
|
302
|
+
[options, options["form_params"], options[:form_params], options["body"], options[:body], options["debug_body"], options[:debug_body]]
|
|
303
|
+
.map { |body| parsed_request_body(body) }
|
|
304
|
+
)
|
|
305
|
+
body_candidates.each do |request_body|
|
|
306
|
+
field_path = boolean_integer_body_path(request_body, segments)
|
|
307
|
+
refuse_boolean_integer(field_path) if field_path
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
metadata.fetch("query").each do |name|
|
|
312
|
+
present, value = hash_value(options, name)
|
|
313
|
+
unless present
|
|
314
|
+
query_params = options["query_params"] || options[:query_params] || {}
|
|
315
|
+
present, value = hash_value(query_params, name)
|
|
316
|
+
end
|
|
317
|
+
next unless present
|
|
318
|
+
|
|
319
|
+
field_path = boolean_integer_value_path(value, name)
|
|
320
|
+
refuse_boolean_integer(field_path) if field_path
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
metadata.fetch("path").each do |name|
|
|
324
|
+
value = arguments[name]
|
|
325
|
+
refuse_boolean_integer(name) if boolean_value?(value)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
attr_reader :actions, :action_dry_run, :action_reads, :action_rules, :agent_context,
|
|
331
|
+
:ad_tests, :cookie_declarations, :creatives, :events, :file_imports, :inventory,
|
|
332
|
+
:launch_policies, :operations, :plans, :sandbox
|
|
333
|
+
|
|
334
|
+
def initialize(token: nil, base_url: DEFAULT_BASE_URL, timeout: 30, user_agent: nil, accept: nil)
|
|
82
335
|
configuration = OpenAPI::Configuration.new
|
|
83
336
|
configure_base_url(configuration, base_url)
|
|
84
337
|
configuration.access_token = token
|
|
85
338
|
configuration.timeout = timeout
|
|
86
339
|
yield configuration if block_given?
|
|
340
|
+
configuration.configure_faraday_connection { |connection| connection.use RejectRedirects }
|
|
87
341
|
|
|
88
|
-
api_client =
|
|
342
|
+
api_client = JsonApiClient.new(configuration, accept: accept)
|
|
89
343
|
api_client.user_agent = user_agent || "plainrouter-ruby/#{VERSION}"
|
|
90
344
|
|
|
345
|
+
@actions = OpenAPI::ActionProposalApiApi.new(api_client)
|
|
346
|
+
@action_dry_run = OpenAPI::ActionDryRunApiApi.new(api_client)
|
|
347
|
+
@action_reads = OpenAPI::ActionReadApiApi.new(api_client)
|
|
348
|
+
@action_rules = OpenAPI::ActionRuleApiApi.new(api_client)
|
|
349
|
+
@agent_context = OpenAPI::AgentContextApi.new(api_client)
|
|
350
|
+
@cookie_declarations = OpenAPI::CookieDeclarationApi.new(api_client)
|
|
351
|
+
@creatives = OpenAPI::CreativeApi.new(api_client)
|
|
91
352
|
@events = OpenAPI::EventApi.new(api_client)
|
|
92
353
|
@events.singleton_class.prepend(EventsValidation)
|
|
354
|
+
@file_imports = OpenAPI::DraftBatchFileImportApi.new(api_client)
|
|
355
|
+
@inventory = OpenAPI::MetaInventoryExposureApi.new(api_client)
|
|
356
|
+
@launch_policies = OpenAPI::LaunchPolicyApi.new(api_client)
|
|
93
357
|
@operations = OpenAPI::OperationsApi.new(api_client)
|
|
94
358
|
@sandbox = OpenAPI::SandboxApi.new(api_client)
|
|
95
359
|
@plans = OpenAPI::DeploymentPlanApi.new(api_client)
|
|
360
|
+
@ad_tests = OpenAPI::AdTestApiApi.new(api_client)
|
|
361
|
+
|
|
362
|
+
[
|
|
363
|
+
@actions, @action_dry_run, @action_reads, @action_rules, @ad_tests, @agent_context,
|
|
364
|
+
@cookie_declarations, @creatives, @events, @file_imports, @inventory,
|
|
365
|
+
@launch_policies, @operations, @plans, @sandbox
|
|
366
|
+
].each { |api_group| install_integer_input_guards(api_group) }
|
|
96
367
|
end
|
|
97
368
|
|
|
98
369
|
private
|
|
99
370
|
|
|
371
|
+
def install_integer_input_guards(api_group)
|
|
372
|
+
return if api_group.instance_variable_defined?(:@plainrouter_integer_input_guards)
|
|
373
|
+
|
|
374
|
+
guard_module = INTEGER_INPUT_GUARD_CACHE_LOCK.synchronize do
|
|
375
|
+
INTEGER_INPUT_GUARD_CACHE[api_group.class] ||= build_integer_input_guard_module(api_group.class)
|
|
376
|
+
end
|
|
377
|
+
api_group.singleton_class.prepend(guard_module)
|
|
378
|
+
api_group.instance_variable_set(:@plainrouter_integer_input_guards, true)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def build_integer_input_guard_module(api_class)
|
|
382
|
+
operation_entries = INTEGER_INPUT_FIELDS.each_with_object({}) do |(method_path, metadata), entries|
|
|
383
|
+
entries[ruby_operation_name(metadata.fetch("operationId"))] = method_path
|
|
384
|
+
end
|
|
385
|
+
guard_module = Module.new
|
|
386
|
+
api_class.instance_methods(false).grep(/_with_http_info\z/).each do |http_info_method|
|
|
387
|
+
operation_method = http_info_method.to_s.delete_suffix("_with_http_info")
|
|
388
|
+
method_path = operation_entries[operation_method]
|
|
389
|
+
next unless method_path
|
|
390
|
+
|
|
391
|
+
[operation_method.to_sym, http_info_method].each do |method_name|
|
|
392
|
+
method = api_class.instance_method(method_name)
|
|
393
|
+
signature = ruby_method_signature(method)
|
|
394
|
+
argument_names = method.parameters.map { |_kind, name| name.to_s }
|
|
395
|
+
argument_values = argument_names.join(", ")
|
|
396
|
+
options_name = argument_names.find { |name| %w[opts options].include?(name) }
|
|
397
|
+
normalize_options = options_name ? "#{options_name} = @api_client.__send__(:normalize_generated_operation_options, #{method_path.inspect}, #{options_name})" : nil
|
|
398
|
+
source = <<~RUBY
|
|
399
|
+
def #{method_name}(#{signature})
|
|
400
|
+
#{normalize_options}
|
|
401
|
+
@api_client.__send__(:validate_generated_operation_inputs, #{method_path.inspect}, #{argument_names.inspect}, [#{argument_values}])
|
|
402
|
+
super
|
|
403
|
+
end
|
|
404
|
+
RUBY
|
|
405
|
+
guard_module.module_eval(source, __FILE__, __LINE__)
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
guard_module
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def ruby_method_signature(method)
|
|
412
|
+
source_path, line_number = method.source_location
|
|
413
|
+
definition = File.foreach(source_path).lazy.drop(line_number - 1).first
|
|
414
|
+
match = definition && /\A\s*def\s+#{Regexp.escape(method.name.to_s)}\((.*)\)\s*\z/.match(definition)
|
|
415
|
+
return match[1] if match
|
|
416
|
+
|
|
417
|
+
method.parameters.map do |kind, name|
|
|
418
|
+
case kind
|
|
419
|
+
when :req then name.to_s
|
|
420
|
+
when :opt then "#{name} = {}"
|
|
421
|
+
when :rest then "*#{name}"
|
|
422
|
+
when :keyreq then "#{name}:"
|
|
423
|
+
when :key then "#{name}: {}"
|
|
424
|
+
when :keyrest then "**#{name}"
|
|
425
|
+
when :block then "&#{name}"
|
|
426
|
+
else raise ArgumentError, "Unsupported generated parameter: #{kind}"
|
|
427
|
+
end
|
|
428
|
+
end.join(", ")
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def ruby_operation_name(operation_id)
|
|
432
|
+
operation_id
|
|
433
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, "\\1_\\2")
|
|
434
|
+
.gsub(/([a-z0-9])([A-Z])/, "\\1_\\2")
|
|
435
|
+
.gsub(/[^A-Za-z0-9]+/, "_")
|
|
436
|
+
.gsub(/\A_+|_+\z/, "")
|
|
437
|
+
.downcase
|
|
438
|
+
end
|
|
439
|
+
|
|
100
440
|
def configure_base_url(configuration, base_url)
|
|
101
441
|
uri = URI.parse(base_url)
|
|
102
442
|
unless uri.is_a?(URI::HTTP) && uri.host && !uri.query && !uri.fragment
|