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.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -0
  3. data/lib/plainrouter/client.rb +343 -3
  4. data/lib/plainrouter/closed_input_objects.json +3896 -0
  5. data/lib/plainrouter/closed_input_validator.rb +246 -0
  6. data/lib/plainrouter/integer_input_fields.json +579 -0
  7. data/lib/plainrouter/openapi/api/action_read_api_api.rb +71 -31
  8. data/lib/plainrouter/openapi/api/action_rule_api_api.rb +350 -0
  9. data/lib/plainrouter/openapi/api/ad_test_api_api.rb +13 -9
  10. data/lib/plainrouter/openapi/api/agent_context_api.rb +93 -0
  11. data/lib/plainrouter/openapi/api/creative_api.rb +241 -0
  12. data/lib/plainrouter/openapi/api/deployment_plan_api.rb +257 -2
  13. data/lib/plainrouter/openapi/api/draft_batch_file_import_api.rb +142 -0
  14. data/lib/plainrouter/openapi/api/launch_policy_api.rb +242 -0
  15. data/lib/plainrouter/openapi/api/meta_inventory_exposure_api.rb +133 -0
  16. data/lib/plainrouter/openapi/api_client.rb +4 -0
  17. data/lib/plainrouter/openapi/api_model_base.rb +2 -0
  18. data/lib/plainrouter/openapi/configuration.rb +34 -0
  19. data/lib/plainrouter/openapi/models/action_batch_read_data.rb +42 -4
  20. data/lib/plainrouter/openapi/models/action_dry_run_read_dry_run.rb +42 -4
  21. data/lib/plainrouter/openapi/models/action_list_read_meta.rb +33 -4
  22. data/lib/plainrouter/openapi/models/action_proposal_input.rb +72 -1
  23. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner.rb +1 -0
  24. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of.rb +2 -0
  25. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of1.rb +2 -0
  26. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of10.rb +2 -0
  27. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of10_params.rb +2 -0
  28. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of11.rb +3 -1
  29. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of12.rb +2 -0
  30. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of12_params.rb +2 -0
  31. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of13.rb +2 -0
  32. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of13_params.rb +2 -0
  33. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of2.rb +2 -0
  34. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of3.rb +2 -0
  35. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of4.rb +2 -0
  36. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of4_target_entity.rb +2 -0
  37. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of5.rb +2 -0
  38. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of6.rb +2 -0
  39. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of7.rb +2 -0
  40. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of8.rb +2 -0
  41. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of9.rb +2 -0
  42. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of_params.rb +2 -0
  43. data/lib/plainrouter/openapi/models/action_proposal_input_actions_inner_any_of_target_entity.rb +2 -0
  44. data/lib/plainrouter/openapi/models/action_proposal_input_evidence_inner.rb +4 -2
  45. data/lib/plainrouter/openapi/models/action_proposal_read_proposal.rb +42 -4
  46. data/lib/plainrouter/openapi/models/action_read_item.rb +11 -1
  47. data/lib/plainrouter/openapi/models/action_read_item_counted_evidence.rb +319 -0
  48. data/lib/plainrouter/openapi/models/action_read_item_counted_evidence_counted.rb +174 -0
  49. data/lib/plainrouter/openapi/models/action_read_item_counted_evidence_coverage.rb +162 -0
  50. data/lib/plainrouter/openapi/models/action_read_item_counted_evidence_meta_reported.rb +198 -0
  51. data/lib/plainrouter/openapi/models/{action_proposal_input_actions_inner_any_of11_params.rb → action_read_item_counted_evidence_target.rb} +41 -24
  52. data/lib/plainrouter/openapi/models/action_read_item_counted_evidence_window.rb +225 -0
  53. data/lib/plainrouter/openapi/models/action_receipt_unavailable.rb +164 -0
  54. data/lib/plainrouter/openapi/models/action_receipt_unavailable_error.rb +214 -0
  55. data/lib/plainrouter/openapi/models/action_rule_detail_read.rb +164 -0
  56. data/lib/plainrouter/openapi/models/action_rule_detail_read_rule.rb +402 -0
  57. data/lib/plainrouter/openapi/models/action_rule_detail_read_rule_current_revision.rb +388 -0
  58. data/lib/plainrouter/openapi/models/action_rule_detail_read_rule_runs_inner.rb +396 -0
  59. data/lib/plainrouter/openapi/models/action_rule_detail_read_rule_runs_inner_decisions_inner.rb +382 -0
  60. data/lib/plainrouter/openapi/models/action_rule_detail_read_rule_window.rb +242 -0
  61. data/lib/plainrouter/openapi/models/action_rule_write_read.rb +164 -0
  62. data/lib/plainrouter/openapi/models/action_rule_write_read_rule.rb +373 -0
  63. data/lib/plainrouter/openapi/models/actions_api_decision_receipt404_response.rb +103 -0
  64. data/lib/plainrouter/openapi/models/actions_rules_index200_response.rb +164 -0
  65. data/lib/plainrouter/openapi/models/actions_rules_index200_response_rules.rb +196 -0
  66. data/lib/plainrouter/openapi/models/actions_rules_index200_response_rules_data_inner.rb +344 -0
  67. data/lib/plainrouter/openapi/models/actions_rules_index200_response_rules_data_inner_account.rb +157 -0
  68. data/lib/plainrouter/openapi/models/actions_rules_index200_response_rules_data_inner_counts.rb +156 -0
  69. data/lib/plainrouter/openapi/models/actions_rules_index403_response.rb +103 -0
  70. data/lib/plainrouter/openapi/models/actions_rules_index403_response_any_of.rb +214 -0
  71. data/lib/plainrouter/openapi/models/actions_rules_store_request.rb +317 -0
  72. data/lib/plainrouter/openapi/models/actions_rules_store_request_definition.rb +105 -0
  73. data/lib/plainrouter/openapi/models/actions_rules_store_request_definition_any_of.rb +317 -0
  74. data/lib/plainrouter/openapi/models/actions_rules_store_request_definition_any_of1.rb +361 -0
  75. data/lib/plainrouter/openapi/models/actions_rules_store_request_definition_any_of2.rb +361 -0
  76. data/lib/plainrouter/openapi/models/actions_rules_store_request_definition_any_of_cost_ratio.rb +104 -0
  77. data/lib/plainrouter/openapi/models/actions_rules_store_request_window.rb +103 -0
  78. data/lib/plainrouter/openapi/models/actions_rules_update_request.rb +267 -0
  79. data/lib/plainrouter/openapi/models/actions_rules_update_request_definition.rb +105 -0
  80. data/lib/plainrouter/openapi/models/actions_rules_update_request_definition_any_of.rb +317 -0
  81. data/lib/plainrouter/openapi/models/actions_rules_update_request_definition_any_of1.rb +361 -0
  82. data/lib/plainrouter/openapi/models/actions_rules_update_request_definition_any_of2.rb +361 -0
  83. data/lib/plainrouter/openapi/models/ad_test_list_read_tests.rb +1 -1
  84. data/lib/plainrouter/openapi/models/agent_context200_response.rb +214 -0
  85. data/lib/plainrouter/openapi/models/agent_context200_response_agent.rb +268 -0
  86. data/lib/plainrouter/openapi/models/agent_context200_response_platform_ad_account.rb +292 -0
  87. data/lib/plainrouter/openapi/models/agent_context200_response_platform_ad_account_workspace.rb +190 -0
  88. data/lib/plainrouter/openapi/models/agent_context401_response.rb +103 -0
  89. data/lib/plainrouter/openapi/models/agent_context_auth_error.rb +190 -0
  90. data/lib/plainrouter/openapi/models/creative_intake_read.rb +1 -1
  91. data/lib/plainrouter/openapi/models/creative_mcp_grant_operation.rb +44 -0
  92. data/lib/plainrouter/openapi/models/creative_mcp_preview.rb +283 -0
  93. data/lib/plainrouter/openapi/models/creative_mcp_refusal.rb +45 -0
  94. data/lib/plainrouter/openapi/models/creative_mcp_tool.rb +44 -0
  95. data/lib/plainrouter/openapi/models/delivery_status.rb +2 -1
  96. data/lib/plainrouter/openapi/models/deployment_plan_read.rb +42 -4
  97. data/lib/plainrouter/openapi/models/destination.rb +73 -9
  98. data/lib/plainrouter/openapi/models/destination_config.rb +103 -0
  99. data/lib/plainrouter/openapi/models/destination_status.rb +2 -1
  100. data/lib/plainrouter/openapi/models/draft_batch_mode.rb +41 -0
  101. data/lib/plainrouter/openapi/models/get_account_inventory200_response.rb +341 -0
  102. data/lib/plainrouter/openapi/models/get_account_inventory200_response_summary.rb +158 -0
  103. data/lib/plainrouter/openapi/models/get_account_inventory404_response.rb +103 -0
  104. data/lib/plainrouter/openapi/models/get_account_inventory404_response_any_of.rb +188 -0
  105. data/lib/plainrouter/openapi/models/get_event200_response_deliveries_inner.rb +25 -1
  106. data/lib/plainrouter/openapi/models/get_event200_response_event_deliveries_inner.rb +25 -1
  107. data/lib/plainrouter/openapi/models/get_inventory_metrics200_response.rb +26 -5
  108. data/lib/plainrouter/openapi/models/get_inventory_metrics200_response_pagination_value.rb +202 -0
  109. data/lib/plainrouter/openapi/models/launch_creatives_index200_response.rb +164 -0
  110. data/lib/plainrouter/openapi/models/launch_creatives_index200_response_creatives.rb +196 -0
  111. data/lib/plainrouter/openapi/models/{creative_intake_read_creative.rb → launch_creatives_index200_response_creatives_data_inner.rb} +4 -4
  112. data/lib/plainrouter/openapi/models/{creative_intake_read_creative_tags.rb → launch_creatives_index200_response_creatives_data_inner_tags.rb} +1 -1
  113. data/lib/plainrouter/openapi/models/launch_creatives_status200_response.rb +166 -0
  114. data/lib/plainrouter/openapi/models/launch_plans_copy422_response.rb +103 -0
  115. data/lib/plainrouter/openapi/models/launch_plans_index200_response_plans.rb +107 -4
  116. data/lib/plainrouter/openapi/models/launch_plans_index200_response_plans_next_page_arguments.rb +104 -0
  117. data/lib/plainrouter/openapi/models/launch_plans_index200_response_plans_next_page_arguments_any_of.rb +190 -0
  118. data/lib/plainrouter/openapi/models/launch_plans_index200_response_plans_next_page_arguments_any_of1.rb +190 -0
  119. data/lib/plainrouter/openapi/models/launch_plans_store201_response.rb +166 -0
  120. data/lib/plainrouter/openapi/models/launch_plans_validate422_response.rb +103 -0
  121. data/lib/plainrouter/openapi/models/launch_policies_index200_response.rb +164 -0
  122. data/lib/plainrouter/openapi/models/launch_policies_index200_response_policies.rb +453 -0
  123. data/lib/plainrouter/openapi/models/{list_events200_response_events_links_inner.rb → launch_policies_index200_response_policies_links_inner.rb} +3 -3
  124. data/lib/plainrouter/openapi/models/launch_policies_store201_response.rb +166 -0
  125. data/lib/plainrouter/openapi/models/launch_policy.rb +346 -0
  126. data/lib/plainrouter/openapi/models/launcher_file_import403_response.rb +216 -0
  127. data/lib/plainrouter/openapi/models/launcher_file_import422_response.rb +103 -0
  128. data/lib/plainrouter/openapi/models/launcher_file_import422_response_any_of.rb +242 -0
  129. data/lib/plainrouter/openapi/models/launcher_file_import422_response_any_of_syntactic_row_failures_inner.rb +190 -0
  130. data/lib/plainrouter/openapi/models/list_events200_response_events.rb +1 -1
  131. data/lib/plainrouter/openapi/models/plan_execute_rejected_error.rb +2 -2
  132. data/lib/plainrouter/openapi/models/plan_validate_rejected.rb +164 -0
  133. data/lib/plainrouter/openapi/models/plan_validate_rejected_error.rb +191 -0
  134. data/lib/plainrouter/openapi/models/proposal_unavailable.rb +164 -0
  135. data/lib/plainrouter/openapi/models/proposal_unavailable_error.rb +214 -0
  136. data/lib/plainrouter/openapi/models/set_destination_test_mode200_response_destination.rb +67 -1
  137. data/lib/plainrouter/openapi/models/{deployment_plan.rb → store_deployment_plan_request.rb} +185 -208
  138. data/lib/plainrouter/openapi/models/store_deployment_plan_request_ad_set_spec.rb +220 -0
  139. data/lib/plainrouter/openapi/models/store_deployment_plan_request_copy.rb +266 -0
  140. data/lib/plainrouter/openapi/models/store_deployment_plan_request_creative_overrides_inner.rb +217 -0
  141. data/lib/plainrouter/openapi/models/store_launch_policy_request.rb +260 -0
  142. data/lib/plainrouter/openapi/models/update_creative_status_request.rb +188 -0
  143. data/lib/plainrouter/openapi/models/update_deployment_plan_request.rb +511 -0
  144. data/lib/plainrouter/openapi/version.rb +1 -1
  145. data/lib/plainrouter/openapi.rb +88 -5
  146. data/lib/plainrouter/proposal_errors.rb +31 -0
  147. data/lib/plainrouter/version.rb +1 -1
  148. data/lib/plainrouter.rb +1 -0
  149. metadata +94 -7
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "set"
5
+
6
+ module PlainRouter
7
+ module OpenAPI
8
+ module ClosedInputValidator
9
+ MANIFEST = JSON.parse(File.read(File.join(__dir__, "closed_input_objects.json")))
10
+ private_constant :MANIFEST
11
+
12
+ module_function
13
+
14
+ def validate_model!(model_class, attributes)
15
+ return unless attributes.is_a?(Hash)
16
+
17
+ model_type = model_class.is_a?(Module) ? model_class : model_class.class
18
+ model_name = model_type.name.split("::").last
19
+ pointers = MANIFEST.dig("ruby", "input_models", model_name)
20
+ return unless pointers && !pointers.empty?
21
+
22
+ object_pointers = pointers.select do |pointer|
23
+ _resolved_pointer, schema = resolve_schema(pointer)
24
+ schema_type = schema["type"]
25
+ schema_type == "object" ||
26
+ (schema_type.nil? && %w[properties allOf oneOf anyOf].any? { |keyword| schema.key?(keyword) })
27
+ end
28
+ errors = []
29
+
30
+ object_pointers.each do |pointer|
31
+ begin
32
+ validate_schema(pointer, attributes, model_name)
33
+ return
34
+ rescue ArgumentError => error
35
+ errors << error
36
+ end
37
+ end
38
+
39
+ raise errors.first if errors.any?
40
+ end
41
+
42
+ def validate_request!(http_method, path, options)
43
+ operation_name = options[:operation].to_s.split(".").last.to_s
44
+ pointer = MANIFEST.dig("ruby", "operation_ids", operation_name)
45
+ pointer ||= request_schema_pointer(http_method, path)
46
+ return unless pointer
47
+
48
+ body = options[:body]
49
+ body = options[:form_params] if body.nil?
50
+ body = request_body_value(body)
51
+ return unless body.is_a?(Hash) || body.is_a?(Array)
52
+
53
+ resolved_pointer, schema = resolve_schema(pointer)
54
+ object_name = schema["title"] || request_object_name(operation_name, http_method, path, resolved_pointer)
55
+ validate_schema(pointer, body, object_name)
56
+ end
57
+
58
+ def request_body_value(body)
59
+ value = body.respond_to?(:to_hash) ? body.to_hash : body
60
+ return value unless value.is_a?(String)
61
+
62
+ JSON.parse(value)
63
+ rescue JSON::ParserError
64
+ nil
65
+ end
66
+ private_class_method :request_body_value
67
+
68
+ def request_object_name(operation_name, http_method, path, schema_pointer)
69
+ object_name = if operation_name.empty?
70
+ schema_pointer.to_s.start_with?("#/components/schemas/") ?
71
+ schema_pointer.split("/")[3].to_s.gsub("~1", "/").gsub("~0", "~") : ""
72
+ else
73
+ operation_name.split("_").map(&:capitalize).join
74
+ end
75
+ object_name = "#{http_method.to_s.upcase} #{path}" if object_name.empty?
76
+
77
+ "#{object_name} (#{http_method.to_s.upcase})"
78
+ end
79
+ private_class_method :request_object_name
80
+
81
+ def request_schema_pointer(http_method, path)
82
+ method = http_method.to_s.upcase
83
+ concrete_path = path.to_s.split("?", 2).first
84
+ concrete_path = concrete_path.sub(%r{\A/api/v1(?=/|\z)}, "")
85
+
86
+ MANIFEST.fetch("operations").each do |method_path, schema_pointer|
87
+ operation_method, template = method_path.split(" ", 2)
88
+ next unless operation_method == method
89
+
90
+ pattern = Regexp.escape(template).gsub(/\\\{[^}]+\\\}/, "[^/]+")
91
+ return schema_pointer if Regexp.new("\\A#{pattern}\\z").match?(concrete_path)
92
+ end
93
+
94
+ nil
95
+ end
96
+ private_class_method :request_schema_pointer
97
+
98
+ def validate_schema(pointer, value, object_path)
99
+ value = value.to_hash if value.respond_to?(:to_hash)
100
+ resolved_pointer, schema = resolve_schema(pointer)
101
+
102
+ if value.is_a?(Array)
103
+ item_pointer = schema.dig("items", "$ref")
104
+ value.each_with_index do |item, index|
105
+ validate_schema(item_pointer, item, "#{object_path}[#{index}]") if item_pointer
106
+ end
107
+ end
108
+
109
+ if value.is_a?(Hash)
110
+ properties = schema_properties(resolved_pointer)
111
+ allowed_keys = properties.keys
112
+
113
+ if schema["additionalProperties"] == false
114
+ value.each_key do |key|
115
+ next if allowed_keys.include?(key.to_s)
116
+
117
+ raise ArgumentError,
118
+ "Unknown key #{key.to_s.inspect} in closed request object #{object_path.inspect} (schema branch #{resolved_pointer})."
119
+ end
120
+ end
121
+
122
+ properties.each do |property_name, property_schema|
123
+ value.each do |key, child|
124
+ next unless key.to_s == property_name
125
+
126
+ child_pointer = property_schema["$ref"]
127
+ validate_schema(child_pointer, child, "#{object_path}.#{property_name}") if child_pointer
128
+ end
129
+ end
130
+
131
+ additional_schema = schema["additionalProperties"]
132
+ if additional_schema.is_a?(Hash) && additional_schema["$ref"]
133
+ value.each do |key, child|
134
+ next if allowed_keys.include?(key.to_s)
135
+
136
+ validate_schema(additional_schema["$ref"], child, "#{object_path}.#{key}")
137
+ end
138
+ end
139
+ end
140
+
141
+ validate_compositions(schema, value, object_path)
142
+ end
143
+ private_class_method :validate_schema
144
+
145
+ def validate_compositions(schema, value, object_path)
146
+ if schema["allOf"].is_a?(Array)
147
+ schema["allOf"].each do |branch|
148
+ validate_schema(branch["$ref"], value, object_path) if branch["$ref"]
149
+ end
150
+ end
151
+
152
+ %w[oneOf anyOf].each do |keyword|
153
+ branches = schema[keyword]
154
+ next unless branches.is_a?(Array)
155
+
156
+ selected = discriminator_matches(branches, value)
157
+ candidates = selected.empty? ? branches : selected
158
+ errors = []
159
+
160
+ candidates.each do |branch|
161
+ begin
162
+ validate_schema(branch["$ref"], value, object_path) if branch["$ref"]
163
+ return
164
+ rescue ArgumentError => error
165
+ errors << error
166
+ end
167
+ end
168
+
169
+ raise errors.first if errors.any?
170
+ end
171
+ end
172
+ private_class_method :validate_compositions
173
+
174
+ def discriminator_matches(branches, value)
175
+ return [] unless value.is_a?(Hash)
176
+
177
+ branch_properties = branches.map do |branch|
178
+ branch_pointer = branch["$ref"]
179
+ branch_pointer ? schema_properties(branch_pointer) : {}
180
+ end
181
+ common_names = branch_properties.map(&:keys).reduce { |left, right| left & right } || []
182
+ discriminator_names = common_names.select do |name|
183
+ sets = branch_properties.map do |properties|
184
+ child = properties.dig(name, "$ref")
185
+ next [] unless child
186
+
187
+ _pointer, property_schema = resolve_schema(child)
188
+ property_schema.key?("const") ? [property_schema["const"]] : property_schema.fetch("enum", [])
189
+ end
190
+ sets.all? { |values| !values.empty? } && sets.uniq.length > 1
191
+ end
192
+ present_discriminators = discriminator_names.select do |name|
193
+ value.keys.any? { |key| key.to_s == name }
194
+ end
195
+ return [] if present_discriminators.empty?
196
+
197
+ matching = branches.each_with_index.filter_map do |branch, index|
198
+ properties = branch_properties[index]
199
+ matches = present_discriminators.all? do |name|
200
+ property_pointer = properties.dig(name, "$ref")
201
+ next false unless property_pointer
202
+
203
+ _pointer, schema = resolve_schema(property_pointer)
204
+ actual = value.find { |key, _child| key.to_s == name }&.last
205
+ schema.key?("const") ? actual == schema["const"] : schema.fetch("enum", []).include?(actual)
206
+ end
207
+ branch if matches
208
+ end
209
+
210
+ matching
211
+ end
212
+ private_class_method :discriminator_matches
213
+
214
+ def schema_properties(pointer, visited = Set.new)
215
+ _resolved_pointer, schema = resolve_schema(pointer)
216
+ properties = schema["properties"] || {}
217
+
218
+ (schema["allOf"] || []).each do |branch|
219
+ next unless branch["$ref"] && !visited.include?(branch["$ref"])
220
+
221
+ visited.add(branch["$ref"])
222
+ properties = properties.merge(schema_properties(branch["$ref"], visited))
223
+ end
224
+
225
+ properties
226
+ end
227
+ private_class_method :schema_properties
228
+
229
+ def resolve_schema(pointer)
230
+ seen = Set.new
231
+ current_pointer = pointer
232
+
233
+ loop do
234
+ raise ArgumentError, "Cyclic schema reference at #{current_pointer}." if seen.include?(current_pointer)
235
+
236
+ seen.add(current_pointer)
237
+ schema = MANIFEST.fetch("schemas").fetch(current_pointer)
238
+ return [current_pointer, schema] unless schema["$ref"]
239
+
240
+ current_pointer = schema["$ref"]
241
+ end
242
+ end
243
+ private_class_method :resolve_schema
244
+ end
245
+ end
246
+ end