repull 0.2.15 → 0.2.17

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 (145) hide show
  1. checksums.yaml +4 -4
  2. data/lib/repull/api/airbnb_api.rb +149 -54
  3. data/lib/repull/api/billing_api.rb +4 -4
  4. data/lib/repull/api/booking_com_api.rb +122 -29
  5. data/lib/repull/api/conversations_api.rb +398 -4
  6. data/lib/repull/api/guests_api.rb +2 -2
  7. data/lib/repull/api/listings_api.rb +186 -16
  8. data/lib/repull/api/reservations_api.rb +157 -6
  9. data/lib/repull/api/webhooks_api.rb +10 -3
  10. data/lib/repull/models/accept_reservation_request200_response.rb +321 -0
  11. data/lib/repull/models/account_created_event.rb +95 -31
  12. data/lib/repull/models/account_disconnected_event.rb +95 -31
  13. data/lib/repull/models/ai_operation_completed_event.rb +95 -31
  14. data/lib/repull/models/ai_operation_failed_event.rb +95 -31
  15. data/lib/repull/models/airbnb_publish_result.rb +41 -2
  16. data/lib/repull/models/airbnb_reservation_action200_response.rb +157 -0
  17. data/lib/repull/models/airbnb_reservation_action_request.rb +248 -0
  18. data/lib/repull/models/booking_availability_update.rb +4 -20
  19. data/lib/repull/models/booking_availability_update_date_range.rb +193 -0
  20. data/lib/repull/models/booking_availability_update_request.rb +14 -3
  21. data/lib/repull/models/booking_pricing_rate_update.rb +32 -4
  22. data/lib/repull/models/booking_pricing_rate_update_date_range.rb +3 -0
  23. data/lib/repull/models/booking_pricing_rate_update_restrictions.rb +10 -10
  24. data/lib/repull/models/booking_pricing_response.rb +16 -4
  25. data/lib/repull/models/booking_pricing_update_response.rb +82 -18
  26. data/lib/repull/models/booking_property.rb +107 -36
  27. data/lib/repull/models/booking_property_action_request.rb +176 -0
  28. data/lib/repull/models/booking_property_action_response.rb +281 -0
  29. data/lib/repull/models/booking_property_listings_inner.rb +244 -0
  30. data/lib/repull/models/booking_publish_result.rb +245 -0
  31. data/lib/repull/models/booking_publish_section_error.rb +220 -0
  32. data/lib/repull/models/booking_rate_write_occupancy.rb +187 -0
  33. data/lib/repull/models/booking_rate_write_price_half.rb +188 -0
  34. data/lib/repull/models/booking_rate_write_restriction_half.rb +212 -0
  35. data/lib/repull/models/booking_rate_write_verification.rb +200 -0
  36. data/lib/repull/models/booking_rate_write_verification_row.rb +196 -0
  37. data/lib/repull/models/booking_restriction_request_row.rb +199 -0
  38. data/lib/repull/models/booking_restriction_verification.rb +208 -0
  39. data/lib/repull/models/booking_restriction_verification_row.rb +203 -0
  40. data/lib/repull/models/booking_restriction_verification_row_booking_value.rb +105 -0
  41. data/lib/repull/models/booking_restriction_verification_row_expected.rb +105 -0
  42. data/lib/repull/models/booking_rooms_rates_response.rb +41 -7
  43. data/lib/repull/models/booking_rooms_rates_response_rooms_inner.rb +12 -1
  44. data/lib/repull/models/booking_rooms_rates_response_rooms_inner_rates_inner.rb +1 -0
  45. data/lib/repull/models/booking_setup_request.rb +44 -5
  46. data/lib/repull/models/booking_setup_request_legal_entity.rb +235 -0
  47. data/lib/repull/models/booking_upstream_failure.rb +204 -0
  48. data/lib/repull/models/calendar_updated_event.rb +95 -31
  49. data/lib/repull/models/channel_market_state_item.rb +291 -0
  50. data/lib/repull/models/conversation_message_attachment.rb +28 -1
  51. data/lib/repull/models/create_airbnb_offer_request.rb +93 -8
  52. data/lib/repull/models/create_airbnb_offer_request_guest_details.rb +279 -0
  53. data/lib/repull/models/create_conversation_special_offer201_response.rb +293 -0
  54. data/lib/repull/models/create_conversation_special_offer201_response_guests.rb +189 -0
  55. data/lib/repull/models/create_conversation_special_offer_request.rb +263 -0
  56. data/lib/repull/models/create_conversation_special_offer_request_guests.rb +263 -0
  57. data/lib/repull/models/decline_reservation_request_request.rb +234 -0
  58. data/lib/repull/models/error_error.rb +11 -1
  59. data/lib/repull/models/get_airbnb_offer200_response.rb +289 -0
  60. data/lib/repull/models/get_usage_summary200_response.rb +10 -1
  61. data/lib/repull/models/get_usage_summary200_response_limits.rb +11 -1
  62. data/lib/repull/models/get_usage_summary200_response_remaining.rb +11 -1
  63. data/lib/repull/models/get_usage_summary200_response_used.rb +10 -1
  64. data/lib/repull/models/get_usage_tier200_response.rb +10 -1
  65. data/lib/repull/models/get_usage_tier200_response_limits.rb +11 -1
  66. data/lib/repull/models/get_usage_tier200_response_remaining.rb +11 -1
  67. data/lib/repull/models/get_usage_tier200_response_used.rb +10 -1
  68. data/lib/repull/models/inquiry_created_event.rb +281 -0
  69. data/lib/repull/models/inquiry_created_payload.rb +185 -0
  70. data/lib/repull/models/inquiry_updated_event.rb +281 -0
  71. data/lib/repull/models/inquiry_updated_payload.rb +213 -0
  72. data/lib/repull/models/inquiry_webhook_object.rb +323 -0
  73. data/lib/repull/models/inquiry_webhook_object_expected_payout.rb +158 -0
  74. data/lib/repull/models/inquiry_webhook_object_guests.rb +188 -0
  75. data/lib/repull/models/list_airbnb_thread_messages200_response.rb +170 -0
  76. data/lib/repull/models/list_airbnb_thread_messages200_response_data_inner.rb +274 -0
  77. data/lib/repull/models/list_airbnb_thread_messages200_response_pagination.rb +158 -0
  78. data/lib/repull/models/list_inquiries200_response.rb +192 -0
  79. data/lib/repull/models/list_inquiries200_response_data_inner.rb +418 -0
  80. data/lib/repull/models/list_inquiries200_response_data_inner_expected_payout.rb +159 -0
  81. data/lib/repull/models/list_inquiries200_response_data_inner_guests.rb +188 -0
  82. data/lib/repull/models/listing_address_readiness.rb +171 -0
  83. data/lib/repull/models/listing_content_update_request_address.rb +40 -3
  84. data/lib/repull/models/listing_create_request.rb +48 -2
  85. data/lib/repull/models/listing_created_event.rb +95 -31
  86. data/lib/repull/models/listing_deleted_event.rb +95 -31
  87. data/lib/repull/models/listing_deleted_payload.rb +55 -21
  88. data/lib/repull/models/listing_market_state_request.rb +149 -0
  89. data/lib/repull/models/listing_market_state_response.rb +221 -0
  90. data/lib/repull/models/listing_publish_booking_request.rb +149 -0
  91. data/lib/repull/models/{listing_publish_response.rb → listing_publish_booking_response.rb} +4 -5
  92. data/lib/repull/models/listing_publish_status_channel.rb +32 -1
  93. data/lib/repull/models/listing_publish_status_connection.rb +17 -5
  94. data/lib/repull/models/listing_publish_status_response.rb +13 -1
  95. data/lib/repull/models/listing_reactivated_event.rb +281 -0
  96. data/lib/repull/models/listing_suspended_event.rb +281 -0
  97. data/lib/repull/models/listing_suspension_payload.rb +202 -0
  98. data/lib/repull/models/listing_updated_event.rb +95 -31
  99. data/lib/repull/models/listing_updated_payload.rb +50 -22
  100. data/lib/repull/models/listing_webhook_object.rb +291 -0
  101. data/lib/repull/models/listing_webhook_object_address.rb +168 -0
  102. data/lib/repull/models/listing_webhook_object_channels_inner.rb +184 -0
  103. data/lib/repull/models/message.rb +1 -0
  104. data/lib/repull/models/payment_completed_event.rb +95 -31
  105. data/lib/repull/models/payment_completed_payload.rb +46 -44
  106. data/lib/repull/models/payment_refunded_event.rb +95 -31
  107. data/lib/repull/models/payment_refunded_payload.rb +46 -44
  108. data/lib/repull/models/payment_webhook_object.rb +351 -0
  109. data/lib/repull/models/preapprove_conversation201_response.rb +253 -0
  110. data/lib/repull/models/preapprove_conversation_request.rb +150 -0
  111. data/lib/repull/models/replay_webhook_delivery_request.rb +150 -0
  112. data/lib/repull/models/repull_ping_event.rb +95 -31
  113. data/lib/repull/models/reservation.rb +22 -2
  114. data/lib/repull/models/reservation_alteration_created_event.rb +95 -31
  115. data/lib/repull/models/reservation_alteration_responded_event.rb +95 -31
  116. data/lib/repull/models/reservation_cancelled_event.rb +95 -31
  117. data/lib/repull/models/reservation_created_event.rb +95 -32
  118. data/lib/repull/models/reservation_message_received_event.rb +95 -31
  119. data/lib/repull/models/reservation_message_received_payload.rb +17 -4
  120. data/lib/repull/models/reservation_request_created_event.rb +281 -0
  121. data/lib/repull/models/reservation_request_created_payload.rb +224 -0
  122. data/lib/repull/models/reservation_request_updated_event.rb +281 -0
  123. data/lib/repull/models/reservation_request_updated_payload.rb +251 -0
  124. data/lib/repull/models/reservation_updated_event.rb +95 -31
  125. data/lib/repull/models/review_created_event.rb +95 -31
  126. data/lib/repull/models/review_responded_event.rb +95 -31
  127. data/lib/repull/models/send_airbnb_message201_response.rb +104 -0
  128. data/lib/repull/models/send_airbnb_message_request.rb +4 -20
  129. data/lib/repull/models/send_message_attachment.rb +225 -0
  130. data/lib/repull/models/send_message_part.rb +201 -0
  131. data/lib/repull/models/send_message_request.rb +44 -19
  132. data/lib/repull/models/send_message_response.rb +29 -5
  133. data/lib/repull/models/sent_attachment.rb +196 -0
  134. data/lib/repull/models/usage_quota_warning_event.rb +281 -0
  135. data/lib/repull/models/usage_quota_warning_payload.rb +224 -0
  136. data/lib/repull/models/usage_quota_warning_payload_top_operation.rb +167 -0
  137. data/lib/repull/models/webhook_event.rb +17 -3
  138. data/lib/repull/models/webhook_event_account.rb +171 -0
  139. data/lib/repull/models/webhook_event_type.rb +8 -1
  140. data/lib/repull/models/withdraw_conversation_special_offer200_response.rb +240 -0
  141. data/lib/repull/version.rb +1 -1
  142. data/lib/repull.rb +71 -1
  143. data/openapi/v1.json +19706 -13248
  144. data/scripts/regen.sh +1 -1
  145. metadata +73 -3
