calendlyr 0.10.0 → 1.0.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.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +38 -0
  4. data/README.md +188 -4
  5. data/calendlyr.gemspec +4 -1
  6. data/docs/resources/activity_log/list_activity_log_entries.md +2 -1
  7. data/docs/resources/availabilities/user_availability_schedule.md +3 -2
  8. data/docs/resources/availabilities/user_busy_time.md +3 -2
  9. data/docs/resources/event_types/available_time.md +25 -0
  10. data/docs/resources/event_types/event_type.md +3 -2
  11. data/docs/resources/event_types/membership.md +28 -0
  12. data/docs/resources/events/event.md +4 -3
  13. data/docs/resources/events/invitee.md +2 -1
  14. data/docs/resources/events/invitee_no_show.md +2 -1
  15. data/docs/resources/groups/group.md +2 -1
  16. data/docs/resources/organizations/membership.md +3 -2
  17. data/docs/resources/organizations/organization.md +13 -0
  18. data/docs/resources/outgoing_communications/outgoing_communication.md +28 -0
  19. data/docs/resources/routing_forms/routing_form.md +2 -1
  20. data/docs/resources/routing_forms/submission.md +2 -1
  21. data/docs/resources/scheduling_links/scheduling_link.md +26 -0
  22. data/docs/resources/share.md +2 -1
  23. data/docs/resources/webhooks/invitee_payload.md +10 -16
  24. data/docs/resources/webhooks/payload.md +32 -3
  25. data/docs/resources/webhooks/sample.md +23 -0
  26. data/docs/resources/webhooks/subscription.md +10 -6
  27. data/lib/calendlyr/client.rb +3 -2
  28. data/lib/calendlyr/collection.rb +25 -7
  29. data/lib/calendlyr/configuration.rb +24 -0
  30. data/lib/calendlyr/error.rb +52 -5
  31. data/lib/calendlyr/object.rb +10 -1
  32. data/lib/calendlyr/objects/event_types/available_time.rb +8 -0
  33. data/lib/calendlyr/objects/event_types/membership.rb +8 -0
  34. data/lib/calendlyr/objects/outgoing_communication.rb +6 -0
  35. data/lib/calendlyr/objects/scheduling_link.rb +6 -0
  36. data/lib/calendlyr/objects/webhooks/payload.rb +30 -0
  37. data/lib/calendlyr/resource.rb +55 -10
  38. data/lib/calendlyr/resources/availability.rb +14 -2
  39. data/lib/calendlyr/resources/event_types.rb +33 -2
  40. data/lib/calendlyr/resources/events.rb +15 -2
  41. data/lib/calendlyr/resources/groups.rb +14 -2
  42. data/lib/calendlyr/resources/locations.rb +6 -1
  43. data/lib/calendlyr/resources/organizations.rb +25 -3
  44. data/lib/calendlyr/resources/outgoing_communications.rb +16 -0
  45. data/lib/calendlyr/resources/routing_forms.rb +14 -2
  46. data/lib/calendlyr/resources/scheduling_links.rb +11 -0
  47. data/lib/calendlyr/resources/shares.rb +1 -0
  48. data/lib/calendlyr/resources/webhooks.rb +13 -1
  49. data/lib/calendlyr/version.rb +1 -1
  50. data/lib/calendlyr/webhook.rb +105 -0
  51. data/lib/calendlyr.rb +51 -0
  52. data/logos/calendlyr.png +0 -0
  53. data/logos/calendlyr_bg_white.png +0 -0
  54. data/test/calendlyr/client_test.rb +15 -0
  55. data/test/calendlyr/collection_test.rb +107 -3
  56. data/test/calendlyr/configuration_test.rb +157 -0
  57. data/test/calendlyr/object_test.rb +50 -0
  58. data/test/calendlyr/objects/webhooks/payload_test.rb +15 -0
  59. data/test/calendlyr/resource_test.rb +287 -1
  60. data/test/calendlyr/resources/availabilities/user_busy_times_test.rb +26 -0
  61. data/test/calendlyr/resources/availabilities/user_schedules_test.rb +25 -0
  62. data/test/calendlyr/resources/event_types_test.rb +84 -0
  63. data/test/calendlyr/resources/events_test.rb +68 -0
  64. data/test/calendlyr/resources/groups_test.rb +54 -0
  65. data/test/calendlyr/resources/locations_test.rb +12 -0
  66. data/test/calendlyr/resources/organizations_test.rb +96 -2
  67. data/test/calendlyr/resources/outgoing_communications_test.rb +42 -0
  68. data/test/calendlyr/resources/routing_forms_test.rb +57 -0
  69. data/test/calendlyr/resources/scheduling_links_test.rb +40 -0
  70. data/test/calendlyr/resources/shares_test.rb +15 -0
  71. data/test/calendlyr/resources/webhooks_test.rb +66 -0
  72. data/test/calendlyr/webhook_test.rb +292 -0
  73. data/test/fixtures/activity_log/list_page2.json +30 -0
  74. data/test/fixtures/event_invitees/list_page2.json +35 -0
  75. data/test/fixtures/event_type_available_times/list.json +10 -0
  76. data/test/fixtures/event_type_memberships/list.json +43 -0
  77. data/test/fixtures/event_type_memberships/list_page2.json +33 -0
  78. data/test/fixtures/event_types/list_page2.json +37 -0
  79. data/test/fixtures/events/list_page2.json +29 -0
  80. data/test/fixtures/group_relationships/list_page2.json +35 -0
  81. data/test/fixtures/groups/list_page2.json +16 -0
  82. data/test/fixtures/locations/list_page2.json +16 -0
  83. data/test/fixtures/organizations/list_invitations_page2.json +18 -0
  84. data/test/fixtures/organizations/list_memberships_page2.json +26 -0
  85. data/test/fixtures/organizations/retrieve.json +11 -0
  86. data/test/fixtures/outgoing_communications/list.json +24 -0
  87. data/test/fixtures/outgoing_communications/list_page2.json +21 -0
  88. data/test/fixtures/routing_forms/list_page2.json +17 -0
  89. data/test/fixtures/routing_forms/list_routing_form_submission_page2.json +29 -0
  90. data/test/fixtures/scheduling_links/create.json +7 -0
  91. data/test/fixtures/user_availability_schedules/list_page1.json +16 -0
  92. data/test/fixtures/user_availability_schedules/list_page2.json +16 -0
  93. data/test/fixtures/user_busy_times/list_page1.json +18 -0
  94. data/test/fixtures/user_busy_times/list_page2.json +13 -0
  95. data/test/fixtures/webhooks/list_page2.json +23 -0
  96. data/test/fixtures/webhooks/sample.json +66 -0
  97. data/test/test_helper.rb +10 -0
  98. metadata +64 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 201abcc548a1e1a59386fefc1416cf05e91dc28c83e7cc057c7a5c207eef4f1d
