calendlyr 0.7.5 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +9 -5
- data/.gitignore +3 -1
- data/CHANGELOG.md +98 -0
- data/README.md +209 -73
- data/calendlyr.gemspec +8 -6
- data/docs/resources/activity_log/list_activity_log_entries.md +2 -1
- data/docs/resources/availabilities/user_availability_schedule.md +3 -2
- data/docs/resources/availabilities/user_busy_time.md +3 -2
- data/docs/resources/data_compliance.md +1 -1
- data/docs/resources/event_types/availability_schedule.md +28 -0
- data/docs/resources/event_types/available_time.md +15 -11
- data/docs/resources/event_types/event_type.md +21 -9
- data/docs/resources/event_types/membership.md +15 -16
- data/docs/resources/events/cancellation.md +1 -1
- data/docs/resources/events/event.md +6 -5
- data/docs/resources/events/invitee.md +18 -0
- data/docs/resources/events/invitee_no_show.md +2 -1
- data/docs/resources/groups/group.md +2 -1
- data/docs/resources/locations/location.md +16 -0
- data/docs/resources/organizations/membership.md +3 -2
- data/docs/resources/organizations/organization.md +13 -9
- data/docs/resources/outgoing_communications/outgoing_communication.md +28 -0
- data/docs/resources/routing_forms/routing_form.md +2 -1
- data/docs/resources/routing_forms/submission.md +2 -1
- data/docs/resources/scheduling_links/scheduling_link.md +26 -0
- data/docs/resources/share.md +4 -10
- data/docs/resources/webhooks/invitee_payload.md +10 -16
- data/docs/resources/webhooks/payload.md +32 -3
- data/docs/resources/webhooks/sample.md +23 -0
- data/docs/resources/webhooks/subscription.md +10 -6
- data/lib/calendlyr/client.rb +7 -2
- data/lib/calendlyr/collection.rb +42 -9
- data/lib/calendlyr/configuration.rb +24 -0
- data/lib/calendlyr/error.rb +65 -27
- data/lib/calendlyr/object.rb +85 -13
- data/lib/calendlyr/objects/event_type.rb +0 -4
- data/lib/calendlyr/objects/event_types/availability_schedule.rb +8 -0
- data/lib/calendlyr/objects/event_types/available_time.rb +5 -1
- data/lib/calendlyr/objects/event_types/membership.rb +4 -7
- data/lib/calendlyr/objects/events/invitee.rb +1 -1
- data/lib/calendlyr/objects/location.rb +4 -0
- data/lib/calendlyr/objects/organization.rb +0 -4
- data/lib/calendlyr/objects/outgoing_communication.rb +6 -0
- data/lib/calendlyr/objects/scheduling_link.rb +2 -5
- data/lib/calendlyr/objects/share.rb +0 -5
- data/lib/calendlyr/objects/webhooks/payload.rb +30 -0
- data/lib/calendlyr/resource.rb +84 -12
- data/lib/calendlyr/resources/availability.rb +14 -2
- data/lib/calendlyr/resources/event_types.rb +45 -5
- data/lib/calendlyr/resources/events.rb +20 -2
- data/lib/calendlyr/resources/groups.rb +14 -2
- data/lib/calendlyr/resources/locations.rb +13 -0
- data/lib/calendlyr/resources/organizations.rb +25 -3
- data/lib/calendlyr/resources/outgoing_communications.rb +11 -3
- data/lib/calendlyr/resources/routing_forms.rb +14 -2
- data/lib/calendlyr/resources/scheduling_links.rb +5 -2
- data/lib/calendlyr/resources/shares.rb +1 -0
- data/lib/calendlyr/resources/webhooks.rb +11 -3
- data/lib/calendlyr/version.rb +1 -1
- data/lib/calendlyr/webhook.rb +105 -0
- data/lib/calendlyr.rb +50 -0
- data/logos/calendlyr.png +0 -0
- data/logos/calendlyr_bg_white.png +0 -0
- data/test/calendlyr/client_test.rb +29 -0
- data/test/calendlyr/collection_test.rb +168 -0
- data/test/calendlyr/configuration_test.rb +157 -0
- data/test/calendlyr/object_test.rb +82 -1
- data/test/calendlyr/objects/event_type_test.rb +0 -15
- data/test/calendlyr/objects/event_types/availability_schedule_test.rb +20 -0
- data/test/calendlyr/objects/events/cancellation_test.rb +1 -1
- data/test/calendlyr/objects/events/guest_test.rb +1 -1
- data/test/calendlyr/objects/events/invitee_no_show_test.rb +1 -1
- data/test/calendlyr/objects/events/invitee_test.rb +10 -3
- data/test/calendlyr/objects/location_test.rb +22 -0
- data/test/calendlyr/objects/organization_test.rb +0 -8
- data/test/calendlyr/objects/organizations/invitation_test.rb +1 -1
- data/test/calendlyr/objects/share_test.rb +3 -9
- data/test/calendlyr/objects/webhooks/payload_test.rb +15 -0
- data/test/calendlyr/resource_test.rb +456 -2
- data/test/calendlyr/resources/availabilities/user_busy_times_test.rb +26 -0
- data/test/calendlyr/resources/availabilities/user_schedules_test.rb +25 -0
- data/test/calendlyr/resources/data_compliance_test.rb +1 -4
- data/test/calendlyr/resources/event_types_test.rb +132 -0
- data/test/calendlyr/resources/events_test.rb +87 -0
- data/test/calendlyr/resources/groups_test.rb +54 -0
- data/test/calendlyr/resources/locations_test.rb +30 -0
- data/test/calendlyr/resources/organizations_test.rb +96 -2
- data/test/calendlyr/resources/outgoing_communications_test.rb +34 -8
- data/test/calendlyr/resources/routing_forms_test.rb +57 -0
- data/test/calendlyr/resources/scheduling_links_test.rb +31 -6
- data/test/calendlyr/resources/shares_test.rb +15 -0
- data/test/calendlyr/resources/webhooks_test.rb +63 -5
- data/test/calendlyr/webhook_test.rb +292 -0
- data/test/fixtures/activity_log/list_page2.json +30 -0
- data/test/fixtures/event_invitees/list_page2.json +35 -0
- data/test/fixtures/event_invitees/retrieve.json +11 -1
- data/test/fixtures/event_type_availability_schedules/list.json +17 -0
- data/test/fixtures/event_type_availability_schedules/update.json +3 -0
- data/test/fixtures/event_type_available_times/list.json +0 -12
- data/test/fixtures/event_type_memberships/list.json +43 -0
- data/test/fixtures/event_type_memberships/list_page2.json +33 -0
- data/test/fixtures/event_types/create.json +30 -0
- data/test/fixtures/event_types/list_page2.json +37 -0
- data/test/fixtures/event_types/update.json +30 -0
- data/test/fixtures/events/create_invitee.json +37 -0
- data/test/fixtures/events/list_page2.json +29 -0
- data/test/fixtures/events/retrieve.json +12 -2
- data/test/fixtures/group_relationships/list_page2.json +35 -0
- data/test/fixtures/groups/list_page2.json +16 -0
- data/test/fixtures/locations/list.json +16 -0
- data/test/fixtures/locations/list_page2.json +16 -0
- data/test/fixtures/objects/event.json +10 -2
- data/test/fixtures/objects/event_types/availability_schedule.json +6 -0
- data/test/fixtures/objects/location.json +5 -0
- data/test/fixtures/organizations/list_invitations_page2.json +18 -0
- data/test/fixtures/organizations/list_memberships_page2.json +26 -0
- data/test/fixtures/organizations/retrieve.json +11 -0
- data/test/fixtures/outgoing_communications/list.json +4 -6
- data/test/fixtures/outgoing_communications/list_page2.json +21 -0
- data/test/fixtures/routing_forms/list_page2.json +17 -0
- data/test/fixtures/routing_forms/list_routing_form_submission_page2.json +29 -0
- data/test/fixtures/user_availability_schedules/list_page1.json +16 -0
- data/test/fixtures/user_availability_schedules/list_page2.json +16 -0
- data/test/fixtures/user_busy_times/list_page1.json +18 -0
- data/test/fixtures/user_busy_times/list_page2.json +13 -0
- data/test/fixtures/webhooks/list_page2.json +23 -0
- data/test/fixtures/webhooks/sample.json +55 -94
- data/test/test_helper.rb +19 -7
- metadata +70 -27
- data/docs/resources/scheduling_link.md +0 -26
- data/test/calendlyr/objects/event_types/available_time_test.rb +0 -20
- data/test/calendlyr/objects/event_types/membership_test.rb +0 -32
- data/test/calendlyr/objects/scheduling_link_test.rb +0 -17
- data/test/calendlyr/resources/event_types/membership_test.rb +0 -22
- data/test/fixtures/objects/event_types/available_time.json +0 -6
- data/test/fixtures/objects/event_types/membership.json +0 -65
- data/test/fixtures/objects/scheduling_links/event_type.json +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ca7216a6bc050ebad873636af0bb988c8841d7828fe6cebc0a0497947c0a9dc7
|
|
4
|
+
data.tar.gz: e36f58a64ad9277fdd38bb44fe69aeb4e7d84fe10fb6b9274a474bb609406340
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 36c382f6725f7fdc22acfd11bee564dbcd5c448df265f49d07c450e074ab23c3a1f165970e4f5f925feebbcfbd7f3dae827ecc144a5ca0fb0c0524f9dcaa2d3c
|
|
7
|
+
data.tar.gz: 2e60e92afb872bb9590cda26e3208ed1bed7f069ea644a55738c5c6284447241c0c90358ff2a19d1e3c1149a790db2e69648c8ed945cd9ed6e4a9ed36526d575
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -11,7 +11,7 @@ jobs:
|
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
strategy:
|
|
13
13
|
matrix:
|
|
14
|
-
ruby: ["2
|
|
14
|
+
ruby: ["3.2", "3.3", "3.4", "4.0"]
|
|
15
15
|
|
|
16
16
|
steps:
|
|
17
17
|
- uses: actions/checkout@v4
|
|
@@ -27,7 +27,11 @@ jobs:
|
|
|
27
27
|
run: bundle exec standardrb
|
|
28
28
|
|
|
29
29
|
- name: Run tests
|
|
30
|
-
run:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
run: bundle exec rake test
|
|
31
|
+
|
|
32
|
+
- name: Upload coverage
|
|
33
|
+
if: matrix.ruby == '3.4'
|
|
34
|
+
uses: codecov/codecov-action@v4
|
|
35
|
+
with:
|
|
36
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
37
|
+
files: coverage/.resultset.json
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,104 @@
|
|
|
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
|
+
|
|
38
|
+
## [0.10.0]
|
|
39
|
+
|
|
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.
|
|
45
|
+
* `client.data_compliance.delete_scheduled_event_data` — Remove scheduled events data within a time range (`POST /data_compliance/deletion/events`)
|
|
46
|
+
* `put_request` support in `Resource` base class for PUT HTTP verb
|
|
47
|
+
* `Collection` now includes `Enumerable` — use `each`, `map`, `select` directly on collections
|
|
48
|
+
* Configurable HTTP timeouts via `Client.new(token:, open_timeout:, read_timeout:)` (default 30s)
|
|
49
|
+
* Automatic retry with exponential backoff for 429 Too Many Requests (max 3 retries, respects `Retry-After` header)
|
|
50
|
+
|
|
51
|
+
### Changed — Breaking
|
|
52
|
+
* **`Calendlyr::Object`** no longer inherits from `OpenStruct`. Replaced with a zero-dependency hash-backed class. Dynamic dot-access for all API fields is preserved. If you were relying on `OpenStruct`-specific methods (e.g., `marshal_dump`), this is a breaking change.
|
|
53
|
+
* Empty API responses (e.g., DELETE) now return `{}` instead of `true`
|
|
54
|
+
* Requires Ruby >= 3.2.0 (dropped support for Ruby 2.4–3.1)
|
|
55
|
+
|
|
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
|
|
58
|
+
* **Security:** Removed `OpenSSL::SSL::VERIFY_NONE` — SSL connections now properly verify certificates
|
|
59
|
+
* **Security:** Bare `rescue` replaced with `rescue JSON::ParserError` — non-JSON errors are no longer silently swallowed
|
|
60
|
+
* `Invitee#cancel` now correctly uses the event UUID instead of the invitee UUID
|
|
61
|
+
* `CGI.parse` replaced with `URI.decode_www_form` for Ruby 4.0 compatibility
|
|
62
|
+
* Tautological test assertions (`assert` with string literal) replaced with `assert_equal`
|
|
63
|
+
|
|
64
|
+
### Removed
|
|
65
|
+
* `codecov` gem dependency (deprecated since Feb 2022, incompatible with Ruby 4.0). Coverage uploads now use `codecov-action` in CI.
|
|
66
|
+
|
|
67
|
+
[0.10.0]: https://github.com/araluce/calendlyr/compare/v0.9.0...v0.10.0
|
|
68
|
+
|
|
69
|
+
## [0.9.0]
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
* `client.event_types.create` — Create standard event types (`POST /event_types`)
|
|
73
|
+
* `client.event_types.update` — Update existing event types (`PATCH /event_types/:uuid`)
|
|
74
|
+
* `client.event_types.list_availability_schedules` — List availability schedules for an event type (`GET /event_type_availability_schedules`)
|
|
75
|
+
* `client.event_types.update_availability_schedule` — Update availability schedules (`PATCH /event_type_availability_schedules`)
|
|
76
|
+
* `client.locations.list` — List host's connected locations: Zoom, Google Meet, etc. (`GET /locations`)
|
|
77
|
+
* `client.events.create_invitee` — Programmatically book events via the Scheduling API (`POST /invitees`)
|
|
78
|
+
* `patch_request` support in `Resource` base class for PATCH HTTP verb
|
|
79
|
+
|
|
80
|
+
### Changed
|
|
81
|
+
* Updated `Event` fixtures with `buffered_start_time`, `buffered_end_time`, `meeting_notes_plain`, `meeting_notes_html`, and `cancellation` fields
|
|
82
|
+
* Updated `Invitee` fixtures with `no_show`, `reconfirmation`, `scheduling_method`, and `invitee_scheduled_by` fields
|
|
83
|
+
|
|
84
|
+
### Removed — Breaking
|
|
85
|
+
* **`client.outgoing_communications`** — `OutgoingCommunicationsResource` removed (endpoint no longer in Calendly API docs)
|
|
86
|
+
* **`client.scheduling_links`** — `SchedulingLinksResource` and `SchedulingLink` object removed (endpoint no longer in Calendly API docs)
|
|
87
|
+
* **`client.webhooks.sample_webhook_data`** — Method removed (endpoint no longer in Calendly API docs)
|
|
88
|
+
* **`client.event_types.list_available_times`** — Method and `EventTypes::AvailableTime` object removed (endpoint no longer in Calendly API docs)
|
|
89
|
+
* **`client.event_types.list_memberships`** — Method and `EventTypes::Membership` object removed (endpoint no longer in Calendly API docs)
|
|
90
|
+
* **`EventType#available_times`** — Convenience method removed (delegates to removed `list_available_times`)
|
|
91
|
+
* **`Share#associated_scheduling_links`** — Method removed (depends on removed `SchedulingLink`)
|
|
92
|
+
* **`Organization#sample_webhook_data`** — Convenience method removed (delegates to removed `webhooks.sample_webhook_data`)
|
|
93
|
+
|
|
94
|
+
[0.9.0]: https://github.com/araluce/calendlyr/compare/v0.8.0...v0.9.0
|
|
95
|
+
|
|
96
|
+
## [0.8.0]
|
|
97
|
+
* **Breaking:** All error classes (`BadRequest`, `NotFound`, `Unauthenticated`, `PermissionDenied`, `ExternalCalendarError`, `TooManyRequests`, `InternalServerError`) now inherit from `Calendlyr::Error` instead of `StandardError`. Code using `rescue Calendlyr::Error` will now catch all API errors.
|
|
98
|
+
* Fix: Response handling with empty body
|
|
99
|
+
* Fix: Some doc typos
|
|
100
|
+
|
|
101
|
+
[0.8.0]: https://github.com/araluce/calendlyr/compare/v0.7.5...v0.8.0
|
|
102
|
+
|
|
5
103
|
## [0.7.5]
|
|
6
104
|
* Fix: Calendlyr::TooManyRequests was not included in autoloading list
|
|
7
105
|
* Fix: Calendlyr::ExternalCalendarError typo in class name
|
data/README.md
CHANGED
|
@@ -1,103 +1,239 @@
|
|
|
1
|
-
[](https://github.com/araluce/calendlyr/blob/master/LICENSE.txt)
|
|
2
|
+
[](https://github.com/araluce/calendlyr/actions)
|
|
3
|
+
[](https://codecov.io/gh/araluce/calendlyr)
|
|
4
|
+

|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Calendlyr
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+

|
|
9
9
|
|
|
10
|
-
|
|
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.
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Installation
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Calendlyr requires **Ruby >= 3.2.0**.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Add to your Gemfile:
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
```ruby
|
|
19
|
+
gem "calendlyr"
|
|
20
|
+
```
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
Then run `bundle install`. That's it.
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
## Quick Start
|
|
23
25
|
|
|
24
26
|
```ruby
|
|
25
|
-
|
|
27
|
+
client = Calendlyr::Client.new(token: ENV["CALENDLY_TOKEN"])
|
|
28
|
+
|
|
29
|
+
# List your scheduled events — just pass the UUID, no full URI needed
|
|
30
|
+
events = client.events.list(user: "YOUR_USER_UUID")
|
|
31
|
+
events.data
|
|
32
|
+
#=> [#<Calendlyr::Event>, #<Calendlyr::Event>, ...]
|
|
33
|
+
|
|
34
|
+
# Access event details naturally
|
|
35
|
+
event = events.data.first
|
|
36
|
+
event.name #=> "30 Minute Meeting"
|
|
37
|
+
event.status #=> "active"
|
|
38
|
+
event.start_time #=> "2024-01-15T10:00:00.000000Z"
|
|
39
|
+
|
|
40
|
+
# List invitees for an event
|
|
41
|
+
invitees = client.events.list_invitees(uuid: event.uuid)
|
|
42
|
+
invitees.data.first.email #=> "john@example.com"
|
|
26
43
|
```
|
|
27
44
|
|
|
28
|
-
|
|
45
|
+
## Global configuration
|
|
29
46
|
|
|
30
|
-
|
|
47
|
+
For single-tenant apps, you can configure `Calendlyr` once and reuse a default client:
|
|
31
48
|
|
|
32
|
-
|
|
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
|
+
```
|
|
33
59
|
|
|
34
|
-
|
|
60
|
+
`Calendlyr.client` memoizes a client instance and rebuilds it if token or timeout values change.
|
|
35
61
|
|
|
36
|
-
###
|
|
62
|
+
### Optional request/response logging
|
|
37
63
|
|
|
38
|
-
|
|
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.
|
|
39
65
|
|
|
40
66
|
```ruby
|
|
41
|
-
|
|
67
|
+
require "logger"
|
|
68
|
+
|
|
69
|
+
client = Calendlyr::Client.new(token: ENV["CALENDLY_TOKEN"], logger: Logger.new($stdout))
|
|
42
70
|
```
|
|
43
71
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
+
|
|
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.
|
|
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
|
+
|
|
225
|
+
## Documentation
|
|
226
|
+
|
|
227
|
+
For the full list of available resources and methods, check out the [API Reference](docs/resources/).
|
|
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.
|
|
88
230
|
|
|
89
231
|
## Contributing
|
|
90
232
|
|
|
91
|
-
1. Fork it
|
|
233
|
+
1. [Fork it](https://github.com/araluce/calendlyr/fork)
|
|
92
234
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
93
235
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
94
236
|
4. Push to the branch (`git push origin my-new-feature`)
|
|
95
237
|
5. Create a new Pull Request
|
|
96
238
|
|
|
97
|
-
When adding resources,
|
|
98
|
-
|
|
99
|
-
## Thanks
|
|
100
|
-
|
|
101
|
-
Many thanks to[@markets](https://github.com/markets) (our behind-the-scenes contributor) for all the comments, insights, and tips on this Ruby gem project, and for helping me grow professionally day by day :raised_hands:
|
|
102
|
-
|
|
103
|
-
Thanks also to [@excid3](https://github.com/excid3) and his [Vultr.rb](https://github.com/excid3/vultr.rb) rubygem project.
|
|
239
|
+
When adding resources, please write tests and update the [docs](docs/resources/).
|
data/calendlyr.gemspec
CHANGED
|
@@ -7,20 +7,22 @@ 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.
|
|
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
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.
|
|
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
|
|
|
20
|
-
spec.add_development_dependency "
|
|
22
|
+
spec.add_development_dependency "logger", "~> 1.6"
|
|
21
23
|
spec.add_development_dependency "minitest", "~> 5.25"
|
|
22
|
-
spec.add_development_dependency "
|
|
23
|
-
spec.add_development_dependency "webmock", "~> 3.23"
|
|
24
|
-
spec.add_development_dependency "codecov", "~> 0.6"
|
|
24
|
+
spec.add_development_dependency "rake", "~> 13.2"
|
|
25
25
|
spec.add_development_dependency "simplecov", "~> 0.21"
|
|
26
|
+
spec.add_development_dependency "standard", "~> 1.37"
|
|
27
|
+
spec.add_development_dependency "webmock", "~> 3.23"
|
|
26
28
|
end
|
|
@@ -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
|
-
|
|
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
|
-
|
|
24
|
-
|
|
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
|
-
|
|
18
|
-
|
|
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
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Delete Invitee Data
|
|
4
4
|
|
|
5
|
-
Remove invitee data from all previously booked events in your organization
|
|
5
|
+
Remove invitee data from all previously booked events in your organization.
|
|
6
6
|
Visit official [API Doc](https://developer.calendly.com/api-docs/4cf896120a018-delete-invitee-data)
|
|
7
7
|
|
|
8
8
|
```ruby
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Availability Schedule Calendlyr::EventTypes::AvailabilitySchedule
|
|
2
|
+
|
|
3
|
+
Availability schedule object for event types.
|
|
4
|
+
|
|
5
|
+
## Client requests
|
|
6
|
+
|
|
7
|
+
### List
|
|
8
|
+
|
|
9
|
+
Returns a list of availability schedules for an event type.
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
client.event_types.list_availability_schedules(event_type_uuid: event_type_uuid)
|
|
13
|
+
#=> #<Calendlyr::Collection @data=[#<Calendlyr::EventTypes::AvailabilitySchedule>, ...], @count=nil, @next_page=nil, @next_page_token=nil, @client=#<Calendlyr::Client>>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Update
|
|
17
|
+
|
|
18
|
+
Updates availability schedules for an event type.
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
client.event_types.update_availability_schedule(
|
|
22
|
+
event_type_uuid: event_type_uuid,
|
|
23
|
+
availability_schedules: [
|
|
24
|
+
{start_time: "2023-10-27T09:00:00Z", end_time: "2023-10-27T12:00:00Z"}
|
|
25
|
+
]
|
|
26
|
+
)
|
|
27
|
+
#=> {"message"=>"Availability schedules updated successfully."}
|
|
28
|
+
```
|
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
# Available Time Calendlyr::EventTypes::AvailableTime
|
|
1
|
+
# Available Time (`Calendlyr::EventTypes::AvailableTime`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Available time for a specific Event Type in a given time window.
|
|
4
4
|
|
|
5
|
-
Visit official [API Doc](https://developer.calendly.com/api-docs
|
|
5
|
+
Visit official [API Doc](https://developer.calendly.com/api-docs)
|
|
6
6
|
|
|
7
7
|
## Client requests
|
|
8
8
|
|
|
9
|
-
### List
|
|
10
|
-
Returns a list of available times for an event type within a specified date range.
|
|
9
|
+
### List Available Times
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
Returns available time slots for an Event Type between `start_time` and `end_time`.
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Visit official [API Doc](https://developer.calendly.com/api-docs/6a1be82aef359-list-event-type-available-times)
|
|
13
|
+
Visit official [API Doc](https://developer.calendly.com/api-docs)
|
|
17
14
|
|
|
18
15
|
```ruby
|
|
19
|
-
|
|
20
|
-
|
|
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>, ...], ...>
|
|
21
23
|
```
|
|
24
|
+
|
|
25
|
+
This endpoint does not support pagination.
|