@@ -0,0 +1,105 @@
1
+ =begin
2
+ #Repull API
3
+
4
+ #The unified API for vacation rental tech. Connect to 50+ PMS platforms and 4 OTA channels through one REST API. Built-in AI operations for guest communication, pricing, and listing optimization. ## Designed for AI agents Every error response on this API includes machine-parseable fields so an LLM (Claude in MCP, Cursor, Cline, GPT, etc.) can self-recover without escalating to a human: - `error.code` — stable string identifier (e.g. `invalid_params`, `rate_limit_exceeded`) - `error.message` — human-readable cause - `error.fix` — exact recovery steps (e.g. \"Pass `check_in_after` as ISO 8601: `?check_in_after=2026-01-15`\") - `error.docs_url` — link to the canonical write-up at `https://repull.dev/docs/errors/{code}` - `error.request_id` — id to correlate with server-side logs - `error.field` / `error.value_received` / `error.valid_values` / `error.did_you_mean` — when the error is parameter-specific - `error.retry_after` — seconds to wait before retrying (rate-limit + transient upstream) `Access-Control-Expose-Headers` lists `x-request-id` and the `X-RateLimit-*` family so browsers can read them on cross-origin responses. ## Quick Start 1. Get an API key at https://repull.dev/dashboard 2. Connect a PMS: `POST /v1/connect/{provider}` 3. List properties: `GET /v1/properties` 4. Get reservations: `GET /v1/reservations` ## Authentication All requests require a Bearer token: ``` Authorization: Bearer sk_live_YOUR_API_KEY ``` ## Request Correlation (X-Request-ID) Every response carries an `X-Request-ID` header, e.g. `X-Request-ID: req_01HXY...`. Include this id in support tickets and bug reports — we can trace the full request lifecycle (auth, rate limit, handler, downstream calls, log row) from a single id. You may set the header on the inbound request to forward your own trace id; we will echo it back instead of generating a new one. Accepted format: `^[\\\\w.-]{1,128}$`. The id is also embedded in error envelopes as `request_id` so server-side log diffs work even when the response headers are stripped by an intermediate proxy. ## Rate Limits The public API enforces a per-API-key sliding-window rate limit on top of the per-tier monthly + daily-AI quotas. **Default policy:** 600 requests per 60 seconds, per API key. Sliding window — there is no fixed-minute boundary you can burst across. Every response includes: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests permitted in the current window. | | `X-RateLimit-Remaining` | Requests left in the current window after this call. | | `X-RateLimit-Reset` | Unix epoch (seconds) when the next slot opens. | | `X-RateLimit-Policy` | Machine-readable policy descriptor, e.g. `600;w=60`. | | `Retry-After` | Seconds to wait before retrying. **Only present on 429 responses.** | **On 429 (rate_limit_exceeded):** the response body matches the standard error envelope with `code: \"rate_limit_exceeded\"`, plus `limit`, `window_seconds`, `retry_after`, and `request_id` fields. SDKs MUST honor `Retry-After` and use exponential backoff with jitter on subsequent retries — never a tight loop. Recommended backoff: ``` sleep_ms = (Retry-After * 1000) + random(0..250) ``` Monthly + daily-AI tier quotas (`free`, `starter`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resetsAt` fields. ## Plan Limits (402 — `listings_limit_exceeded`) The Repull API also enforces a per-tier cap on **active listings**: | Tier | Active listings cap | |---|---| | `free` | 3 | | `starter` | 50 | | `custom` | unlimited | When a customer's active-listing count is above their tier cap, the API returns **`402 Payment Required`** with `error.code = \"listings_limit_exceeded\"` on every route EXCEPT: - `/v1/health` — uptime probes are never gated. - `/v1/usage/*` — so dashboards can render the over-cap state. - Any `DELETE` — so the customer can trim listings to get back under the cap without paying. Unlike 429, 402 is NOT a \"wait and retry\" condition — `Retry-After` is not set. The only paths back to 200 are: 1. `DELETE` enough listings to come back under the cap, or 2. Upgrade at `https://repull.dev/dashboard/billing`. The server-side usage cache is 60s, so the first 200 after an upgrade may take up to a minute. The envelope mirrors `rate_limit_exceeded` for SDK ergonomics: `tier`, `limit`, `active_listings`, `upgrade_url`, plus the standard `code` / `message` / `fix` / `docs_url` / `request_id`.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+ Contact: ivan@vanio.ai
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.22.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Repull
17
+ # What Booking.com holds for that night now; `null` when they reported nothing for the field either way.
18
+ module BookingRestrictionVerificationRowBookingValue
19
+ class << self
20
+ # List of class defined in oneOf (OpenAPI v3)
21
+ def openapi_one_of
22
+ [
23
+ :'Boolean',
24
+ :'Integer'
25
+ ]
26
+ end
27
+
28
+ # Builds the object
29
+ # @param [Mixed] Data to be matched against the list of oneOf items
30
+ # @return [Object] Returns the model or the data itself
31
+ def build(data)
32
+ # Go through the list of oneOf items and attempt to identify the appropriate one.
33
+ # Note:
34
+ # - We do not attempt to check whether exactly one item matches.
35
+ # - No advanced validation of types in some cases (e.g. "x: { type: string }" will happily match { x: 123 })
36
+ # due to the way the deserialization is made in the base_object template (it just casts without verifying).
37
+ # - TODO: scalar values are de facto behaving as if they were nullable.
38
+ # - TODO: logging when debugging is set.
39
+ openapi_one_of.each do |klass|
40
+ begin
41
+ next if klass == :AnyType # "nullable: true"
42
+ return find_and_cast_into_type(klass, data)
43
+ rescue # rescue all errors so we keep iterating even if the current item lookup raises
44
+ end
45
+ end
46
+
47
+ openapi_one_of.include?(:AnyType) ? data : nil
48
+ end
49
+
50
+ private
51
+
52
+ SchemaMismatchError = Class.new(StandardError)
53
+
54
+ # Note: 'File' is missing here because in the regular case we get the data _after_ a call to JSON.parse.
55
+ def find_and_cast_into_type(klass, data)
56
+ return if data.nil?
57
+
58
+ case klass.to_s
59
+ when 'Boolean'
60
+ return data if data.instance_of?(TrueClass) || data.instance_of?(FalseClass)
61
+ when 'Float'
62
+ return data if data.instance_of?(Float)
63
+ when 'Integer'
64
+ return data if data.instance_of?(Integer)
65
+ when 'Time'
66
+ return Time.parse(data)
67
+ when 'Date'
68
+ return Date.iso8601(data)
69
+ when 'String'
70
+ return data if data.instance_of?(String)
71
+ when 'Object' # "type: object"
72
+ return data if data.instance_of?(Hash)
73
+ when /\AArray<(?<sub_type>.+)>\z/ # "type: array"
74
+ if data.instance_of?(Array)
75
+ sub_type = Regexp.last_match[:sub_type]
76
+ return data.map { |item| find_and_cast_into_type(sub_type, item) }
77
+ end
78
+ when /\AHash<String, (?<sub_type>.+)>\z/ # "type: object" with "additionalProperties: { ... }"
79
+ if data.instance_of?(Hash) && data.keys.all? { |k| k.instance_of?(Symbol) || k.instance_of?(String) }
80
+ sub_type = Regexp.last_match[:sub_type]
81
+ return data.each_with_object({}) { |(k, v), hsh| hsh[k] = find_and_cast_into_type(sub_type, v) }
82
+ end
83
+ else # model
84
+ const = Repull.const_get(klass)
85
+ if const
86
+ if const.respond_to?(:openapi_one_of) # nested oneOf model
87
+ model = const.build(data)
88
+ return model if model
89
+ else
90
+ # raise if data contains keys that are not known to the model
91
+ raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty?
92
+ model = const.build_from_hash(data)
93
+ return model if model
94
+ end
95
+ end
96
+ end
97
+
98
+ raise # if no match by now, raise
99
+ rescue
100
+ raise SchemaMismatchError, "#{data} doesn't match the #{klass} type"
101
+ end
102
+ end
103
+ end
104
+
105
+ end
@@ -0,0 +1,105 @@
1
+ =begin
2
+ #Repull API
3
+
4
+ #The unified API for vacation rental tech. Connect to 50+ PMS platforms and 4 OTA channels through one REST API. Built-in AI operations for guest communication, pricing, and listing optimization. ## Designed for AI agents Every error response on this API includes machine-parseable fields so an LLM (Claude in MCP, Cursor, Cline, GPT, etc.) can self-recover without escalating to a human: - `error.code` — stable string identifier (e.g. `invalid_params`, `rate_limit_exceeded`) - `error.message` — human-readable cause - `error.fix` — exact recovery steps (e.g. \"Pass `check_in_after` as ISO 8601: `?check_in_after=2026-01-15`\") - `error.docs_url` — link to the canonical write-up at `https://repull.dev/docs/errors/{code}` - `error.request_id` — id to correlate with server-side logs - `error.field` / `error.value_received` / `error.valid_values` / `error.did_you_mean` — when the error is parameter-specific - `error.retry_after` — seconds to wait before retrying (rate-limit + transient upstream) `Access-Control-Expose-Headers` lists `x-request-id` and the `X-RateLimit-*` family so browsers can read them on cross-origin responses. ## Quick Start 1. Get an API key at https://repull.dev/dashboard 2. Connect a PMS: `POST /v1/connect/{provider}` 3. List properties: `GET /v1/properties` 4. Get reservations: `GET /v1/reservations` ## Authentication All requests require a Bearer token: ``` Authorization: Bearer sk_live_YOUR_API_KEY ``` ## Request Correlation (X-Request-ID) Every response carries an `X-Request-ID` header, e.g. `X-Request-ID: req_01HXY...`. Include this id in support tickets and bug reports — we can trace the full request lifecycle (auth, rate limit, handler, downstream calls, log row) from a single id. You may set the header on the inbound request to forward your own trace id; we will echo it back instead of generating a new one. Accepted format: `^[\\\\w.-]{1,128}$`. The id is also embedded in error envelopes as `request_id` so server-side log diffs work even when the response headers are stripped by an intermediate proxy. ## Rate Limits The public API enforces a per-API-key sliding-window rate limit on top of the per-tier monthly + daily-AI quotas. **Default policy:** 600 requests per 60 seconds, per API key. Sliding window — there is no fixed-minute boundary you can burst across. Every response includes: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests permitted in the current window. | | `X-RateLimit-Remaining` | Requests left in the current window after this call. | | `X-RateLimit-Reset` | Unix epoch (seconds) when the next slot opens. | | `X-RateLimit-Policy` | Machine-readable policy descriptor, e.g. `600;w=60`. | | `Retry-After` | Seconds to wait before retrying. **Only present on 429 responses.** | **On 429 (rate_limit_exceeded):** the response body matches the standard error envelope with `code: \"rate_limit_exceeded\"`, plus `limit`, `window_seconds`, `retry_after`, and `request_id` fields. SDKs MUST honor `Retry-After` and use exponential backoff with jitter on subsequent retries — never a tight loop. Recommended backoff: ``` sleep_ms = (Retry-After * 1000) + random(0..250) ``` Monthly + daily-AI tier quotas (`free`, `starter`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resetsAt` fields. ## Plan Limits (402 — `listings_limit_exceeded`) The Repull API also enforces a per-tier cap on **active listings**: | Tier | Active listings cap | |---|---| | `free` | 3 | | `starter` | 50 | | `custom` | unlimited | When a customer's active-listing count is above their tier cap, the API returns **`402 Payment Required`** with `error.code = \"listings_limit_exceeded\"` on every route EXCEPT: - `/v1/health` — uptime probes are never gated. - `/v1/usage/*` — so dashboards can render the over-cap state. - Any `DELETE` — so the customer can trim listings to get back under the cap without paying. Unlike 429, 402 is NOT a \"wait and retry\" condition — `Retry-After` is not set. The only paths back to 200 are: 1. `DELETE` enough listings to come back under the cap, or 2. Upgrade at `https://repull.dev/dashboard/billing`. The server-side usage cache is 60s, so the first 200 after an upgrade may take up to a minute. The envelope mirrors `rate_limit_exceeded` for SDK ergonomics: `tier`, `limit`, `active_listings`, `upgrade_url`, plus the standard `code` / `message` / `fix` / `docs_url` / `request_id`.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+ Contact: ivan@vanio.ai
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.22.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Repull
17
+ # The value that was sent.
18
+ module BookingRestrictionVerificationRowExpected
19
+ class << self
20
+ # List of class defined in oneOf (OpenAPI v3)
21
+ def openapi_one_of
22
+ [
23
+ :'Boolean',
24
+ :'Integer'
25
+ ]
26
+ end
27
+
28
+ # Builds the object
29
+ # @param [Mixed] Data to be matched against the list of oneOf items
30
+ # @return [Object] Returns the model or the data itself
31
+ def build(data)
32
+ # Go through the list of oneOf items and attempt to identify the appropriate one.
33
+ # Note:
34
+ # - We do not attempt to check whether exactly one item matches.
35
+ # - No advanced validation of types in some cases (e.g. "x: { type: string }" will happily match { x: 123 })
36
+ # due to the way the deserialization is made in the base_object template (it just casts without verifying).
37
+ # - TODO: scalar values are de facto behaving as if they were nullable.
38
+ # - TODO: logging when debugging is set.
39
+ openapi_one_of.each do |klass|
40
+ begin
41
+ next if klass == :AnyType # "nullable: true"
42
+ return find_and_cast_into_type(klass, data)
43
+ rescue # rescue all errors so we keep iterating even if the current item lookup raises
44
+ end
45
+ end
46
+
47
+ openapi_one_of.include?(:AnyType) ? data : nil
48
+ end
49
+
50
+ private
51
+
52
+ SchemaMismatchError = Class.new(StandardError)
53
+
54
+ # Note: 'File' is missing here because in the regular case we get the data _after_ a call to JSON.parse.
55
+ def find_and_cast_into_type(klass, data)
56
+ return if data.nil?
57
+
58
+ case klass.to_s
59
+ when 'Boolean'
60
+ return data if data.instance_of?(TrueClass) || data.instance_of?(FalseClass)
61
+ when 'Float'
62
+ return data if data.instance_of?(Float)
63
+ when 'Integer'
64
+ return data if data.instance_of?(Integer)
65
+ when 'Time'
66
+ return Time.parse(data)
67
+ when 'Date'
68
+ return Date.iso8601(data)
69
+ when 'String'
70
+ return data if data.instance_of?(String)
71
+ when 'Object' # "type: object"
72
+ return data if data.instance_of?(Hash)
73
+ when /\AArray<(?<sub_type>.+)>\z/ # "type: array"
74
+ if data.instance_of?(Array)
75
+ sub_type = Regexp.last_match[:sub_type]
76
+ return data.map { |item| find_and_cast_into_type(sub_type, item) }
77
+ end
78
+ when /\AHash<String, (?<sub_type>.+)>\z/ # "type: object" with "additionalProperties: { ... }"
79
+ if data.instance_of?(Hash) && data.keys.all? { |k| k.instance_of?(Symbol) || k.instance_of?(String) }
80
+ sub_type = Regexp.last_match[:sub_type]
81
+ return data.each_with_object({}) { |(k, v), hsh| hsh[k] = find_and_cast_into_type(sub_type, v) }
82
+ end
83
+ else # model
84
+ const = Repull.const_get(klass)
85
+ if const
86
+ if const.respond_to?(:openapi_one_of) # nested oneOf model
87
+ model = const.build(data)
88
+ return model if model
89
+ else
90
+ # raise if data contains keys that are not known to the model
91
+ raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty?
92
+ model = const.build_from_hash(data)
93
+ return model if model
94
+ end
95
+ end
96
+ end
97
+
98
+ raise # if no match by now, raise
99
+ rescue
100
+ raise SchemaMismatchError, "#{data} doesn't match the #{klass} type"
101
+ end
102
+ end
103
+ end
104
+
105
+ end
@@ -14,21 +14,34 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Repull
17
- # Returned by `GET /v1/channels/booking/properties/{id}/rooms`. Exposes the Booking.com room + rate-plan mapping ids for a listing so a caller can assemble a `PUT /v1/channels/booking/availability` restriction write (which requires `roomId` + `rateId` on every update). Sourced from Booking's B.XML roomrates feed.
17
+ # Returned by `GET /v1/channels/booking/properties/{id}/rooms`. Exposes the Booking.com room + rate-plan mapping ids for a listing so a caller can assemble a `PUT /v1/channels/booking/availability` restriction write (which requires `roomId` + `rateId` on every update). Read live from Booking's B.XML roomrates feed; `source` says so, and says when the answer came from the last import instead.
18
18
  class BookingRoomsRatesResponse < ApiModelBase