4
- data.tar.gz: 94aee4dc1740d031eea51a32309225340178ee2ec59fdcf4987b7413eacb8bd2
3
+ metadata.gz: ca7216a6bc050ebad873636af0bb988c8841d7828fe6cebc0a0497947c0a9dc7
4
+ data.tar.gz: e36f58a64ad9277fdd38bb44fe69aeb4e7d84fe10fb6b9274a474bb609406340
5
5
  SHA512:
6
- metadata.gz: 7837b5d47c2e989c5d2221e344269713427efd21639a8b5a23ff7a85e0a8baff2eebfa028469c1e17d1bb353eac2dfb5346747de63653d564c312d03cb3dd77a
7
- data.tar.gz: d13ae93dd51ac622139c19c2bbd9e328a8dd2275c5a1fcce3b825a08291b33139831ddae74256a1a39a2d25f9d541bf5b6656ab8b293b54d717302812f0c7ec5
6
+ metadata.gz: 36c382f6725f7fdc22acfd11bee564dbcd5c448df265f49d07c450e074ab23c3a1f165970e4f5f925feebbcfbd7f3dae827ecc144a5ca0fb0c0524f9dcaa2d3c
7
+ data.tar.gz: 2e60e92afb872bb9590cda26e3208ed1bed7f069ea644a55738c5c6284447241c0c90358ff2a19d1e3c1149a790db2e69648c8ed945cd9ed6e4a9ed36526d575
data/.gitignore CHANGED
@@ -24,3 +24,4 @@ vendor
24
24
  .idea/
25
25
  bin/my_console