19
- # Booking.com hotel/property id the rooms belong to.
19
+ # Booking.com hotel/property id the rooms belong to — the one the mapping resolved to.
20
20
  attr_accessor :hotel_id
21
21
 
22
- # Vanio listing id echoed back.
22
+ # Repull listing id echoed back.
23
23
  attr_accessor :listing_id
24
24
 
25
+ # Other Booking.com properties this listing is also published under. Empty in the normal case. Pass one as `?hotel_id=` to read its rooms instead.
26
+ attr_accessor :other_hotel_ids
27
+
28
+ # Where the rooms came from. `booking` — read live from Booking.com just now. `mirror` — Booking.com returned nothing usable, so these are the rooms and rate plans recorded at the last import; the ids are Booking.com's own and are safe to write against, but they can be stale and `maxPersons`, `policy`, `policyId`, `pricingType` and `isChildRate` come back `null` because only the live feed states them.
29
+ attr_accessor :source
30
+
31
+ # Why the live read was not used. Null when `source` is `booking`.
32
+ attr_accessor :mirror_reason
33
+
34
+ # Empty only when Booking.com reports no rooms for this property AND nothing was recorded at the last import. A failed read is never an empty list — it is an error.
25
35
  attr_accessor :rooms
26
36
 
27
37
  # Attribute mapping from ruby-style variable name to JSON key.
28
38
  def self.attribute_map
29
39
  {
30
- :'hotel_id' => :'hotel_id',
31
- :'listing_id' => :'listing_id',
40
+ :'hotel_id' => :'hotelId',
41
+ :'listing_id' => :'listingId',
42
+ :'other_hotel_ids' => :'otherHotelIds',
43
+ :'source' => :'source',
44
+ :'mirror_reason' => :'mirrorReason',
32
45
  :'rooms' => :'rooms'
33
46
  }
34
47
  end
@@ -47,7 +60,10 @@ module Repull
47
60
  def self.openapi_types
48
61
  {
49
62
  :'hotel_id' => :'String',
50
- :'listing_id' => :'Integer',
63
+ :'listing_id' => :'String',
64
+ :'other_hotel_ids' => :'Array<String>',
65
+ :'source' => :'String',
66
+ :'mirror_reason' => :'String',
51
67
  :'rooms' => :'Array<BookingRoomsRatesResponseRoomsInner>'
52
68
  }
53
69
  end
@@ -55,6 +71,7 @@ module Repull
55
71
  # List of attributes with nullable: true
56
72
  def self.openapi_nullable
57
73
  Set.new([
74
+ :'mirror_reason',
58
75
  ])
59
76
  end
60
77
 
@@ -82,6 +99,20 @@ module Repull
82
99
  self.listing_id = attributes[:'listing_id']