26
26
  .atl/*
27
+ .DS_Store
data/CHANGELOG.md CHANGED
@@ -2,9 +2,46 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.0.0]
6
+
7
+ ### Added
8
+ * Auto-pagination via `Enumerator::Lazy` — `collection.auto_paginate` returns a lazy enumerator that traverses all pages on demand without pre-fetching. Compose with `.take(n)`, `.select { }`, `.map { }`, etc.
9
+ * `#next_page` method on `Collection` — returns the next page as a new `Collection`, or `nil` when there are no more pages.
10
+ * `#next_page_url` attr_reader on `Collection` — exposes the raw next-page URL string.
11
+ * `list_all` convenience methods on every resource with list endpoints:
12
+ - `client.events.list_all`, `client.events.list_all_invitees`
13
+ - `client.event_types.list_all`, `client.event_types.list_all_availability_schedules`, `client.event_types.list_all_memberships`
14
+ - `client.organizations.list_all_memberships`, `client.organizations.list_all_invitations`, `client.organizations.list_all_activity_log`
15
+ - `client.groups.list_all`, `client.groups.list_all_relationships`
16
+ - `client.availability.list_all_user_busy_times`, `client.availability.list_all_user_schedules`
17
+ - `client.routing_forms.list_all`, `client.routing_forms.list_all_submissions`
18
+ - `client.webhooks.list_all`
19
+ - `client.locations.list_all`
20
+ - `client.outgoing_communications.list_all`
21
+ * `GET /event_type_available_times` — `client.event_types.list_available_times(event_type:, start_time:, end_time:)` returns a `Collection` of `EventTypes::AvailableTime` objects. No pagination (not supported by this endpoint).
22
+ * `GET /event_type_memberships` — `client.event_types.list_memberships(event_type:)` and `list_all_memberships`. Returns `EventTypes::Membership` objects.
23
+ * `POST /scheduling_links` — `client.scheduling_links.create(owner:, owner_type:, max_event_count:)` returns a `SchedulingLink` object. Bare UUID expansion for `owner`.
24
+ * `GET /organizations/{uuid}` — `client.organizations.retrieve(uuid:)` returns an `Organization` object.
25
+ * `GET /outgoing_communications` — `client.outgoing_communications.list(organization:)` and `list_all`. Returns `OutgoingCommunication` objects.
26
+ * `GET /sample_webhook_data` — `client.webhooks.sample(event:, organization:, scope:)` returns the raw response hash (shape varies by event type).
27
+
28
+ ### Changed — Breaking
29
+ * **`Collection#next_page`** (attr_reader returning the raw URL string) is now **`Collection#next_page_url`**. If you were reading the raw next-page URL via `collection.next_page`, change to `collection.next_page_url`.
30
+ * **`Collection#next_page`** is now a **method** that returns the next `Collection` object (or `nil`), not the raw URL string.
31
+
32
+ ### Notes
33
+ * Version 1.0.0 signals complete Calendly API v2 coverage. All documented endpoints are now implemented.
34
+ * Changes originally prepared for an unreleased `0.11.0` were shipped as part of `1.0.0`.
35
+
36
+ [1.0.0]: https://github.com/araluce/calendlyr/compare/v0.10.0...v1.0.0
37
+
5
38
  ## [0.10.0]
6
39
 
7
40
  ### Added
41
+ * `Calendlyr::Webhook.verify!`, `valid?`, and `parse` — verify signed webhook payloads with HMAC-SHA256, optional timestamp tolerance, and typed payload parsing
42
+ * `Calendlyr.configure`, `Calendlyr.configuration`, `Calendlyr.client`, and `Calendlyr.reset!` — module-level global configuration and default client support with token/timeout settings
43
+ * Optional request/response logging via `Client.new(logger:)` or `Calendlyr.configure { |c| c.logger = ... }` — INFO for method/URL/status/duration, DEBUG for response body (truncated), WARN for retries, ERROR for API errors. Authorization header is never logged.
44
+ * `Object#to_json` — Serialize any API object to JSON. Works with `JSON.generate`, nested objects, and arrays. The internal `client` reference is automatically excluded from serialization.
8
45
  * `client.data_compliance.delete_scheduled_event_data` — Remove scheduled events data within a time range (`POST /data_compliance/deletion/events`)
9
46
  * `put_request` support in `Resource` base class for PUT HTTP verb
10
47
  * `Collection` now includes `Enumerable` — use `each`, `map`, `select` directly on collections
@@ -17,6 +54,7 @@ All notable changes to this project will be documented in this file.
17
54
  * Requires Ruby >= 3.2.0 (dropped support for Ruby 2.4–3.1)
18
55
 
19
56
  ### Fixed
57
+ * Error messages now include request context (`GET /path`) and expose structured attributes on `Calendlyr::Error` (`status`, `http_method`, `path`, `response_body`) for easier debugging
20
58
  * **Security:** Removed `OpenSSL::SSL::VERIFY_NONE` — SSL connections now properly verify certificates
21
59
  * **Security:** Bare `rescue` replaced with `rescue JSON::ParserError` — non-JSON errors are no longer silently swallowed
22
60
  * `Invitee#cancel` now correctly uses the event UUID instead of the invitee UUID
data/README.md CHANGED
@@ -5,10 +5,14 @@
5
5
 
6
6
  # Calendlyr
7
7
 
8
- The simplest way to interact with [Calendly's API v2](https://developer.calendly.com/api-docs) in Ruby. No dependencies, no complexity — just a Personal Access Token and you're good to go.
8
+ ![Calendlyr logo](logos/calendlyr_bg_white.png)
9
+
10
+ The simplest way to interact with [Calendly's API v2](https://developer.calendly.com/api-docs) in Ruby. No runtime dependencies, no ceremony — just a Personal Access Token and you're good to go.
9
11
 
10
12
  ## Installation
11
13
 
14
+ Calendlyr requires **Ruby >= 3.2.0**.
15
+
12
16
  Add to your Gemfile:
13
17
 
14
18
  ```ruby
@@ -22,8 +26,8 @@ Then run `bundle install`. That's it.
22
26
  ```ruby
23
27
  client = Calendlyr::Client.new(token: ENV["CALENDLY_TOKEN"])
24
28
 
25
- # List your scheduled events
26
- events = client.events.list(user: "https://api.calendly.com/users/YOUR_USER_UUID")
29
+ # List your scheduled events — just pass the UUID, no full URI needed
30
+ events = client.events.list(user: "YOUR_USER_UUID")
27
31
  events.data
28
32
  #=> [#<Calendlyr::Event>, #<Calendlyr::Event>, ...]
29
33
 
@@ -38,15 +42,195 @@ invitees = client.events.list_invitees(uuid: event.uuid)
38
42
  invitees.data.first.email #=> "john@example.com"
39
43
  ```
40
44
 
45
+ ## Global configuration
46
+
47
+ For single-tenant apps, you can configure `Calendlyr` once and reuse a default client:
48
+
49
+ ```ruby
50
+ Calendlyr.configure do |config|
51
+ config.token = ENV.fetch("CALENDLY_TOKEN")
52
+ config.open_timeout = 5
53
+ config.read_timeout = 15
54
+ end
55
+
56
+ client = Calendlyr.client
57
+ events = client.events.list(user: "YOUR_USER_UUID")
58
+ ```
59
+
60
+ `Calendlyr.client` memoizes a client instance and rebuilds it if token or timeout values change.
61
+
62
+ ### Optional request/response logging
63
+
64
+ Calendlyr can emit request lifecycle logs with any logger-like object that responds to `info`, `debug`, `warn`, and `error`. Logging is opt-in, and the gem does not ship a logger implementation for you.
65
+
66
+ ```ruby
67
+ require "logger"
68
+
69
+ client = Calendlyr::Client.new(token: ENV["CALENDLY_TOKEN"], logger: Logger.new($stdout))
70
+ ```
71
+
72
+ `Logger` is just an example. You can pass any object that responds to `info`, `debug`, `warn`, and `error`.
73
+
74
+ If you're on Ruby 4 and want to use Ruby's `Logger`, make sure your application includes the `logger` gem.
75
+
76
+ Or configure it globally:
77
+
78
+ ```ruby
79
+ Calendlyr.configure do |config|
80
+ config.token = ENV.fetch("CALENDLY_TOKEN")
81
+ config.logger = Logger.new($stdout)
82
+ end
83
+ ```
84
+
85
+ In Rails, this is typically configured in an initializer:
86
+
87
+ ```ruby
88
+ # config/initializers/calendlyr.rb
89
+ Calendlyr.configure do |config|
90
+ config.token = ENV.fetch("CALENDLY_TOKEN")
91
+ end
92
+ ```
93
+
94
+ > [!IMPORTANT]
95
+ > `Calendlyr.client` is module-global and **not thread-safe for multi-tenant usage**.
96
+ > If your app serves multiple tenants or uses per-request credentials, use `Calendlyr::Client.new(token:)` per request.
97
+
98
+ ### Bare UUIDs
99
+
100
+ Most methods that take a Calendly resource reference (like `user:`, `organization:`, `event_type:`, or `owner:`) accept both bare UUIDs and full URIs. When supported, the gem expands bare UUIDs automatically:
101
+
102
+ ```ruby
103
+ # Both are equivalent:
104
+ client.events.list(user: "YOUR_USER_UUID")
105
+ client.events.list(user: "https://api.calendly.com/users/YOUR_USER_UUID")
106
+ ```
107
+
41
108
  The gem mirrors the Calendly API closely, so converting API examples into gem code is straightforward. Responses are wrapped in Ruby objects with dot-access for every field.
42
109
 
110
+ > **Note:** A few endpoints still expect a full Calendly URI for specific parameters. When that matters, the resource docs call it out explicitly.
111
+
112
+ ### Webhook signature verification
113
+
114
+ `Calendlyr::Webhook` (singular) verifies signed webhook payloads. This is separate from `client.webhooks` / `Calendlyr::Webhooks` (plural), which are API resources for managing webhook subscriptions.
115
+
116
+ - Calendly sends the signature in the `Calendly-Webhook-Signature` HTTP header.
117
+ - In Rack/Rails, that header is available as `HTTP_CALENDLY_WEBHOOK_SIGNATURE`.
118
+ - `verify!` raises on invalid signature/timestamp; `valid?` returns `true`/`false`; `parse` verifies first, then JSON-parses and wraps the payload.
119
+
120
+ ```ruby
121
+ payload = request.body.read
122
+ signature_header = request.get_header("HTTP_CALENDLY_WEBHOOK_SIGNATURE")
123
+ signing_key = ENV.fetch("CALENDLY_WEBHOOK_SIGNING_KEY")
124
+
125
+ if Calendlyr::Webhook.valid?(payload: payload, signature_header: signature_header, signing_key: signing_key)
126
+ webhook = Calendlyr::Webhook.parse(
127
+ payload: payload,
128
+ signature_header: signature_header,
129
+ signing_key: signing_key
130
+ )
131
+
132
+ webhook.event #=> "invitee.created"
133
+ webhook.payload #=> Calendlyr::Webhooks::InviteePayload (for invitee.* events)
134
+ #=> Calendlyr::Object (for other/unknown events)
135
+ end
136
+ ```
137
+
138
+ ### JSON serialization
139
+
140
+ All API objects support `#to_json` for easy serialization (caching, logging, API proxying):
141
+
142
+ ```ruby
143
+ event = client.events.retrieve(uuid: "ABC123")
144
+
145
+ event.to_json
146
+ #=> '{"uri":"https://api.calendly.com/scheduled_events/ABC123","name":"30 Minute Meeting",...}'
147
+
148
+ # Works with JSON.generate and nested objects
149
+ JSON.generate(event)
150
+
151
+ # Round-trip: parse back into an Object
152
+ parsed = Calendlyr::Object.new(JSON.parse(event.to_json))
153
+ parsed.name #=> "30 Minute Meeting"
154
+ ```
155
+
156
+ > **Note:** `#to_json` and `#to_h` exclude the internal `client` reference — only API data is serialized.
157
+
158
+ ### Error context
159
+
160
+ API errors now include the HTTP method and path in the message, and expose structured attributes for debugging:
161
+
162
+ ```ruby
163
+ begin
164
+ client.events.retrieve(uuid: "INVALID_UUID")
165
+ rescue Calendlyr::NotFound => error
166
+ error.message #=> "[Error 404] GET /scheduled_events/INVALID_UUID — Not Found. The resource you requested does not exist."
167
+ error.status #=> 404
168
+ error.http_method #=> "GET"
169
+ error.path #=> "/scheduled_events/INVALID_UUID"
170
+ error.response_body #=> { "title" => "Not Found", "message" => "..." }
171
+ end
172
+ ```
173
+
174
+ This makes debugging failed requests much easier without changing existing `rescue Calendlyr::Error` patterns.
175
+
176
+ ## Auto-pagination
177
+
178
+ Calendlyr supports lazy auto-pagination for paginated collection endpoints. There are two ways to consume paginated results:
179
+
180
+ ### `list_all` — Eager, returns a flat Array
181
+
182
+ The simplest option. Fetches every page and returns all items as an Array.
183
+
184
+ ```ruby
185
+ # Get all events across all pages (e.g., hundreds of events)
186
+ events = client.events.list_all(organization: "YOUR_ORG_UUID")
187
+ events #=> [#<Calendlyr::Event>, #<Calendlyr::Event>, ...]
188
+
189
+ # Same pattern works for every resource with a list method:
190
+ client.event_types.list_all(organization: "YOUR_ORG_UUID")
191
+ client.webhooks.list_all(organization: "YOUR_ORG_UUID", scope: "organization")
192
+ client.organizations.list_all_memberships(organization: "YOUR_ORG_UUID")
193
+ client.organizations.list_all_invitations(uuid: "YOUR_ORG_UUID")
194
+ client.organizations.list_all_activity_log(organization: "YOUR_ORG_UUID")
195
+ client.groups.list_all(organization: "YOUR_ORG_UUID")
196
+ client.groups.list_all_relationships(organization: "YOUR_ORG_UUID")
197
+ client.routing_forms.list_all(organization: "YOUR_ORG_UUID")
198
+ client.routing_forms.list_all_submissions(form: "YOUR_FORM_UUID")
199
+ client.availability.list_all_user_busy_times(user: "YOUR_USER_UUID", start_time: "...", end_time: "...")
200
+ client.availability.list_all_user_schedules(user: "YOUR_USER_UUID")
201
+ client.locations.list_all
202
+ client.event_types.list_all_memberships(event_type: "YOUR_EVENT_TYPE_UUID")
203
+ client.outgoing_communications.list_all(organization: "YOUR_ORG_UUID")
204
+ ```
205
+
206
+ ### `auto_paginate` — Lazy Enumerator
207
+
208
+ Returns an `Enumerator::Lazy` that fetches pages on demand. Pages are only requested as you consume items, so you can stop early without fetching all pages.
209
+
210
+ ```ruby
211
+ collection = client.events.list(organization: "YOUR_ORG_UUID")
212
+
213
+ # Take only the first 50 events — fetches only as many pages as needed
214
+ first_50 = collection.auto_paginate.take(50)
215
+
216
+ # Filter lazily — stops fetching once the condition is met
217
+ active = collection.auto_paginate.select { |e| e.status == "active" }.first(10)
218
+
219
+ # Consume all items lazily
220
+ collection.auto_paginate.each do |event|
221
+ puts event.name
222
+ end
223
+ ```
224
+
43
225
  ## Documentation
44
226
 
45
227
  For the full list of available resources and methods, check out the [API Reference](docs/resources/).
46
228
 
229
+ The docs in this repository focus on the Ruby wrapper API. For request/response schemas and endpoint-level behavior, use the official Calendly API docs linked from each resource page.
230
+
47
231
  ## Contributing
48
232
 
49
- 1. Fork it ( https://github.com/araluce/calendlyr/fork )
233
+ 1. [Fork it](https://github.com/araluce/calendlyr/fork)
50
234
  2. Create your feature branch (`git checkout -b my-new-feature`)
51
235
  3. Commit your changes (`git commit -am 'Add some feature'`)
52
236
  4. Push to the branch (`git push origin my-new-feature`)
data/calendlyr.gemspec CHANGED
@@ -7,16 +7,19 @@ Gem::Specification.new do |spec|
7
7
  spec.email = ["araluce11@gmail.com"]
8
8
 
9
9
  spec.summary = "Ruby bindings for Calendly API."
10
- spec.description = "Ruby bindings for Calendly API. Calendly APIs can be found here: https://calendly.stoplight.io/docs/api-docs/"
10
+ spec.description = "Ruby bindings for Calendly API v2. Full docs: https://developer.calendly.com/api-docs/"
11
11
  spec.homepage = "https://github.com/araluce/calendlyr"
12
12
  spec.license = "MIT"
13
13
  spec.required_ruby_version = Gem::Requirement.new(">= 3.2.0")
14
14
  spec.metadata["homepage_uri"] = spec.homepage
15
+ spec.metadata["changelog_uri"] = "https://github.com/araluce/calendlyr/blob/master/CHANGELOG.md"
16
+ spec.metadata["documentation_uri"] = "https://github.com/araluce/calendlyr/tree/master/docs"
15
17
 
16
18
  spec.files = `git ls-files -z`.split("\x0")
17
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
20
  spec.require_paths = ["lib"]
19
21
 
22
+ spec.add_development_dependency "logger", "~> 1.6"
20
23
  spec.add_development_dependency "minitest", "~> 5.25"
21
24
  spec.add_development_dependency "rake", "~> 13.2"
22
25
  spec.add_development_dependency "simplecov", "~> 0.21"
@@ -13,7 +13,8 @@ Visit official [API Doc](https://developer.calendly.com/api-docs/d37c7f031f339-l
13
13
  For the example bellow we will use only required parameters, but you can use any other parameter as well.
14
14
 
15
15
  ```ruby
16
- client.organizations.activity_log(organization: `organization_uri`)
16
+ # organization: accepts a bare UUID or full Calendly URI
17
+ client.organizations.activity_log(organization: "ORG_UUID")
17
18
  #=> #<Calendlyr::Collection @data=[#<Calendlyr::ActivityLog, ...], @client=#<Calendlyr::Client>>
18
19
  ```
19
20
 
@@ -20,8 +20,9 @@ client.availabilities.retrieve_user_schedule(uuid: @uuid)
20
20
  Return the availability schedules of the given user. See [official API doc](https://developer.calendly.com/api-docs/8098de44af94c-list-user-availability-schedules)
21
21
 
22
22
  ```ruby
23
- client.availabilities.list_user_schedules(user: @user)
24
- #=> #<Calendlyr::Collection @data=[#<Calendlyr::Availabilities::UserSchedule uri="https://api.calendly.com/user_availability_schedule/abc123", default=true, name="Working Hours", user="https://api.calendly.com/users/abc123", timezone="America/New_York", rules=[#<OpenStruct type="wday", intervals=[#<OpenStruct from="08:30", to="09:30">], wday="sunday", date="2022-12-31">], client=#<Calendlyr::Client>, uuid="abc123">, #<Calendlyr::Availabilities::UserSchedule uri="https://api.calendly.com/user_availability_schedule/abc456", default=false, name="Evening Hours", user="https://api.calendly.com/users/abc123", timezone="America/New_York", rules=[#<OpenStruct type="wday", intervals=[#<OpenStruct from="08:30", to="17:00">], wday="monday">, #<OpenStruct type="wday", intervals=[#<OpenStruct from="08:30", to="17:00">], wday="tuesday">, #<OpenStruct type="wday", intervals=[], wday="wednesday">, #<OpenStruct type="wday", intervals=[#<OpenStruct from="08:30", to="17:00">], wday="thursday">, #<OpenStruct type="wday", intervals=[#<OpenStruct from="08:30", to="17:00">], wday="friday">, #<OpenStruct type="wday", intervals=[], wday="saturday">, #<OpenStruct type="date", intervals=[#<OpenStruct from="08:30", to="09:30">], date="2028-12-31">], client=#<Calendlyr::Client>, uuid="abc456">], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
23
+ # user: accepts a bare UUID or full Calendly URI
24
+ client.availabilities.list_user_schedules(user: "USER_UUID")
25
+ #=> #<Calendlyr::Collection @data=[#<Calendlyr::Availabilities::UserSchedule>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
25
26
  ```
26
27
 
27
28
  #### List on me
@@ -14,8 +14,9 @@ Date range can be no greater than 1 week (7 days).
14
14
 
15
15
  For the example bellow we will use only required parameters, but you can use any other parameter as well.
16
16
  ```ruby
17
- client.availabilities.list_user_busy_times(user: `user_uri`, start_time: `start_time`, end_time: `end_time`)
18
- #=> #<Calendlyr::Collection @data=[#<Calendlyr::Availabilities::UserBusyTime type="calendly", start_time="2020-01-02T20:00:00.000000Z", end_time="2020-01-02T20:30:00.000000Z", buffered_start_time="2020-01-02T19:30:00.000000Z", buffered_end_time="2020-01-02T21:00:00.000000Z", event=#<OpenStruct uri="https://api.calendly.com/scheduled_events/abc123">, client=#<Calendlyr::Client>, uuid=nil>, #<Calendlyr::UserBusyTime type="calendly", start_time="2020-01-05T20:00:00.000000Z", end_time="2020-01-05T20:30:00.000000Z", buffered_start_time="2020-01-05T19:30:00.000000Z", buffered_end_time="2020-01-05T21:00:00.000000Z", event=#<OpenStruct uri="https://api.calendly.com/scheduled_events/abc12345">, client=#<Calendlyr::Client>, uuid=nil>, #<Calendlyr::UserBusyTime type="external", start_time="2020-01-07T20:00:00.000000Z", end_time="2020-01-07T20:30:00.000000Z", client=#<Calendlyr::Client>, uuid=nil>], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
17
+ # user: accepts a bare UUID or full Calendly URI
18
+ client.availabilities.list_user_busy_times(user: "USER_UUID", start_time: start_time, end_time: end_time)
19
+ #=> #<Calendlyr::Collection @data=[#<Calendlyr::Availabilities::UserBusyTime>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
19
20
  ```
20
21
 
21
22
  ## Object methods
@@ -0,0 +1,25 @@
1
+ # Available Time (`Calendlyr::EventTypes::AvailableTime`)
2
+
3
+ Available time for a specific Event Type in a given time window.
4
+
5
+ Visit official [API Doc](https://developer.calendly.com/api-docs)
6
+
7
+ ## Client requests
8
+
9
+ ### List Available Times
10
+
11
+ Returns available time slots for an Event Type between `start_time` and `end_time`.
12
+
13
+ Visit official [API Doc](https://developer.calendly.com/api-docs)
14
+
15
+ ```ruby
16
+ # event_type: accepts a bare UUID or full Calendly URI
17
+ client.event_types.list_available_times(
18
+ event_type: "EVENT_TYPE_UUID",
19
+ start_time: "2026-04-07T09:00:00Z",
20
+ end_time: "2026-04-07T18:00:00Z"
21
+ )
22
+ #=> #<Calendlyr::Collection @data=[#<Calendlyr::EventTypes::AvailableTime>, ...], ...>
23
+ ```
24
+
25
+ This endpoint does not support pagination.
@@ -24,10 +24,11 @@ Visit official [API Doc](https://developer.calendly.com/api-docs/25a4ece03c1bc-l
24
24
 
25
25
  For the examples bellow we will use only required parameters, but you can use any other parameter as well.
26
26
  ```ruby
27
- client.event_types.list(user: @user)
27
+ # user: and organization: accept bare UUIDs or full Calendly URIs
28
+ client.event_types.list(user: "USER_UUID")
28
29
  #=> #<Calendlyr::Collection @data=[#<Calendlyr::EventType>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
29
30
 
30
- client.event_types.list(organization: @organization)
31
+ client.event_types.list(organization: "ORG_UUID")
31
32
  #=> #<Calendlyr::Collection @data=[#<Calendlyr::EventType>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
32
33
  ```
33
34
 
@@ -0,0 +1,28 @@
1
+ # Event Type Membership (`Calendlyr::EventTypes::Membership`)
2
+
3
+ Membership object for an Event Type.
4
+
5
+ Visit official [API Doc](https://developer.calendly.com/api-docs)
6
+
7
+ ## Client requests
8
+
9
+ ### List
10
+
11
+ Returns memberships for a specified Event Type.
12
+
13
+ Visit official [API Doc](https://developer.calendly.com/api-docs)
14
+
15
+ ```ruby
16
+ # event_type: accepts a bare UUID or full Calendly URI
17
+ client.event_types.list_memberships(event_type: "EVENT_TYPE_UUID")
18
+ #=> #<Calendlyr::Collection @data=[#<Calendlyr::EventTypes::Membership>, ...], ...>
19
+ ```
20
+
21
+ ### List All
22
+
23
+ Fetches every membership page and returns a flat Array.
24
+
25
+ ```ruby
26
+ client.event_types.list_all_memberships(event_type: "EVENT_TYPE_UUID")
27
+ #=> [#<Calendlyr::EventTypes::Membership>, ...]
28
+ ```
@@ -24,13 +24,14 @@ Returns a list of Events.
24
24
  Visit official [API Doc](https://developer.calendly.com/api-docs/2d5ed9bbd2952-list-events)
25
25
 
26
26
  ```ruby
27
- client.events.list(organization: organization, user: user)
27
+ # You can pass bare UUIDs or full Calendly URIs for user:, organization:, and group:
28
+ client.events.list(organization: "ORG_UUID", user: "USER_UUID")
28
29
  #=> #<Calendlyr::Collection @data=[#<Calendlyr::Event>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
29
30
 
30
- client.events.list(organization: organization, group: group)
31
+ client.events.list(organization: "ORG_UUID", group: group)
31
32
  #=> #<Calendlyr::Collection @data=[#<Calendlyr::Event>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
32
33
 
33
- client.events.list(user: user)
34
+ client.events.list(user: "USER_UUID")
34
35
  #=> #<Calendlyr::Collection @data=[#<Calendlyr::Event>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
35
36
  ```
36
37
 
@@ -32,8 +32,9 @@ client.events.list_invitees(uuid: uuid)
32
32
  Creates and schedules an Invitee.
33
33
 
34
34
  ```ruby
35
+ # event_type: accepts a bare UUID or full Calendly URI
35
36
  client.events.create_invitee(
36
- event_type: event_type_uri,
37
+ event_type: "EVENT_TYPE_UUID",
37
38
  start_time: "2019-08-07T06:05:04.321123Z",
38
39
  invitee: {
39
40
  name: "John Doe",
@@ -24,7 +24,8 @@ Marks an Invitee as a No Show.
24
24
  Visit official [API Doc](https://developer.calendly.com/api-docs/cebd8c3170790-create-invitee-no-show)
25
25
 
26
26
  ```ruby
27
- client.events.create_invitee_no_show(invitee: invitee_uri)
27
+ # invitee: accepts a bare UUID or full Calendly URI
28
+ client.events.create_invitee_no_show(invitee: "INVITEE_UUID")
28
29
  #=> #<Calendlyr::Events::InviteeNoShow>
29
30
  ```
30
31
 
@@ -24,7 +24,8 @@ Returns a list of groups.
24
24
  Visit official [API Doc](https://developer.calendly.com/api-docs/6rb6dtdln74sy-list-groups)
25
25
 
26
26
  ```ruby
27
- client.groups.list(organization: organization_uri)
27
+ # organization: accepts a bare UUID or full Calendly URI
28
+ client.groups.list(organization: "ORG_UUID")
28
29
  #=> #<Calendlyr::Collection @data=[#<Calendlyr::Group>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
29
30
  ```
30
31
 
@@ -26,10 +26,11 @@ Visit official [API Doc](https://developer.calendly.com/api-docs/eaed2e61a6bc3-l
26
26
  For the example bellow we will use only required parameters, but you can use any other parameter as well.
27
27
 
28
28
  ```ruby
29
- client.organizations.list_memberships(user: user)
29
+ # user: and organization: accept bare UUIDs or full Calendly URIs
30
+ client.organizations.list_memberships(user: "USER_UUID")
30
31
  #=> #<Calendlyr::Collection @data=[#<Calendlyr::Organizations::Membership>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
31
32
 
32
- client.organizations.list_memberships(organization: organization)
33
+ client.organizations.list_memberships(organization: "ORG_UUID")
33
34
  #=> #<Calendlyr::Collection @data=[#<Calendlyr::Organizations::Membership>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
34
35
  ```
35
36
 
@@ -13,6 +13,19 @@ organization = client.organization
13
13
  #=> #<Calendlyr::Organization>
14
14
  ```
15
15
 
16
+ ## Client requests
17
+
18
+ ### Retrieve
19
+
20
+ Returns information about a specified Organization.
21
+
22
+ Visit official [API Doc](https://developer.calendly.com/api-docs)
23
+
24
+ ```ruby
25
+ client.organizations.retrieve(uuid: "ORG_UUID")
26
+ #=> #<Calendlyr::Organization>
27
+ ```
28
+
16
29
  ## Object methods
17
30
 
18
31
  ### Activity Logs
@@ -0,0 +1,28 @@
1
+ # Outgoing Communication (`Calendlyr::OutgoingCommunication`)
2
+
3
+ Outgoing communication object.
4
+
5
+ Visit official [API Doc](https://developer.calendly.com/api-docs)
6
+
7
+ ## Client requests
8
+
9
+ ### List
10
+
11
+ Returns outgoing communications for a specified Organization.
12
+
13
+ Visit official [API Doc](https://developer.calendly.com/api-docs)
14
+
15
+ ```ruby
16
+ # organization: accepts a bare UUID or full Calendly URI
17
+ client.outgoing_communications.list(organization: "ORG_UUID")
18
+ #=> #<Calendlyr::Collection @data=[#<Calendlyr::OutgoingCommunication>, ...], ...>
19
+ ```
20
+
21
+ ### List All
22
+
23
+ Fetches every page and returns a flat Array.
24
+
25
+ ```ruby
26
+ client.outgoing_communications.list_all(organization: "ORG_UUID")
27
+ #=> [#<Calendlyr::OutgoingCommunication>, ...]
28
+ ```
@@ -26,7 +26,8 @@ Visit official [API Doc](https://developer.calendly.com/api-docs/9fe7334bec6ad-l
26
26
  For the example bellow we will use only required parameters, but you can use any other parameter as well.
27
27
 
28
28
  ```ruby
29
- client.routing_forms.list(organization: organization_uri)
29
+ # organization: accepts a bare UUID or full Calendly URI
30
+ client.routing_forms.list(organization: "ORG_UUID")
30
31
  #=> #<Calendlyr::Collection @data=[#<Calendlyr::RoutingForm>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
31
32
  ```
32
33
 
@@ -26,7 +26,8 @@ Visit official [API Doc](https://developer.calendly.com/api-docs/17db5cb915a57-l
26
26
  For the example bellow we will use only required parameters, but you can use any other parameter as well.
27
27
 
28
28
  ```ruby
29
- client.routing_forms.list_submissions(form: routing_form_uri)
29
+ # form: accepts a bare UUID or full Calendly URI
30
+ client.routing_forms.list_submissions(form: "ROUTING_FORM_UUID")
30
31
  #=> #<Calendlyr::Collection @data=[#<Calendlyr::RoutingForms::Submission>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
31
32
  ```
32
33
 
@@ -0,0 +1,26 @@
1
+ # Scheduling Link (`Calendlyr::SchedulingLink`)
2
+
3
+ Scheduling link object.
4
+
5
+ Visit official [API Doc](https://developer.calendly.com/api-docs)
6
+
7
+ ## Client requests
8
+
9
+ ### Create
10
+
11
+ Creates a scheduling link for an Event Type owner.
12
+
13
+ Visit official [API Doc](https://developer.calendly.com/api-docs)
14
+
15
+ ```ruby
16
+ # owner: accepts a bare UUID or full Calendly URI
17
+ client.scheduling_links.create(owner: "EVENT_TYPE_UUID")
18
+ #=> #<Calendlyr::SchedulingLink>
19
+
20
+ client.scheduling_links.create(
21
+ owner: "EVENT_TYPE_UUID",
22
+ owner_type: "EventType",
23
+ max_event_count: 3
24
+ )
25
+ #=> #<Calendlyr::SchedulingLink>
26
+ ```
@@ -14,7 +14,8 @@ Endpoint for our Customize Once and Share feature. This allows you to customize
14
14
  Visit official [API Doc](https://developer.calendly.com/api-docs/fdcac06abfc8c-create-share)
15
15
 
16
16
  ```ruby
17
- client.shares.create(create: event_type_uri, name: "15 minute meeting", duration: ...)
17
+ # event_type: accepts a bare UUID or full Calendly URI
18
+ client.shares.create(event_type: "EVENT_TYPE_UUID", name: "15 minute meeting", duration: ...)
18
19
  #=> #<Calendlyr::Share>
19
20
  ```
20
21
 
@@ -1,6 +1,12 @@
1
- # Webhooks Invitee Payload Calendlyr::Wehooks::InviteePayload
1
+ # Webhooks Invitee Payload (`Calendlyr::Webhooks::InviteePayload`)
2
2
 
3
- The payload that is sent via Webhook when an invitee creates or schedules a meeting, and when an invitee cancels.
3
+ Typed payload wrapper used by `Calendlyr::Webhook.parse` when `event` is one of:
4
+ - `invitee.created`
5
+ - `invitee.canceled`
6
+ - `invitee_no_show.created`
7
+ - `invitee_no_show.deleted`
8
+
9
+ For other events, `parsed.payload` is returned as a generic `Calendlyr::Object`.
4
10
 
5
11
  Visit official [API Doc](https://developer.calendly.com/api-docs/b92768854bc06-invitee-payload)
6
12
 
@@ -9,7 +15,7 @@ Visit official [API Doc](https://developer.calendly.com/api-docs/b92768854bc06-i
9
15
  ### Associated Event
10
16
 
11
17
  ```ruby
12
- webhook_invitee_payload.associated_organization
18
+ webhook_invitee_payload.associated_event
13
19
  #=> #<Calendlyr::Event>
14
20
  ```
15
21
 
@@ -27,16 +33,4 @@ webhook_invitee_payload.associated_invitee_no_show
27
33
  #=> #<Calendlyr::Events::InviteeNoShow>
28
34
  ```
29
35
 
30
- ### active?
31
-
32
- ```ruby
33
- webhook_subscription.active?
34
- #=> true
35
- ```
36
-
37
- ### disabled?
38
-
39
- ```ruby
40
- webhook_subscription.disabled?
41
- #=> false
42
- ```
36
+ `active?` / `disabled?` are methods on `Calendlyr::Webhooks::Subscription`, not on invitee payloads.