83
100
  end
84
101
 
102
+ if attributes.key?(:'other_hotel_ids')
103
+ if (value = attributes[:'other_hotel_ids']).is_a?(Array)
104
+ self.other_hotel_ids = value
105
+ end
106
+ end
107
+
108
+ if attributes.key?(:'source')
109
+ self.source = attributes[:'source']
110
+ end
111
+
112
+ if attributes.key?(:'mirror_reason')
113
+ self.mirror_reason = attributes[:'mirror_reason']
114
+ end
115
+
85
116
  if attributes.key?(:'rooms')
86
117
  if (value = attributes[:'rooms']).is_a?(Array)
87
118
  self.rooms = value
@@ -111,6 +142,9 @@ module Repull
111
142
  self.class == o.class &&
112
143
  hotel_id == o.hotel_id &&
113
144
  listing_id == o.listing_id &&
145
+ other_hotel_ids == o.other_hotel_ids &&
146
+ source == o.source &&
147
+ mirror_reason == o.mirror_reason &&
114
148
  rooms == o.rooms
115
149
  end
116
150
 
@@ -123,7 +157,7 @@ module Repull
123
157
  # Calculates hash code according to all attributes.
124
158
  # @return [Integer] Hash code
125
159
  def hash
126
- [hotel_id, listing_id, rooms].hash
160
+ [hotel_id, listing_id, other_hotel_ids, source, mirror_reason, rooms].hash
127
161
  end
128
162
 
129
163
  # Builds the object from hash
@@ -20,6 +20,9 @@ module Repull
20
20
 
21
21
  attr_accessor :room_name
22
22
 
23
+ # Booking.com's capacity for this room, as imported. The occupancy a rate write falls back to when the rate plan states no `maxPersons`. Null when Booking.com never stated one.
24
+ attr_accessor :max_adults
25
+
23
26
  attr_accessor :rates
24
27
 
25
28
  # Attribute mapping from ruby-style variable name to JSON key.
@@ -27,6 +30,7 @@ module Repull
27
30
  {
28
31
  :'room_id' => :'roomId',
29
32
  :'room_name' => :'roomName',
33
+ :'max_adults' => :'maxAdults',
30
34
  :'rates' => :'rates'
31
35
  }
32
36
  end
@@ -46,6 +50,7 @@ module Repull
46
50
  {
47
51
  :'room_id' => :'String',
48
52
  :'room_name' => :'String',
53
+ :'max_adults' => :'Integer',
49
54
  :'rates' => :'Array<BookingRoomsRatesResponseRoomsInnerRatesInner>'
50
55
  }
51
56
  end
@@ -55,6 +60,7 @@ module Repull
55
60
  Set.new([
56
61
  :'room_id',
57
62
  :'room_name',
63
+ :'max_adults',
58
64
  ])
59
65
  end
60
66
 
@@ -82,6 +88,10 @@ module Repull
82
88
  self.room_name = attributes[:'room_name']
83
89
  end
84
90
 
91
+ if attributes.key?(:'max_adults')
92
+ self.max_adults = attributes[:'max_adults']
93
+ end
94
+
85
95
  if attributes.key?(:'rates')
86
96
  if (value = attributes[:'rates']).is_a?(Array)
87
97
  self.rates = value
@@ -111,6 +121,7 @@ module Repull
111
121
  self.class == o.class &&
112
122
  room_id == o.room_id &&
113
123
  room_name == o.room_name &&
124
+ max_adults == o.max_adults &&
114
125
  rates == o.rates
115
126
  end
116
127
 
@@ -123,7 +134,7 @@ module Repull
123
134
  # Calculates hash code according to all attributes.
124
135
  # @return [Integer] Hash code
125
136
  def hash
126
- [room_id, room_name, rates].hash
137
+ [room_id, room_name, max_adults, rates].hash
127
138
  end
128
139
 
129
140
  # Builds the object from hash
@@ -25,6 +25,7 @@ module Repull
25
25
 
26
26
  attr_accessor :policy_id
27
27
 
28
+ # The party size this rate plan prices. A rate amount must be written at this number: above it Booking.com declines the price in silence, below it answers 400. Null when `source` is `mirror`.
28
29
  attr_accessor :max_persons
29
30
 
30
31
  # Pricing model: `Standard`, `RLO`, `OBP`, or `LOS`.
@@ -17,10 +17,21 @@ module Repull
17
17
  class BookingSetupRequest < ApiModelBase
18
18
  attr_accessor :action
19
19
 
20
- # Booking.com property id — required for readiness/open/contacts/policies actions.
20
+ # Repull listing id — required for `create-property`, `add-room` and `add-unit`. NOT a Booking.com Hotel ID. `listingId` is accepted as an alias.
21
+ attr_accessor :listing_id
22
+
23
+ # Booking.com Hotel ID — required for `add-room`, `add-unit`, `advance`, and the readiness/open/contacts/policies actions.
21
24
  attr_accessor :property_id
22
25
 
23
- # Legal entity id — required for `check-legal-status`.
26
+ # Booking.com room id — required for `add-unit`. `GET /v1/channels/booking/properties/{listingId}/rooms` lists them. `roomId` is accepted as an alias.
27
+ attr_accessor :room_id
28
+
29
+ # Optional override for `create-property`. Omit it: the legal entity this workspace already uses is resolved automatically. An id that carries another workspace's properties is refused with `403 legal_entity_not_yours`. `legalEntityId` is accepted as an alias.
30
+ attr_accessor :legal_entity_id
31
+
32
+ attr_accessor :legal_entity
33
+
34
+ # Legal entity id — required for `check-legal-status`, which always answers 404.
24
35
  attr_accessor :leid
25
36
 
26
37
  # Contacts payload for `set-contacts`.
@@ -52,7 +63,11 @@ module Repull
52
63
  def self.attribute_map
53
64
  {
54
65
  :'action' => :'action',
66
+ :'listing_id' => :'listing_id',
55
67
  :'property_id' => :'property_id',
68
+ :'room_id' => :'room_id',
69
+ :'legal_entity_id' => :'legal_entity_id',
70
+ :'legal_entity' => :'legal_entity',
56
71
  :'leid' => :'leid',
57
72
  :'contacts' => :'contacts'
58
73
  }
@@ -72,7 +87,11 @@ module Repull
72
87
  def self.openapi_types
73
88
  {
74
89
  :'action' => :'String',
90
+ :'listing_id' => :'Integer',
75
91
  :'property_id' => :'String',
92
+ :'room_id' => :'Integer',
93
+ :'legal_entity_id' => :'Integer',
94
+ :'legal_entity' => :'BookingSetupRequestLegalEntity',
76
95
  :'leid' => :'Integer',
77
96
  :'contacts' => :'Array<Hash<String, Object>>'
78
97
  }
@@ -106,10 +125,26 @@ module Repull
106
125
  self.action = nil
107
126
  end
108
127
 
128
+ if attributes.key?(:'listing_id')
129
+ self.listing_id = attributes[:'listing_id']
130
+ end
131
+
109
132
  if attributes.key?(:'property_id')
110
133
  self.property_id = attributes[:'property_id']
111
134
  end
112
135
 
136
+ if attributes.key?(:'room_id')
137
+ self.room_id = attributes[:'room_id']
138
+ end
139
+
140
+ if attributes.key?(:'legal_entity_id')
141
+ self.legal_entity_id = attributes[:'legal_entity_id']
142
+ end
143
+
144
+ if attributes.key?(:'legal_entity')
145
+ self.legal_entity = attributes[:'legal_entity']
146
+ end
147
+
113
148
  if attributes.key?(:'leid')
114
149
  self.leid = attributes[:'leid']
115
150
  end
@@ -138,7 +173,7 @@ module Repull
138
173
  def valid?
139
174
  warn '[DEPRECATED] the `valid?` method is obsolete'
140
175
  return false if @action.nil?
141
- action_validator = EnumAttributeValidator.new('String', ["create-legal-entity", "check-legal-status", "check-readiness", "open-property", "set-contacts", "set-policies"])
176
+ action_validator = EnumAttributeValidator.new('String', ["create-property", "add-room", "add-unit", "advance", "create-legal-entity", "check-legal-status", "check-readiness", "open-property", "set-contacts", "set-policies"])
142
177
  return false unless action_validator.valid?(@action)
143
178
  true
144
179
  end
@@ -146,7 +181,7 @@ module Repull
146
181
  # Custom attribute writer method checking allowed values (enum).
147
182
  # @param [Object] action Object to be assigned
148
183
  def action=(action)
149
- validator = EnumAttributeValidator.new('String', ["create-legal-entity", "check-legal-status", "check-readiness", "open-property", "set-contacts", "set-policies"])
184
+ validator = EnumAttributeValidator.new('String', ["create-property", "add-room", "add-unit", "advance", "create-legal-entity", "check-legal-status", "check-readiness", "open-property", "set-contacts", "set-policies"])
150
185
  unless validator.valid?(action)
151
186
  fail ArgumentError, "invalid value for \"action\", must be one of #{validator.allowable_values}."
152
187
  end
@@ -159,7 +194,11 @@ module Repull
159
194
  return true if self.equal?(o)
160
195
  self.class == o.class &&
161
196
  action == o.action &&
197
+ listing_id == o.listing_id &&
162
198
  property_id == o.property_id &&
199
+ room_id == o.room_id &&
200
+ legal_entity_id == o.legal_entity_id &&
201
+ legal_entity == o.legal_entity &&
163
202
  leid == o.leid &&
164
203
  contacts == o.contacts
165
204
  end
@@ -173,7 +212,7 @@ module Repull
173
212
  # Calculates hash code according to all attributes.
174
213
  # @return [Integer] Hash code
175
214
  def hash
176
- [action, property_id, leid, contacts].hash
215
+ [action, listing_id, property_id, room_id, legal_entity_id, legal_entity, leid, contacts].hash
177
216
  end
178
217
 
179
218
  # Builds the object from hash