├── .env.example ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .smoke-test-env.example ├── .tool-versions ├── CHANGELOG.md ├── LICENSE.txt ├── Makefile ├── README.md ├── composer.json ├── composer.lock ├── dev-smoke-test.php ├── init ├── phpunit.xml ├── ruleset.xml ├── src ├── Batch │ ├── Batch.php │ ├── BatchRequest.php │ ├── BatchResponse.php │ └── BatchResult.php ├── Cronofy.php ├── Exception │ ├── CronofyException.php │ └── PartialBatchFailureException.php ├── Http │ ├── CurlRequest.php │ └── HttpRequest.php └── PagedResultIterator.php └── tests ├── AccessibleCalendarsTest.php ├── ApplicationCalendarTest.php ├── BatchTest.php ├── CronofyTest.php ├── DelegatedAuthorizationsTest.php ├── ReadEventsTest.php ├── RulesTest.php ├── SchedulingTest.php ├── UpsertEventTest.php └── bootstrap.php /.env.example: -------------------------------------------------------------------------------- 1 | export CLIENT_ID= 2 | export CLIENT_SECRET= 3 | export REFRESH_TOKEN= 4 | export ACCESS_TOKEN= 5 | export DATACENTER=local 6 | export SUB= 7 | export CALENDAR_ID= 8 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: php CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | php_version: ['8.2', '8.1', '8.0','7.4','7.3'] 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Setup PHP Action 16 | uses: shivammathur/setup-php@2.11.0 17 | with: 18 | php-version: ${{ matrix.php_version }} 19 | 20 | - name: Install composer 21 | run: make install_composer 22 | 23 | - name: Run tests 24 | run: make ci 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | .idea 4 | .env 5 | .phpunit.result.cache 6 | .vscode -------------------------------------------------------------------------------- /.smoke-test-env.example: -------------------------------------------------------------------------------- 1 | export CLIENT_ID= 2 | export CLIENT_SECRET= 3 | export CALENDAR_ID= 4 | export REFRESH_TOKEN= 5 | export ACCESS_TOKEN= 6 | export DATACENTER= 7 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | php 7.4.33 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.7.3] 2 | * Improved PHPDoc code documentation 3 | 4 | ## [1.7.2] 5 | * Adds support for bulk event deletion with bulkDeleteEvents method 6 | 7 | ## [1.7.1] 8 | * Fixes hmacValid function not outputting raw binary data 9 | 10 | ## [1.7.0] 11 | * Adds support for event_classes on UpsertEvent 12 | * Updates PHP dev env to 8.1 13 | 14 | ## [1.6.0] 15 | * add support for disabling Real-Time Scheduling links 16 | 17 | ## [1.5.3] 18 | * accept locale on upsertEvent 19 | 20 | ## [1.5.2] 21 | * update package description 22 | 23 | ## [1.5.1] 24 | * add support for additional Real-Time Scheduling params 25 | 26 | ## [1.5.0] 27 | * add support for PHP 8 28 | * add `hmacValid` to verify a webhook call genuinely originated from Cronofy [#111] 29 | 30 | ## [1.4.0] 31 | * add support for `recurrence` key on upsertEvent (this is currently pre-release and not generally available yet) 32 | 33 | ## [1.3.0] 34 | * add AvailablePeriod create, read, delete and bulkDelete [#105] [#106] 35 | * add support for empty descriptions [#103] 36 | * add support for subscriptions on baseUpsertEvent [#104] 37 | 38 | ## [1.2.0] 39 | 40 | * add support for the Batch endpoint [#97] 41 | * add description for HTTP 429 (Too many requests) responses 42 | * add an SDK-dev script for manually testing the SDK against the API 43 | 44 | ## [1.1.10] 45 | 46 | * add missing exception `use` [#86] 47 | 48 | ## [1.1.9] 49 | 50 | * add support for `provider_name` to `conferencingServiceAuthorization()` [#94] 51 | 52 | ## [1.1.8] 53 | 54 | * add `conferencingServiceAuthorization()` 55 | 56 | ## [1.1.7] 57 | 58 | * support revoking subs 59 | 60 | ## [1.1.6] 61 | 62 | * less restrictive options for `conferencing` 63 | 64 | ## [1.1.5] 65 | 66 | * add `requestElementToken()` 67 | 68 | ## [1.1.4] 69 | 70 | * add `color` support to `upsertEvent` 71 | 72 | ## [1.1.3] 73 | 74 | * allow serialization of booleans in params 75 | 76 | ## [1.1.2] 77 | 78 | * disable libcurl verbose output 79 | 80 | ## [1.1.1] 81 | 82 | * add support for `provider_name` on `getAuthorizationURL()` 83 | 84 | ## [1.1.0] 85 | 86 | * add support for Conferencing Services 87 | 88 | ## [1.0.1] 89 | 90 | * documentation updates for v1 release 91 | 92 | ## [1.0.0] 93 | 94 | * namespacing and updated naming [#33] 95 | 96 | ## [0.29.0] 97 | 98 | * cancel_smart_invite recipients [#32] 99 | 100 | [0.29.0]: https://github.com/cronofy/cronofy-php/releases/tag/v0.29.0 101 | [1.0.0]: https://github.com/cronofy/cronofy-php/releases/tag/v1.0.0 102 | [1.0.1]: https://github.com/cronofy/cronofy-php/releases/tag/v1.0.1 103 | [1.1.0]: https://github.com/cronofy/cronofy-php/releases/tag/v1.1.0 104 | [1.1.1]: https://github.com/cronofy/cronofy-php/releases/tag/v1.1.1 105 | [1.1.2]: https://github.com/cronofy/cronofy-php/releases/tag/v1.1.2 106 | [1.1.3]: https://github.com/cronofy/cronofy-php/releases/tag/v1.1.3 107 | [1.1.4]: https://github.com/cronofy/cronofy-php/releases/tag/v1.1.4 108 | [1.1.5]: https://github.com/cronofy/cronofy-php/releases/tag/v1.1.5 109 | [1.1.6]: https://github.com/cronofy/cronofy-php/releases/tag/v1.1.6 110 | [1.1.7]: https://github.com/cronofy/cronofy-php/releases/tag/v1.1.7 111 | [1.1.8]: https://github.com/cronofy/cronofy-php/releases/tag/v1.1.8 112 | [1.1.9]: https://github.com/cronofy/cronofy-php/releases/tag/v1.1.9 113 | [1.1.10]: https://github.com/cronofy/cronofy-php/releases/tag/v1.1.10 114 | [1.2.0]: https://github.com/cronofy/cronofy-php/releases/tag/v1.2.0 115 | [1.3.0]: https://github.com/cronofy/cronofy-php/releases/tag/v1.3.0 116 | [1.4.0]: https://github.com/cronofy/cronofy-php/releases/tag/v1.4.0 117 | [1.5.0]: https://github.com/cronofy/cronofy-php/releases/tag/v1.5.0 118 | [1.5.1]: https://github.com/cronofy/cronofy-php/releases/tag/v1.5.1 119 | [1.5.2]: https://github.com/cronofy/cronofy-php/releases/tag/v1.5.2 120 | [1.5.3]: https://github.com/cronofy/cronofy-php/releases/tag/v1.5.3 121 | [1.6.0]: https://github.com/cronofy/cronofy-php/releases/tag/v1.6.0 122 | [1.7.0]: https://github.com/cronofy/cronofy-php/releases/tag/v1.7.0 123 | [1.7.1]: https://github.com/cronofy/cronofy-php/releases/tag/v1.7.1 124 | [1.7.2]: https://github.com/cronofy/cronofy-php/releases/tag/v1.7.2 125 | [1.7.3]: https://github.com/cronofy/cronofy-php/releases/tag/v1.7.3 126 | 127 | [#32]: https://github.com/cronofy/cronofy-php/pull/76 128 | [#33]: https://github.com/cronofy/cronofy-php/pull/74 129 | [#34]: https://github.com/cronofy/cronofy-php/pull/77 130 | [#94]: https://github.com/cronofy/cronofy-php/pull/94 131 | [#86]: https://github.com/cronofy/cronofy-php/pull/86 132 | [#97]: https://github.com/cronofy/cronofy-php/pull/97 133 | [#103]: https://github.com/cronofy/cronofy-php/pull/103 134 | [#104]: https://github.com/cronofy/cronofy-php/pull/104 135 | [#105]: https://github.com/cronofy/cronofy-php/pull/105 136 | [#106]: https://github.com/cronofy/cronofy-php/pull/106 137 | [#111]: https://github.com/cronofy/cronofy-php/pull/111 138 | [#116]: https://github.com/cronofy/cronofy-php/pull/116 139 | [#126]: https://github.com/cronofy/cronofy-php/pull/126 140 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Cronofy 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CURRENT_VERSION:=$(shell jq ".version" -r composer.json) 2 | 3 | .PHONY: all 4 | all: test 5 | 6 | .PHONY: install_composer 7 | install_composer: 8 | curl -sS https://getcomposer.org/installer | php 9 | 10 | .PHONY: install_dependencies 11 | install_dependencies: install_composer 12 | php composer.phar install 13 | 14 | .PHONY: update 15 | update: 16 | php composer.phar update 17 | 18 | .PHONY: test 19 | test: install_dependencies 20 | vendor/bin/phpunit tests/ 21 | vendor/bin/phpcs tests/ src/Cronofy.php --standard=ruleset.xml 22 | 23 | .PHONY: smoke-test 24 | smoke-test: 25 | source .env && php ./dev-smoke-test.php 26 | 27 | .PHONY: ci 28 | ci: test 29 | 30 | .PHONY: check_dependencies 31 | check_dependencies: 32 | @command -v jq >/dev/null || (echo "jq not installed please install via homebrew - 'brew install jq'"; exit 1) 33 | 34 | .PHONY: release 35 | release: check_dependencies test 36 | git push 37 | git tag $(CURRENT_VERSION) 38 | git push --tags 39 | 40 | .PHONY: init 41 | init: 42 | ./init 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cronofy 2 | 3 | [Cronofy](https://www.cronofy.com) - one API for all the calendars (Google, iCloud, Exchange, Office 365, Outlook.com) 4 | [![php CI](https://github.com/cronofy/cronofy-php/actions/workflows/ci.yml/badge.svg)](https://github.com/cronofy/cronofy-php/actions/workflows/ci.yml) 5 | 6 | 7 | ## Sample Application 8 | 9 | To see this API wrapper in action see our sample app [here](https://github.com/cronofy/cronofy-php-sample-app) 10 | 11 | ## Usage 12 | 13 | > Note: if upgrading from a v0.x.x version to v1.0.0, please read the [migration guide](#migration-guides) 14 | 15 | In order to use the Cronofy API you will need to [create a developer account](https://app.cronofy.com/sign_up/new). 16 | 17 | From there you can [use your Calendar Sandbox](https://app.cronofy.com/oauth/sandbox) 18 | to access your own calendars, or you can [create an OAuth application](https://app.cronofy.com/oauth/applications/new) 19 | to obtain an OAuth `client_id` and `client_secret` to be able to use the full 20 | API. 21 | 22 | ## Authorization 23 | 24 | [API documentation](https://www.cronofy.com/developers/api/#authorization) 25 | 26 | Generate a link for a user to grant access to their calendars: 27 | 28 | ```php 29 | $redirect_uri = "http://yoursite.dev/oauth2/callback"; 30 | 31 | $cronofy = new Cronofy\Cronofy(["client_id" => "CRONOFY_CLIENT_ID"]); 32 | $params = [ 33 | 'redirect_uri' => $redirect_uri, 34 | 'scope' => ['read_account','list_calendars','read_events','create_event','delete_event'] 35 | ]; 36 | $auth = $cronofy->getAuthorizationURL($params); 37 | ``` 38 | 39 | The redirect URI is a page on your website that will handle the OAuth 2.0 40 | callback and receive a `code` parameter. You can then use that code to retrieve 41 | an OAuth token granting access to the user's Cronofy account: 42 | 43 | ```php 44 | $cronofy = new Cronofy\Cronofy([ 45 | "client_id" => "CRONOFY_CLIENT_ID", 46 | "client_secret" => "CRONOFY_CLIENT_SECRET" 47 | ]); 48 | 49 | $params = [ 50 | 'redirect_uri' => $redirect_uri, 51 | 'code' => $code 52 | ]; 53 | 54 | $result = $cronofy->requestToken($params); 55 | 56 | // Retrieve credentials value 57 | $accessToken = $cronofy->accessToken; 58 | $refreshToken = $cronofy->refreshToken; 59 | $expiresIn = $cronofy->expiresIn; 60 | ``` 61 | 62 | You should save the response's `AccessToken` and `RefreshToken` for later use. 63 | 64 | Note that the **exact same** redirect URI must be passed to both methods for 65 | access to be granted. 66 | 67 | `$result` will be `true` for a successful request; otherwise, it will be an error code. 68 | Please reference [our documentation](https://docs.cronofy.com/developers/api/authorization/request-authorization/#param-error) for possible error code. 69 | 70 | ## Refresh Access Token 71 | 72 | Refresh an access token for an account: 73 | 74 | ```php 75 | $cronofy = new Cronofy\Cronofy([ 76 | "client_id" => "CRONOFY_CLIENT_ID", 77 | "client_secret" => "CRONOFY_CLIENT_SECRET", 78 | "grant_type" => "refresh_token", 79 | "refresh_token" => "YOUR_REFRESH_TOKEN" 80 | ]); 81 | 82 | $new_access_token = $cronofy->refreshToken(); 83 | ``` 84 | 85 | ## List calendars 86 | 87 | [API documentation](https://www.cronofy.com/developers/api/#calendars) 88 | 89 | Get a list of all the user's calendars: 90 | 91 | ```php 92 | $cronofy = new Cronofy\Cronofy([ 93 | "client_id" => "CRONOFY_CLIENT_ID", 94 | "client_secret" => "CRONOFY_CLIENT_SECRET", 95 | "access_token" => "AccessToken", 96 | "refresh_token" => "RefreshToken" 97 | ]); 98 | 99 | $calendar = $cronofy->listCalendars(); 100 | ``` 101 | 102 | ## Read events 103 | 104 | [API documentation](https://www.cronofy.com/developers/api/#read-events) 105 | 106 | Get a list of all the user's events: 107 | 108 | ```php 109 | $cronofy = new Cronofy\Cronofy([ 110 | "client_id" => "CRONOFY_CLIENT_ID", 111 | "client_secret" => "CRONOFY_CLIENT_SECRET", 112 | "access_token" => "AccessToken", 113 | "refresh_token" => "RefreshToken" 114 | ]); 115 | 116 | $params = [ 117 | 'tzid' => 'Etc/UTC' 118 | ]; 119 | 120 | $events = $cronofy->readEvents($params); 121 | 122 | foreach($events->each() as $event){ 123 | // process event 124 | } 125 | ``` 126 | 127 | ## Create or update events 128 | 129 | [API documentation](https://www.cronofy.com/developers/api/#upsert-event) 130 | 131 | To create or update an event in the user's calendar: 132 | 133 | ```php 134 | $cronofy = new Cronofy\Cronofy([ 135 | "client_id" => "CRONOFY_CLIENT_ID", 136 | "client_secret" => "CRONOFY_CLIENT_SECRET", 137 | "access_token" => "AccessToken", 138 | "refresh_token" => "RefreshToken" 139 | ]); 140 | 141 | $params = [ 142 | 'calendar_id' => 'CalendarID', 143 | 'event_id' => 'event_test_12345679', 144 | 'summary' => 'test event 2', 145 | 'description' => 'some event data here', 146 | 'start' => '2015-12-07T09:00:00Z', 147 | 'end' => '2015-12-08T10:00:00Z' 148 | ]; 149 | $new_event = $cronofy->upsertEvent($params); 150 | 151 | ``` 152 | 153 | ## Delete events 154 | 155 | [API documentation](https://www.cronofy.com/developers/api/#delete-event) 156 | 157 | To delete an event from user's calendar: 158 | 159 | ```php 160 | $cronofy = new Cronofy\Cronofy([ 161 | "client_id" => "CRONOFY_CLIENT_ID", 162 | "client_secret" => "CRONOFY_CLIENT_SECRET", 163 | "access_token" => "AccessToken", 164 | "refresh_token" => "RefreshToken" 165 | ]); 166 | 167 | $params = [ 168 | 'calendar_id' => 'CalendarID', 169 | 'event_id' => 'EventID' 170 | ]; 171 | 172 | $delete = $cronofy->deleteEvent($params); 173 | 174 | ``` 175 | 176 | ## Delete external events 177 | 178 | To delete an external event from a user's calendar: 179 | 180 | ```php 181 | $cronofy = new Cronofy\Cronofy([ 182 | "client_id" => "CRONOFY_CLIENT_ID", 183 | "client_secret" => "CRONOFY_CLIENT_SECRET", 184 | "access_token" => "AccessToken", 185 | "refresh_token" => "RefreshToken" 186 | ]); 187 | 188 | $params = [ 189 | 'calendar_id' => 'CalendarID', 190 | 'event_uid' => 'EventUID' 191 | ]; 192 | 193 | $delete = $cronofy->deleteExternalEvent($params); 194 | 195 | ``` 196 | 197 | ## Elevated permissions 198 | 199 | To elevate a client's permissions for a user's calendar(s): 200 | 201 | ```php 202 | $cronofy = new Cronofy\Cronofy([ 203 | "client_id" => "CRONOFY_CLIENT_ID", 204 | "client_secret" => "CRONOFY_CLIENT_SECRET", 205 | "access_token" => "AccessToken", 206 | "refresh_token" => "RefreshToken" 207 | ]); 208 | 209 | $params = [ 210 | 'permissions' => [ 211 | [ 212 | 'calendar_id' => 'CalendarID_1', 213 | 'permission_level' => 'unrestricted' 214 | ], 215 | [ 216 | 'calendar_id' => 'CalendarID_2', 217 | 'permission_level' => 'unrestricted' 218 | ] 219 | ], 220 | 'redirect_uri' => 'http://yoursite.dev/elevate/callback' 221 | ]; 222 | 223 | $response = $cronofy->elevatedPermissions($params); 224 | 225 | ``` 226 | 227 | ## Authorize with a Service Account 228 | 229 | To authorize a user's account using a service account: 230 | 231 | ```php 232 | $cronofy = new Cronofy\Cronofy([ 233 | "client_id" => "CRONOFY_CLIENT_ID", 234 | "client_secret" => "CRONOFY_CLIENT_SECRET", 235 | "access_token" => "AccessToken", 236 | "refresh_token" => "RefreshToken" 237 | ]); 238 | 239 | $params = [ 240 | 'email' => $email, 241 | 'callback_url' => $callback_url, 242 | 'scope' => ['read_account','list_calendars','read_events','create_event','delete_event'] 243 | ]; 244 | 245 | $response = $cronofy->authorizeWithServiceAccount($params); 246 | 247 | ``` 248 | 249 | Note: You will need to use a Service Account access token to perform this action. 250 | 251 | ## Create a Calendar 252 | 253 | To create a calendar for a user's account profile: 254 | 255 | ```php 256 | $cronofy = new Cronofy\Cronofy([ 257 | "client_id" => "CRONOFY_CLIENT_ID", 258 | "client_secret" => "CRONOFY_CLIENT_SECRET", 259 | "access_token" => "AccessToken", 260 | "refresh_token" => "RefreshToken" 261 | ]); 262 | 263 | $params = [ 264 | 'profile_id' => $account_profile_id, 265 | 'name' => $new_calendar_name 266 | ]; 267 | 268 | $response = $cronofy->createCalendar($params); 269 | 270 | ``` 271 | 272 | ## Using an Alternative Data Center 273 | 274 | To use an alternative data center: 275 | 276 | ```php 277 | $cronofy = new Cronofy\Cronofy([ 278 | "client_id" => "CRONOFY_CLIENT_ID", 279 | "client_secret" => "CRONOFY_CLIENT_SECRET", 280 | "access_token" => "AccessToken", 281 | "refresh_token" => "RefreshToken", 282 | "data_center" => "DataCenter" 283 | ]); 284 | 285 | ``` 286 | 287 | ## List Availability Rules 288 | 289 | To retrieve all availability rules saved against an account: 290 | 291 | ```php 292 | $cronofy = new Cronofy\Cronofy([ 293 | "client_id" => "CRONOFY_CLIENT_ID", 294 | "client_secret" => "CRONOFY_CLIENT_SECRET", 295 | "access_token" => "AccessToken", 296 | "refresh_token" => "RefreshToken" 297 | ]); 298 | 299 | $response = $cronofy->listAvailabilityRules(); 300 | 301 | ``` 302 | 303 | ## Read Availability Rule 304 | 305 | To retrieve an availability rule: 306 | 307 | ```php 308 | $cronofy = new Cronofy\Cronofy([ 309 | "client_id" => "CRONOFY_CLIENT_ID", 310 | "client_secret" => "CRONOFY_CLIENT_SECRET", 311 | "access_token" => "AccessToken", 312 | "refresh_token" => "RefreshToken" 313 | ]); 314 | 315 | // The String that uniquely identifies the availability rule. 316 | $rule_id = "default"; 317 | 318 | $response = $cronofy->getAvailabilityRule($rule_id); 319 | 320 | ``` 321 | 322 | ## Delete Availability Rule 323 | 324 | To delete an availability rule for the authenticated account: 325 | 326 | ```php 327 | $cronofy = new Cronofy\Cronofy([ 328 | "client_id" => "CRONOFY_CLIENT_ID", 329 | "client_secret" => "CRONOFY_CLIENT_SECRET", 330 | "access_token" => "AccessToken", 331 | "refresh_token" => "RefreshToken" 332 | ]); 333 | 334 | // The String that uniquely identifies the availability rule. 335 | $rule_id = "default"; 336 | 337 | $response = $cronofy->deleteAvailabilityRule($rule_id); 338 | 339 | ``` 340 | 341 | ## Create or Update Availability Rule 342 | 343 | To create or update an availability rule for the authenticated account: 344 | 345 | ```php 346 | $cronofy = new Cronofy\Cronofy([ 347 | "client_id" => "CRONOFY_CLIENT_ID", 348 | "client_secret" => "CRONOFY_CLIENT_SECRET", 349 | "access_token" => "AccessToken", 350 | "refresh_token" => "RefreshToken" 351 | ]); 352 | 353 | // The details of the event to create or update: 354 | $params = [ 355 | "availability_rule_id" => "default", 356 | "calendar_ids" => ["cal_123"], 357 | "tzid" => "America/Chicago", 358 | "weekly_periods" => [ 359 | [ 360 | "day" => "monday", 361 | "start_time" => "09:30", 362 | "end_time" => "12:30" 363 | ], 364 | [ 365 | "day" => "wednesday", 366 | "start_time" => "09:30", 367 | "end_time" => "12:30" 368 | ] 369 | ] 370 | ]; 371 | 372 | $response = $cronofy->createAvailabilityRule($params); 373 | 374 | ``` 375 | 376 | ## Make a Batch request 377 | 378 | Send multiple requests as a batch of operations via the [Batch][(https://docs.cronofy.com/developers/api/batch/) endpoint. 379 | 380 | ```php 381 | $cronofy = new Cronofy\Cronofy([ 382 | "client_id" => "CRONOFY_CLIENT_ID", 383 | "client_secret" => "CRONOFY_CLIENT_SECRET", 384 | "access_token" => "AccessToken", 385 | "refresh_token" => "RefreshToken" 386 | ]); 387 | 388 | $eventData = [ 389 | 'calendar_id' => 'CalendarID', 390 | 'event_id' => 'myapp-event-001', 391 | 'summary' => 'Wyld Stallyns band practice', 392 | 'start' => date("Y-m-d", strtotime('tomorrow')) . "T15:30:00Z", 393 | 'end' => date("Y-m-d", strtotime('tomorrow')) . "T17:00:00Z", 394 | ]; 395 | 396 | $batch = Batch::create() 397 | ->upsertEvent($calendarId, $testEventData) 398 | ->deleteEvent($calendarId, $testEventId) 399 | 400 | try { 401 | $result = $cronofy->executeBatch($batch); 402 | 403 | foreach ($result->responses() as $response) { 404 | // $response->status(); 405 | // $response->headers(); 406 | // $response->data(); 407 | // $response->request(); 408 | } 409 | } catch (PartialBatchFailureException $exception) { 410 | $result = $exception->result(); 411 | 412 | foreach ($result->errors() as $response) { 413 | // $response->status(); 414 | // $response->headers(); 415 | // $response->data(); 416 | // $response->request(); 417 | } 418 | } 419 | ``` 420 | 421 | ## Running unit tests 422 | 423 | ```shell 424 | make test 425 | ``` 426 | 427 | ## Links 428 | 429 | * [API documentation](https://www.cronofy.com/developers/api) 430 | * [API mailing list](https://groups.google.com/d/forum/cronofy-api) 431 | 432 | ## Migration guides 433 | 434 | * `v0.X.X` -> `v1.0.0`: [version `1.0.0`](https://github.com/cronofy/cronofy-php/releases/tag/v1.0.0) adds namespacing and standardizes the names of public methods to camelCase (whereas previously some methods were named with camel_case). Where in v0.29.0 you would have written `$cronofy = new Cronofy();` and `$calendar = $cronofy->list_calendars();`, for v1.0.0 you should write `$cronofy = new Cronofy\Cronofy();` and `$calendar = $cronofy->listCalendars();`. 435 | 436 | ## A feature I want is not in the SDK, how do I get it? 437 | 438 | We add features to this SDK as they are requested, to focus on developing the Cronofy API. 439 | 440 | If you're comfortable contributing support for an endpoint or attribute, then we love to receive pull requests! 441 | Please create a PR mentioning the feature/API endpoint you’ve added and we’ll review it as soon as we can. 442 | 443 | If you would like to request a feature is added by our team then please let us know by getting in touch via [support@cronofy.com](mailto:support@cronofy.com). 444 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cronofy/cronofy", 3 | "description": "SDK for Cronofy - the Scheduling Platform for Business", 4 | "version": "v1.7.3", 5 | "require": { 6 | "php": ">=7.1" 7 | }, 8 | "autoload": { 9 | "files": [ 10 | "src/Cronofy.php" 11 | ], 12 | "psr-4": { 13 | "Cronofy\\": "src/" 14 | } 15 | }, 16 | "keywords": [ 17 | "cronofy", 18 | "calendar", 19 | "oauth", 20 | "oauth2", 21 | "client", 22 | "authorization", 23 | "authentication" 24 | ], 25 | "minimum-stability": "beta", 26 | "license": "MIT", 27 | "require-dev": { 28 | "phpunit/phpunit": "^8", 29 | "squizlabs/php_codesniffer": "3.*" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "31781d4e30860fdd7d03648b6db71552", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "doctrine/instantiator", 12 | "version": "1.5.0", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/doctrine/instantiator.git", 16 | "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", 21 | "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": "^7.1 || ^8.0" 26 | }, 27 | "require-dev": { 28 | "doctrine/coding-standard": "^9 || ^11", 29 | "ext-pdo": "*", 30 | "ext-phar": "*", 31 | "phpbench/phpbench": "^0.16 || ^1", 32 | "phpstan/phpstan": "^1.4", 33 | "phpstan/phpstan-phpunit": "^1", 34 | "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", 35 | "vimeo/psalm": "^4.30 || ^5.4" 36 | }, 37 | "type": "library", 38 | "autoload": { 39 | "psr-4": { 40 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 41 | } 42 | }, 43 | "notification-url": "https://packagist.org/downloads/", 44 | "license": [ 45 | "MIT" 46 | ], 47 | "authors": [ 48 | { 49 | "name": "Marco Pivetta", 50 | "email": "ocramius@gmail.com", 51 | "homepage": "https://ocramius.github.io/" 52 | } 53 | ], 54 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 55 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 56 | "keywords": [ 57 | "constructor", 58 | "instantiate" 59 | ], 60 | "support": { 61 | "issues": "https://github.com/doctrine/instantiator/issues", 62 | "source": "https://github.com/doctrine/instantiator/tree/1.5.0" 63 | }, 64 | "funding": [ 65 | { 66 | "url": "https://www.doctrine-project.org/sponsorship.html", 67 | "type": "custom" 68 | }, 69 | { 70 | "url": "https://www.patreon.com/phpdoctrine", 71 | "type": "patreon" 72 | }, 73 | { 74 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", 75 | "type": "tidelift" 76 | } 77 | ], 78 | "time": "2022-12-30T00:15:36+00:00" 79 | }, 80 | { 81 | "name": "myclabs/deep-copy", 82 | "version": "1.11.0", 83 | "source": { 84 | "type": "git", 85 | "url": "https://github.com/myclabs/DeepCopy.git", 86 | "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" 87 | }, 88 | "dist": { 89 | "type": "zip", 90 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", 91 | "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", 92 | "shasum": "" 93 | }, 94 | "require": { 95 | "php": "^7.1 || ^8.0" 96 | }, 97 | "conflict": { 98 | "doctrine/collections": "<1.6.8", 99 | "doctrine/common": "<2.13.3 || >=3,<3.2.2" 100 | }, 101 | "require-dev": { 102 | "doctrine/collections": "^1.6.8", 103 | "doctrine/common": "^2.13.3 || ^3.2.2", 104 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 105 | }, 106 | "type": "library", 107 | "autoload": { 108 | "files": [ 109 | "src/DeepCopy/deep_copy.php" 110 | ], 111 | "psr-4": { 112 | "DeepCopy\\": "src/DeepCopy/" 113 | } 114 | }, 115 | "notification-url": "https://packagist.org/downloads/", 116 | "license": [ 117 | "MIT" 118 | ], 119 | "description": "Create deep copies (clones) of your objects", 120 | "keywords": [ 121 | "clone", 122 | "copy", 123 | "duplicate", 124 | "object", 125 | "object graph" 126 | ], 127 | "support": { 128 | "issues": "https://github.com/myclabs/DeepCopy/issues", 129 | "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" 130 | }, 131 | "funding": [ 132 | { 133 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 134 | "type": "tidelift" 135 | } 136 | ], 137 | "time": "2022-03-03T13:19:32+00:00" 138 | }, 139 | { 140 | "name": "phar-io/manifest", 141 | "version": "2.0.3", 142 | "source": { 143 | "type": "git", 144 | "url": "https://github.com/phar-io/manifest.git", 145 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53" 146 | }, 147 | "dist": { 148 | "type": "zip", 149 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", 150 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53", 151 | "shasum": "" 152 | }, 153 | "require": { 154 | "ext-dom": "*", 155 | "ext-phar": "*", 156 | "ext-xmlwriter": "*", 157 | "phar-io/version": "^3.0.1", 158 | "php": "^7.2 || ^8.0" 159 | }, 160 | "type": "library", 161 | "extra": { 162 | "branch-alias": { 163 | "dev-master": "2.0.x-dev" 164 | } 165 | }, 166 | "autoload": { 167 | "classmap": [ 168 | "src/" 169 | ] 170 | }, 171 | "notification-url": "https://packagist.org/downloads/", 172 | "license": [ 173 | "BSD-3-Clause" 174 | ], 175 | "authors": [ 176 | { 177 | "name": "Arne Blankerts", 178 | "email": "arne@blankerts.de", 179 | "role": "Developer" 180 | }, 181 | { 182 | "name": "Sebastian Heuer", 183 | "email": "sebastian@phpeople.de", 184 | "role": "Developer" 185 | }, 186 | { 187 | "name": "Sebastian Bergmann", 188 | "email": "sebastian@phpunit.de", 189 | "role": "Developer" 190 | } 191 | ], 192 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 193 | "support": { 194 | "issues": "https://github.com/phar-io/manifest/issues", 195 | "source": "https://github.com/phar-io/manifest/tree/2.0.3" 196 | }, 197 | "time": "2021-07-20T11:28:43+00:00" 198 | }, 199 | { 200 | "name": "phar-io/version", 201 | "version": "3.2.1", 202 | "source": { 203 | "type": "git", 204 | "url": "https://github.com/phar-io/version.git", 205 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 206 | }, 207 | "dist": { 208 | "type": "zip", 209 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 210 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 211 | "shasum": "" 212 | }, 213 | "require": { 214 | "php": "^7.2 || ^8.0" 215 | }, 216 | "type": "library", 217 | "autoload": { 218 | "classmap": [ 219 | "src/" 220 | ] 221 | }, 222 | "notification-url": "https://packagist.org/downloads/", 223 | "license": [ 224 | "BSD-3-Clause" 225 | ], 226 | "authors": [ 227 | { 228 | "name": "Arne Blankerts", 229 | "email": "arne@blankerts.de", 230 | "role": "Developer" 231 | }, 232 | { 233 | "name": "Sebastian Heuer", 234 | "email": "sebastian@phpeople.de", 235 | "role": "Developer" 236 | }, 237 | { 238 | "name": "Sebastian Bergmann", 239 | "email": "sebastian@phpunit.de", 240 | "role": "Developer" 241 | } 242 | ], 243 | "description": "Library for handling version information and constraints", 244 | "support": { 245 | "issues": "https://github.com/phar-io/version/issues", 246 | "source": "https://github.com/phar-io/version/tree/3.2.1" 247 | }, 248 | "time": "2022-02-21T01:04:05+00:00" 249 | }, 250 | { 251 | "name": "phpunit/php-code-coverage", 252 | "version": "7.0.15", 253 | "source": { 254 | "type": "git", 255 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 256 | "reference": "819f92bba8b001d4363065928088de22f25a3a48" 257 | }, 258 | "dist": { 259 | "type": "zip", 260 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48", 261 | "reference": "819f92bba8b001d4363065928088de22f25a3a48", 262 | "shasum": "" 263 | }, 264 | "require": { 265 | "ext-dom": "*", 266 | "ext-xmlwriter": "*", 267 | "php": ">=7.2", 268 | "phpunit/php-file-iterator": "^2.0.2", 269 | "phpunit/php-text-template": "^1.2.1", 270 | "phpunit/php-token-stream": "^3.1.3 || ^4.0", 271 | "sebastian/code-unit-reverse-lookup": "^1.0.1", 272 | "sebastian/environment": "^4.2.2", 273 | "sebastian/version": "^2.0.1", 274 | "theseer/tokenizer": "^1.1.3" 275 | }, 276 | "require-dev": { 277 | "phpunit/phpunit": "^8.2.2" 278 | }, 279 | "suggest": { 280 | "ext-xdebug": "^2.7.2" 281 | }, 282 | "type": "library", 283 | "extra": { 284 | "branch-alias": { 285 | "dev-master": "7.0-dev" 286 | } 287 | }, 288 | "autoload": { 289 | "classmap": [ 290 | "src/" 291 | ] 292 | }, 293 | "notification-url": "https://packagist.org/downloads/", 294 | "license": [ 295 | "BSD-3-Clause" 296 | ], 297 | "authors": [ 298 | { 299 | "name": "Sebastian Bergmann", 300 | "email": "sebastian@phpunit.de", 301 | "role": "lead" 302 | } 303 | ], 304 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 305 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 306 | "keywords": [ 307 | "coverage", 308 | "testing", 309 | "xunit" 310 | ], 311 | "support": { 312 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 313 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" 314 | }, 315 | "funding": [ 316 | { 317 | "url": "https://github.com/sebastianbergmann", 318 | "type": "github" 319 | } 320 | ], 321 | "time": "2021-07-26T12:20:09+00:00" 322 | }, 323 | { 324 | "name": "phpunit/php-file-iterator", 325 | "version": "2.0.5", 326 | "source": { 327 | "type": "git", 328 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 329 | "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5" 330 | }, 331 | "dist": { 332 | "type": "zip", 333 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", 334 | "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", 335 | "shasum": "" 336 | }, 337 | "require": { 338 | "php": ">=7.1" 339 | }, 340 | "require-dev": { 341 | "phpunit/phpunit": "^8.5" 342 | }, 343 | "type": "library", 344 | "extra": { 345 | "branch-alias": { 346 | "dev-master": "2.0.x-dev" 347 | } 348 | }, 349 | "autoload": { 350 | "classmap": [ 351 | "src/" 352 | ] 353 | }, 354 | "notification-url": "https://packagist.org/downloads/", 355 | "license": [ 356 | "BSD-3-Clause" 357 | ], 358 | "authors": [ 359 | { 360 | "name": "Sebastian Bergmann", 361 | "email": "sebastian@phpunit.de", 362 | "role": "lead" 363 | } 364 | ], 365 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 366 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 367 | "keywords": [ 368 | "filesystem", 369 | "iterator" 370 | ], 371 | "support": { 372 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 373 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5" 374 | }, 375 | "funding": [ 376 | { 377 | "url": "https://github.com/sebastianbergmann", 378 | "type": "github" 379 | } 380 | ], 381 | "time": "2021-12-02T12:42:26+00:00" 382 | }, 383 | { 384 | "name": "phpunit/php-text-template", 385 | "version": "1.2.1", 386 | "source": { 387 | "type": "git", 388 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 389 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 390 | }, 391 | "dist": { 392 | "type": "zip", 393 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 394 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 395 | "shasum": "" 396 | }, 397 | "require": { 398 | "php": ">=5.3.3" 399 | }, 400 | "type": "library", 401 | "autoload": { 402 | "classmap": [ 403 | "src/" 404 | ] 405 | }, 406 | "notification-url": "https://packagist.org/downloads/", 407 | "license": [ 408 | "BSD-3-Clause" 409 | ], 410 | "authors": [ 411 | { 412 | "name": "Sebastian Bergmann", 413 | "email": "sebastian@phpunit.de", 414 | "role": "lead" 415 | } 416 | ], 417 | "description": "Simple template engine.", 418 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 419 | "keywords": [ 420 | "template" 421 | ], 422 | "support": { 423 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 424 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" 425 | }, 426 | "time": "2015-06-21T13:50:34+00:00" 427 | }, 428 | { 429 | "name": "phpunit/php-timer", 430 | "version": "2.1.3", 431 | "source": { 432 | "type": "git", 433 | "url": "https://github.com/sebastianbergmann/php-timer.git", 434 | "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" 435 | }, 436 | "dist": { 437 | "type": "zip", 438 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", 439 | "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", 440 | "shasum": "" 441 | }, 442 | "require": { 443 | "php": ">=7.1" 444 | }, 445 | "require-dev": { 446 | "phpunit/phpunit": "^8.5" 447 | }, 448 | "type": "library", 449 | "extra": { 450 | "branch-alias": { 451 | "dev-master": "2.1-dev" 452 | } 453 | }, 454 | "autoload": { 455 | "classmap": [ 456 | "src/" 457 | ] 458 | }, 459 | "notification-url": "https://packagist.org/downloads/", 460 | "license": [ 461 | "BSD-3-Clause" 462 | ], 463 | "authors": [ 464 | { 465 | "name": "Sebastian Bergmann", 466 | "email": "sebastian@phpunit.de", 467 | "role": "lead" 468 | } 469 | ], 470 | "description": "Utility class for timing", 471 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 472 | "keywords": [ 473 | "timer" 474 | ], 475 | "support": { 476 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 477 | "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" 478 | }, 479 | "funding": [ 480 | { 481 | "url": "https://github.com/sebastianbergmann", 482 | "type": "github" 483 | } 484 | ], 485 | "time": "2020-11-30T08:20:02+00:00" 486 | }, 487 | { 488 | "name": "phpunit/php-token-stream", 489 | "version": "4.0.4", 490 | "source": { 491 | "type": "git", 492 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 493 | "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3" 494 | }, 495 | "dist": { 496 | "type": "zip", 497 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/a853a0e183b9db7eed023d7933a858fa1c8d25a3", 498 | "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3", 499 | "shasum": "" 500 | }, 501 | "require": { 502 | "ext-tokenizer": "*", 503 | "php": "^7.3 || ^8.0" 504 | }, 505 | "require-dev": { 506 | "phpunit/phpunit": "^9.0" 507 | }, 508 | "type": "library", 509 | "extra": { 510 | "branch-alias": { 511 | "dev-master": "4.0-dev" 512 | } 513 | }, 514 | "autoload": { 515 | "classmap": [ 516 | "src/" 517 | ] 518 | }, 519 | "notification-url": "https://packagist.org/downloads/", 520 | "license": [ 521 | "BSD-3-Clause" 522 | ], 523 | "authors": [ 524 | { 525 | "name": "Sebastian Bergmann", 526 | "email": "sebastian@phpunit.de" 527 | } 528 | ], 529 | "description": "Wrapper around PHP's tokenizer extension.", 530 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 531 | "keywords": [ 532 | "tokenizer" 533 | ], 534 | "support": { 535 | "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", 536 | "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" 537 | }, 538 | "funding": [ 539 | { 540 | "url": "https://github.com/sebastianbergmann", 541 | "type": "github" 542 | } 543 | ], 544 | "abandoned": true, 545 | "time": "2020-08-04T08:28:15+00:00" 546 | }, 547 | { 548 | "name": "phpunit/phpunit", 549 | "version": "8.5.31", 550 | "source": { 551 | "type": "git", 552 | "url": "https://github.com/sebastianbergmann/phpunit.git", 553 | "reference": "33c126b09a42de5c99e5e8032b54e8221264a74e" 554 | }, 555 | "dist": { 556 | "type": "zip", 557 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/33c126b09a42de5c99e5e8032b54e8221264a74e", 558 | "reference": "33c126b09a42de5c99e5e8032b54e8221264a74e", 559 | "shasum": "" 560 | }, 561 | "require": { 562 | "doctrine/instantiator": "^1.3.1", 563 | "ext-dom": "*", 564 | "ext-json": "*", 565 | "ext-libxml": "*", 566 | "ext-mbstring": "*", 567 | "ext-xml": "*", 568 | "ext-xmlwriter": "*", 569 | "myclabs/deep-copy": "^1.10.0", 570 | "phar-io/manifest": "^2.0.3", 571 | "phar-io/version": "^3.0.2", 572 | "php": ">=7.2", 573 | "phpunit/php-code-coverage": "^7.0.12", 574 | "phpunit/php-file-iterator": "^2.0.4", 575 | "phpunit/php-text-template": "^1.2.1", 576 | "phpunit/php-timer": "^2.1.2", 577 | "sebastian/comparator": "^3.0.5", 578 | "sebastian/diff": "^3.0.2", 579 | "sebastian/environment": "^4.2.3", 580 | "sebastian/exporter": "^3.1.5", 581 | "sebastian/global-state": "^3.0.0", 582 | "sebastian/object-enumerator": "^3.0.3", 583 | "sebastian/resource-operations": "^2.0.1", 584 | "sebastian/type": "^1.1.3", 585 | "sebastian/version": "^2.0.1" 586 | }, 587 | "suggest": { 588 | "ext-soap": "*", 589 | "ext-xdebug": "*", 590 | "phpunit/php-invoker": "^2.0.0" 591 | }, 592 | "bin": [ 593 | "phpunit" 594 | ], 595 | "type": "library", 596 | "extra": { 597 | "branch-alias": { 598 | "dev-master": "8.5-dev" 599 | } 600 | }, 601 | "autoload": { 602 | "classmap": [ 603 | "src/" 604 | ] 605 | }, 606 | "notification-url": "https://packagist.org/downloads/", 607 | "license": [ 608 | "BSD-3-Clause" 609 | ], 610 | "authors": [ 611 | { 612 | "name": "Sebastian Bergmann", 613 | "email": "sebastian@phpunit.de", 614 | "role": "lead" 615 | } 616 | ], 617 | "description": "The PHP Unit Testing framework.", 618 | "homepage": "https://phpunit.de/", 619 | "keywords": [ 620 | "phpunit", 621 | "testing", 622 | "xunit" 623 | ], 624 | "support": { 625 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 626 | "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.31" 627 | }, 628 | "funding": [ 629 | { 630 | "url": "https://phpunit.de/sponsors.html", 631 | "type": "custom" 632 | }, 633 | { 634 | "url": "https://github.com/sebastianbergmann", 635 | "type": "github" 636 | }, 637 | { 638 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 639 | "type": "tidelift" 640 | } 641 | ], 642 | "time": "2022-10-28T05:57:37+00:00" 643 | }, 644 | { 645 | "name": "sebastian/code-unit-reverse-lookup", 646 | "version": "1.0.2", 647 | "source": { 648 | "type": "git", 649 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 650 | "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" 651 | }, 652 | "dist": { 653 | "type": "zip", 654 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", 655 | "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", 656 | "shasum": "" 657 | }, 658 | "require": { 659 | "php": ">=5.6" 660 | }, 661 | "require-dev": { 662 | "phpunit/phpunit": "^8.5" 663 | }, 664 | "type": "library", 665 | "extra": { 666 | "branch-alias": { 667 | "dev-master": "1.0.x-dev" 668 | } 669 | }, 670 | "autoload": { 671 | "classmap": [ 672 | "src/" 673 | ] 674 | }, 675 | "notification-url": "https://packagist.org/downloads/", 676 | "license": [ 677 | "BSD-3-Clause" 678 | ], 679 | "authors": [ 680 | { 681 | "name": "Sebastian Bergmann", 682 | "email": "sebastian@phpunit.de" 683 | } 684 | ], 685 | "description": "Looks up which function or method a line of code belongs to", 686 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 687 | "support": { 688 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 689 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" 690 | }, 691 | "funding": [ 692 | { 693 | "url": "https://github.com/sebastianbergmann", 694 | "type": "github" 695 | } 696 | ], 697 | "time": "2020-11-30T08:15:22+00:00" 698 | }, 699 | { 700 | "name": "sebastian/comparator", 701 | "version": "3.0.5", 702 | "source": { 703 | "type": "git", 704 | "url": "https://github.com/sebastianbergmann/comparator.git", 705 | "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770" 706 | }, 707 | "dist": { 708 | "type": "zip", 709 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dc7ceb4a24aede938c7af2a9ed1de09609ca770", 710 | "reference": "1dc7ceb4a24aede938c7af2a9ed1de09609ca770", 711 | "shasum": "" 712 | }, 713 | "require": { 714 | "php": ">=7.1", 715 | "sebastian/diff": "^3.0", 716 | "sebastian/exporter": "^3.1" 717 | }, 718 | "require-dev": { 719 | "phpunit/phpunit": "^8.5" 720 | }, 721 | "type": "library", 722 | "extra": { 723 | "branch-alias": { 724 | "dev-master": "3.0-dev" 725 | } 726 | }, 727 | "autoload": { 728 | "classmap": [ 729 | "src/" 730 | ] 731 | }, 732 | "notification-url": "https://packagist.org/downloads/", 733 | "license": [ 734 | "BSD-3-Clause" 735 | ], 736 | "authors": [ 737 | { 738 | "name": "Sebastian Bergmann", 739 | "email": "sebastian@phpunit.de" 740 | }, 741 | { 742 | "name": "Jeff Welch", 743 | "email": "whatthejeff@gmail.com" 744 | }, 745 | { 746 | "name": "Volker Dusch", 747 | "email": "github@wallbash.com" 748 | }, 749 | { 750 | "name": "Bernhard Schussek", 751 | "email": "bschussek@2bepublished.at" 752 | } 753 | ], 754 | "description": "Provides the functionality to compare PHP values for equality", 755 | "homepage": "https://github.com/sebastianbergmann/comparator", 756 | "keywords": [ 757 | "comparator", 758 | "compare", 759 | "equality" 760 | ], 761 | "support": { 762 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 763 | "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.5" 764 | }, 765 | "funding": [ 766 | { 767 | "url": "https://github.com/sebastianbergmann", 768 | "type": "github" 769 | } 770 | ], 771 | "time": "2022-09-14T12:31:48+00:00" 772 | }, 773 | { 774 | "name": "sebastian/diff", 775 | "version": "3.0.3", 776 | "source": { 777 | "type": "git", 778 | "url": "https://github.com/sebastianbergmann/diff.git", 779 | "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" 780 | }, 781 | "dist": { 782 | "type": "zip", 783 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", 784 | "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", 785 | "shasum": "" 786 | }, 787 | "require": { 788 | "php": ">=7.1" 789 | }, 790 | "require-dev": { 791 | "phpunit/phpunit": "^7.5 || ^8.0", 792 | "symfony/process": "^2 || ^3.3 || ^4" 793 | }, 794 | "type": "library", 795 | "extra": { 796 | "branch-alias": { 797 | "dev-master": "3.0-dev" 798 | } 799 | }, 800 | "autoload": { 801 | "classmap": [ 802 | "src/" 803 | ] 804 | }, 805 | "notification-url": "https://packagist.org/downloads/", 806 | "license": [ 807 | "BSD-3-Clause" 808 | ], 809 | "authors": [ 810 | { 811 | "name": "Sebastian Bergmann", 812 | "email": "sebastian@phpunit.de" 813 | }, 814 | { 815 | "name": "Kore Nordmann", 816 | "email": "mail@kore-nordmann.de" 817 | } 818 | ], 819 | "description": "Diff implementation", 820 | "homepage": "https://github.com/sebastianbergmann/diff", 821 | "keywords": [ 822 | "diff", 823 | "udiff", 824 | "unidiff", 825 | "unified diff" 826 | ], 827 | "support": { 828 | "issues": "https://github.com/sebastianbergmann/diff/issues", 829 | "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" 830 | }, 831 | "funding": [ 832 | { 833 | "url": "https://github.com/sebastianbergmann", 834 | "type": "github" 835 | } 836 | ], 837 | "time": "2020-11-30T07:59:04+00:00" 838 | }, 839 | { 840 | "name": "sebastian/environment", 841 | "version": "4.2.4", 842 | "source": { 843 | "type": "git", 844 | "url": "https://github.com/sebastianbergmann/environment.git", 845 | "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" 846 | }, 847 | "dist": { 848 | "type": "zip", 849 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", 850 | "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", 851 | "shasum": "" 852 | }, 853 | "require": { 854 | "php": ">=7.1" 855 | }, 856 | "require-dev": { 857 | "phpunit/phpunit": "^7.5" 858 | }, 859 | "suggest": { 860 | "ext-posix": "*" 861 | }, 862 | "type": "library", 863 | "extra": { 864 | "branch-alias": { 865 | "dev-master": "4.2-dev" 866 | } 867 | }, 868 | "autoload": { 869 | "classmap": [ 870 | "src/" 871 | ] 872 | }, 873 | "notification-url": "https://packagist.org/downloads/", 874 | "license": [ 875 | "BSD-3-Clause" 876 | ], 877 | "authors": [ 878 | { 879 | "name": "Sebastian Bergmann", 880 | "email": "sebastian@phpunit.de" 881 | } 882 | ], 883 | "description": "Provides functionality to handle HHVM/PHP environments", 884 | "homepage": "http://www.github.com/sebastianbergmann/environment", 885 | "keywords": [ 886 | "Xdebug", 887 | "environment", 888 | "hhvm" 889 | ], 890 | "support": { 891 | "issues": "https://github.com/sebastianbergmann/environment/issues", 892 | "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" 893 | }, 894 | "funding": [ 895 | { 896 | "url": "https://github.com/sebastianbergmann", 897 | "type": "github" 898 | } 899 | ], 900 | "time": "2020-11-30T07:53:42+00:00" 901 | }, 902 | { 903 | "name": "sebastian/exporter", 904 | "version": "3.1.5", 905 | "source": { 906 | "type": "git", 907 | "url": "https://github.com/sebastianbergmann/exporter.git", 908 | "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6" 909 | }, 910 | "dist": { 911 | "type": "zip", 912 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/73a9676f2833b9a7c36968f9d882589cd75511e6", 913 | "reference": "73a9676f2833b9a7c36968f9d882589cd75511e6", 914 | "shasum": "" 915 | }, 916 | "require": { 917 | "php": ">=7.0", 918 | "sebastian/recursion-context": "^3.0" 919 | }, 920 | "require-dev": { 921 | "ext-mbstring": "*", 922 | "phpunit/phpunit": "^8.5" 923 | }, 924 | "type": "library", 925 | "extra": { 926 | "branch-alias": { 927 | "dev-master": "3.1.x-dev" 928 | } 929 | }, 930 | "autoload": { 931 | "classmap": [ 932 | "src/" 933 | ] 934 | }, 935 | "notification-url": "https://packagist.org/downloads/", 936 | "license": [ 937 | "BSD-3-Clause" 938 | ], 939 | "authors": [ 940 | { 941 | "name": "Sebastian Bergmann", 942 | "email": "sebastian@phpunit.de" 943 | }, 944 | { 945 | "name": "Jeff Welch", 946 | "email": "whatthejeff@gmail.com" 947 | }, 948 | { 949 | "name": "Volker Dusch", 950 | "email": "github@wallbash.com" 951 | }, 952 | { 953 | "name": "Adam Harvey", 954 | "email": "aharvey@php.net" 955 | }, 956 | { 957 | "name": "Bernhard Schussek", 958 | "email": "bschussek@gmail.com" 959 | } 960 | ], 961 | "description": "Provides the functionality to export PHP variables for visualization", 962 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 963 | "keywords": [ 964 | "export", 965 | "exporter" 966 | ], 967 | "support": { 968 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 969 | "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.5" 970 | }, 971 | "funding": [ 972 | { 973 | "url": "https://github.com/sebastianbergmann", 974 | "type": "github" 975 | } 976 | ], 977 | "time": "2022-09-14T06:00:17+00:00" 978 | }, 979 | { 980 | "name": "sebastian/global-state", 981 | "version": "3.0.2", 982 | "source": { 983 | "type": "git", 984 | "url": "https://github.com/sebastianbergmann/global-state.git", 985 | "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921" 986 | }, 987 | "dist": { 988 | "type": "zip", 989 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/de036ec91d55d2a9e0db2ba975b512cdb1c23921", 990 | "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921", 991 | "shasum": "" 992 | }, 993 | "require": { 994 | "php": ">=7.2", 995 | "sebastian/object-reflector": "^1.1.1", 996 | "sebastian/recursion-context": "^3.0" 997 | }, 998 | "require-dev": { 999 | "ext-dom": "*", 1000 | "phpunit/phpunit": "^8.0" 1001 | }, 1002 | "suggest": { 1003 | "ext-uopz": "*" 1004 | }, 1005 | "type": "library", 1006 | "extra": { 1007 | "branch-alias": { 1008 | "dev-master": "3.0-dev" 1009 | } 1010 | }, 1011 | "autoload": { 1012 | "classmap": [ 1013 | "src/" 1014 | ] 1015 | }, 1016 | "notification-url": "https://packagist.org/downloads/", 1017 | "license": [ 1018 | "BSD-3-Clause" 1019 | ], 1020 | "authors": [ 1021 | { 1022 | "name": "Sebastian Bergmann", 1023 | "email": "sebastian@phpunit.de" 1024 | } 1025 | ], 1026 | "description": "Snapshotting of global state", 1027 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1028 | "keywords": [ 1029 | "global state" 1030 | ], 1031 | "support": { 1032 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1033 | "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.2" 1034 | }, 1035 | "funding": [ 1036 | { 1037 | "url": "https://github.com/sebastianbergmann", 1038 | "type": "github" 1039 | } 1040 | ], 1041 | "time": "2022-02-10T06:55:38+00:00" 1042 | }, 1043 | { 1044 | "name": "sebastian/object-enumerator", 1045 | "version": "3.0.4", 1046 | "source": { 1047 | "type": "git", 1048 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1049 | "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" 1050 | }, 1051 | "dist": { 1052 | "type": "zip", 1053 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", 1054 | "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", 1055 | "shasum": "" 1056 | }, 1057 | "require": { 1058 | "php": ">=7.0", 1059 | "sebastian/object-reflector": "^1.1.1", 1060 | "sebastian/recursion-context": "^3.0" 1061 | }, 1062 | "require-dev": { 1063 | "phpunit/phpunit": "^6.0" 1064 | }, 1065 | "type": "library", 1066 | "extra": { 1067 | "branch-alias": { 1068 | "dev-master": "3.0.x-dev" 1069 | } 1070 | }, 1071 | "autoload": { 1072 | "classmap": [ 1073 | "src/" 1074 | ] 1075 | }, 1076 | "notification-url": "https://packagist.org/downloads/", 1077 | "license": [ 1078 | "BSD-3-Clause" 1079 | ], 1080 | "authors": [ 1081 | { 1082 | "name": "Sebastian Bergmann", 1083 | "email": "sebastian@phpunit.de" 1084 | } 1085 | ], 1086 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1087 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1088 | "support": { 1089 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1090 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" 1091 | }, 1092 | "funding": [ 1093 | { 1094 | "url": "https://github.com/sebastianbergmann", 1095 | "type": "github" 1096 | } 1097 | ], 1098 | "time": "2020-11-30T07:40:27+00:00" 1099 | }, 1100 | { 1101 | "name": "sebastian/object-reflector", 1102 | "version": "1.1.2", 1103 | "source": { 1104 | "type": "git", 1105 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1106 | "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" 1107 | }, 1108 | "dist": { 1109 | "type": "zip", 1110 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", 1111 | "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", 1112 | "shasum": "" 1113 | }, 1114 | "require": { 1115 | "php": ">=7.0" 1116 | }, 1117 | "require-dev": { 1118 | "phpunit/phpunit": "^6.0" 1119 | }, 1120 | "type": "library", 1121 | "extra": { 1122 | "branch-alias": { 1123 | "dev-master": "1.1-dev" 1124 | } 1125 | }, 1126 | "autoload": { 1127 | "classmap": [ 1128 | "src/" 1129 | ] 1130 | }, 1131 | "notification-url": "https://packagist.org/downloads/", 1132 | "license": [ 1133 | "BSD-3-Clause" 1134 | ], 1135 | "authors": [ 1136 | { 1137 | "name": "Sebastian Bergmann", 1138 | "email": "sebastian@phpunit.de" 1139 | } 1140 | ], 1141 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1142 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1143 | "support": { 1144 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1145 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" 1146 | }, 1147 | "funding": [ 1148 | { 1149 | "url": "https://github.com/sebastianbergmann", 1150 | "type": "github" 1151 | } 1152 | ], 1153 | "time": "2020-11-30T07:37:18+00:00" 1154 | }, 1155 | { 1156 | "name": "sebastian/recursion-context", 1157 | "version": "3.0.1", 1158 | "source": { 1159 | "type": "git", 1160 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1161 | "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" 1162 | }, 1163 | "dist": { 1164 | "type": "zip", 1165 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", 1166 | "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", 1167 | "shasum": "" 1168 | }, 1169 | "require": { 1170 | "php": ">=7.0" 1171 | }, 1172 | "require-dev": { 1173 | "phpunit/phpunit": "^6.0" 1174 | }, 1175 | "type": "library", 1176 | "extra": { 1177 | "branch-alias": { 1178 | "dev-master": "3.0.x-dev" 1179 | } 1180 | }, 1181 | "autoload": { 1182 | "classmap": [ 1183 | "src/" 1184 | ] 1185 | }, 1186 | "notification-url": "https://packagist.org/downloads/", 1187 | "license": [ 1188 | "BSD-3-Clause" 1189 | ], 1190 | "authors": [ 1191 | { 1192 | "name": "Sebastian Bergmann", 1193 | "email": "sebastian@phpunit.de" 1194 | }, 1195 | { 1196 | "name": "Jeff Welch", 1197 | "email": "whatthejeff@gmail.com" 1198 | }, 1199 | { 1200 | "name": "Adam Harvey", 1201 | "email": "aharvey@php.net" 1202 | } 1203 | ], 1204 | "description": "Provides functionality to recursively process PHP variables", 1205 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1206 | "support": { 1207 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1208 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" 1209 | }, 1210 | "funding": [ 1211 | { 1212 | "url": "https://github.com/sebastianbergmann", 1213 | "type": "github" 1214 | } 1215 | ], 1216 | "time": "2020-11-30T07:34:24+00:00" 1217 | }, 1218 | { 1219 | "name": "sebastian/resource-operations", 1220 | "version": "2.0.2", 1221 | "source": { 1222 | "type": "git", 1223 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1224 | "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" 1225 | }, 1226 | "dist": { 1227 | "type": "zip", 1228 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", 1229 | "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", 1230 | "shasum": "" 1231 | }, 1232 | "require": { 1233 | "php": ">=7.1" 1234 | }, 1235 | "type": "library", 1236 | "extra": { 1237 | "branch-alias": { 1238 | "dev-master": "2.0-dev" 1239 | } 1240 | }, 1241 | "autoload": { 1242 | "classmap": [ 1243 | "src/" 1244 | ] 1245 | }, 1246 | "notification-url": "https://packagist.org/downloads/", 1247 | "license": [ 1248 | "BSD-3-Clause" 1249 | ], 1250 | "authors": [ 1251 | { 1252 | "name": "Sebastian Bergmann", 1253 | "email": "sebastian@phpunit.de" 1254 | } 1255 | ], 1256 | "description": "Provides a list of PHP built-in functions that operate on resources", 1257 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1258 | "support": { 1259 | "issues": "https://github.com/sebastianbergmann/resource-operations/issues", 1260 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" 1261 | }, 1262 | "funding": [ 1263 | { 1264 | "url": "https://github.com/sebastianbergmann", 1265 | "type": "github" 1266 | } 1267 | ], 1268 | "time": "2020-11-30T07:30:19+00:00" 1269 | }, 1270 | { 1271 | "name": "sebastian/type", 1272 | "version": "1.1.4", 1273 | "source": { 1274 | "type": "git", 1275 | "url": "https://github.com/sebastianbergmann/type.git", 1276 | "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" 1277 | }, 1278 | "dist": { 1279 | "type": "zip", 1280 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", 1281 | "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", 1282 | "shasum": "" 1283 | }, 1284 | "require": { 1285 | "php": ">=7.2" 1286 | }, 1287 | "require-dev": { 1288 | "phpunit/phpunit": "^8.2" 1289 | }, 1290 | "type": "library", 1291 | "extra": { 1292 | "branch-alias": { 1293 | "dev-master": "1.1-dev" 1294 | } 1295 | }, 1296 | "autoload": { 1297 | "classmap": [ 1298 | "src/" 1299 | ] 1300 | }, 1301 | "notification-url": "https://packagist.org/downloads/", 1302 | "license": [ 1303 | "BSD-3-Clause" 1304 | ], 1305 | "authors": [ 1306 | { 1307 | "name": "Sebastian Bergmann", 1308 | "email": "sebastian@phpunit.de", 1309 | "role": "lead" 1310 | } 1311 | ], 1312 | "description": "Collection of value objects that represent the types of the PHP type system", 1313 | "homepage": "https://github.com/sebastianbergmann/type", 1314 | "support": { 1315 | "issues": "https://github.com/sebastianbergmann/type/issues", 1316 | "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" 1317 | }, 1318 | "funding": [ 1319 | { 1320 | "url": "https://github.com/sebastianbergmann", 1321 | "type": "github" 1322 | } 1323 | ], 1324 | "time": "2020-11-30T07:25:11+00:00" 1325 | }, 1326 | { 1327 | "name": "sebastian/version", 1328 | "version": "2.0.1", 1329 | "source": { 1330 | "type": "git", 1331 | "url": "https://github.com/sebastianbergmann/version.git", 1332 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 1333 | }, 1334 | "dist": { 1335 | "type": "zip", 1336 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 1337 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 1338 | "shasum": "" 1339 | }, 1340 | "require": { 1341 | "php": ">=5.6" 1342 | }, 1343 | "type": "library", 1344 | "extra": { 1345 | "branch-alias": { 1346 | "dev-master": "2.0.x-dev" 1347 | } 1348 | }, 1349 | "autoload": { 1350 | "classmap": [ 1351 | "src/" 1352 | ] 1353 | }, 1354 | "notification-url": "https://packagist.org/downloads/", 1355 | "license": [ 1356 | "BSD-3-Clause" 1357 | ], 1358 | "authors": [ 1359 | { 1360 | "name": "Sebastian Bergmann", 1361 | "email": "sebastian@phpunit.de", 1362 | "role": "lead" 1363 | } 1364 | ], 1365 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1366 | "homepage": "https://github.com/sebastianbergmann/version", 1367 | "support": { 1368 | "issues": "https://github.com/sebastianbergmann/version/issues", 1369 | "source": "https://github.com/sebastianbergmann/version/tree/master" 1370 | }, 1371 | "time": "2016-10-03T07:35:21+00:00" 1372 | }, 1373 | { 1374 | "name": "squizlabs/php_codesniffer", 1375 | "version": "3.7.1", 1376 | "source": { 1377 | "type": "git", 1378 | "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", 1379 | "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" 1380 | }, 1381 | "dist": { 1382 | "type": "zip", 1383 | "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", 1384 | "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", 1385 | "shasum": "" 1386 | }, 1387 | "require": { 1388 | "ext-simplexml": "*", 1389 | "ext-tokenizer": "*", 1390 | "ext-xmlwriter": "*", 1391 | "php": ">=5.4.0" 1392 | }, 1393 | "require-dev": { 1394 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" 1395 | }, 1396 | "bin": [ 1397 | "bin/phpcs", 1398 | "bin/phpcbf" 1399 | ], 1400 | "type": "library", 1401 | "extra": { 1402 | "branch-alias": { 1403 | "dev-master": "3.x-dev" 1404 | } 1405 | }, 1406 | "notification-url": "https://packagist.org/downloads/", 1407 | "license": [ 1408 | "BSD-3-Clause" 1409 | ], 1410 | "authors": [ 1411 | { 1412 | "name": "Greg Sherwood", 1413 | "role": "lead" 1414 | } 1415 | ], 1416 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", 1417 | "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", 1418 | "keywords": [ 1419 | "phpcs", 1420 | "standards" 1421 | ], 1422 | "support": { 1423 | "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", 1424 | "source": "https://github.com/squizlabs/PHP_CodeSniffer", 1425 | "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" 1426 | }, 1427 | "time": "2022-06-18T07:21:10+00:00" 1428 | }, 1429 | { 1430 | "name": "theseer/tokenizer", 1431 | "version": "1.2.1", 1432 | "source": { 1433 | "type": "git", 1434 | "url": "https://github.com/theseer/tokenizer.git", 1435 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" 1436 | }, 1437 | "dist": { 1438 | "type": "zip", 1439 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", 1440 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", 1441 | "shasum": "" 1442 | }, 1443 | "require": { 1444 | "ext-dom": "*", 1445 | "ext-tokenizer": "*", 1446 | "ext-xmlwriter": "*", 1447 | "php": "^7.2 || ^8.0" 1448 | }, 1449 | "type": "library", 1450 | "autoload": { 1451 | "classmap": [ 1452 | "src/" 1453 | ] 1454 | }, 1455 | "notification-url": "https://packagist.org/downloads/", 1456 | "license": [ 1457 | "BSD-3-Clause" 1458 | ], 1459 | "authors": [ 1460 | { 1461 | "name": "Arne Blankerts", 1462 | "email": "arne@blankerts.de", 1463 | "role": "Developer" 1464 | } 1465 | ], 1466 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1467 | "support": { 1468 | "issues": "https://github.com/theseer/tokenizer/issues", 1469 | "source": "https://github.com/theseer/tokenizer/tree/1.2.1" 1470 | }, 1471 | "funding": [ 1472 | { 1473 | "url": "https://github.com/theseer", 1474 | "type": "github" 1475 | } 1476 | ], 1477 | "time": "2021-07-28T10:34:58+00:00" 1478 | } 1479 | ], 1480 | "aliases": [], 1481 | "minimum-stability": "beta", 1482 | "stability-flags": [], 1483 | "prefer-stable": false, 1484 | "prefer-lowest": false, 1485 | "platform": { 1486 | "php": ">=7.1" 1487 | }, 1488 | "platform-dev": [], 1489 | "plugin-api-version": "2.3.0" 1490 | } 1491 | -------------------------------------------------------------------------------- /dev-smoke-test.php: -------------------------------------------------------------------------------- 1 | getenv("CLIENT_ID"), 18 | "client_secret" => getenv("CLIENT_SECRET"), 19 | "access_token" => getenv("ACCESS_TOKEN"), 20 | "refresh_token" => getenv("REFRESH_TOKEN"), 21 | "data_center" => $dataCenter, 22 | ]); 23 | 24 | $sub = getenv("SUB"); 25 | $calendarId = getenv("CALENDAR_ID"); 26 | $start = date("Y-m-d", strtotime('tomorrow')) . "T09:30:00Z"; 27 | $end = date("Y-m-d", strtotime('tomorrow')) . "T10:00:00Z"; 28 | 29 | $yesterday = date("Y-m-d", strtotime('yesterday')); 30 | $next_week = date("Y-m-d", strtotime('next week')); 31 | 32 | $testEventId = 'php-smoke-test-001'; 33 | $testEventData = [ 34 | 'calendar_id' => $calendarId, 35 | 'event_id' => $testEventId, 36 | 'summary' => 'PHP SDK test event 001', 37 | 'description' => 'Just checking this thing is on!', 38 | 'start' => $start, 39 | 'end' => $end, 40 | ]; 41 | 42 | echo "Writing test events to " . $dataCenter . ", account " . $sub . ", calendar " . $calendarId . "\n"; 43 | 44 | if ( $testBatch ) { 45 | $batch = Batch::create() 46 | ->upsertEvent($calendarId, $testEventData) 47 | ->deleteEvent($calendarId, $testEventId) 48 | ->deleteEvent("fake-calendar-id", "just-want-it-to-fail") 49 | ->upsertEvent($calendarId, []); 50 | 51 | try { 52 | $result = $cronofy->executeBatch($batch); 53 | 54 | } catch (PartialBatchFailureException $exception) { 55 | echo "PARTIAL FAILURE\n\n"; 56 | $result = $exception->result(); 57 | } finally { 58 | foreach ($result->responses() as $index=>$response) { 59 | echo "Request " . $index . " - " . $response->request()->method() . " " . $response->request()->relativeUrl() . "\n"; 60 | echo $response->hasSuccessStatus() ? " Success" : " Failed"; 61 | echo "\n"; 62 | echo " status " . $response->status() . "\n"; 63 | 64 | echo " headers "; 65 | $headers = $response->headers(); 66 | print_r($headers); 67 | echo "\n"; 68 | 69 | echo " data "; 70 | $data = $response->data(); 71 | print_r($data); 72 | echo "\n\n"; 73 | } 74 | } 75 | } 76 | 77 | if( $testAvailablePeriod ) { 78 | echo "Creating AvailablePeriod\n"; 79 | $ap_id = "test_available_period_001"; 80 | 81 | $params = [ 82 | "available_period_id" => $ap_id, 83 | "start" => $start, 84 | "end" => $end, 85 | ]; 86 | 87 | $cronofy->createAvailablePeriod($params); 88 | 89 | echo "Reading Available Period\n"; 90 | 91 | $readParams = [ 92 | "from" => $yesterday, 93 | "to" => $next_week, 94 | "tzid" => "Europe/London", 95 | ]; 96 | 97 | $periods = $cronofy->readAvailablePeriods($readParams); 98 | foreach($periods->each() as $available_period){ 99 | print_r($available_period); 100 | } 101 | 102 | echo "\n"; 103 | echo "Deleting Available Period\n"; 104 | 105 | $params = [ 106 | "available_period_id" => $ap_id, 107 | ]; 108 | 109 | $result = $cronofy->deleteAvailablePeriod($params); 110 | print_r($result); 111 | 112 | $periods = $cronofy->readAvailablePeriods($readParams); 113 | foreach($periods->each() as $available_period){ 114 | print_r($available_period); 115 | } 116 | } 117 | 118 | if( $testRecurrence ) { 119 | echo "\n"; 120 | echo "Creating event with recurrence\n"; 121 | 122 | $recurrenceEventParams = $testEventData; 123 | $recurrenceEventParams['recurrence'] = [ 124 | "rules" => [ 125 | [ 126 | "frequency" => "daily", 127 | "interval" => 2, 128 | "count" => 3, 129 | ], 130 | ], 131 | ]; 132 | 133 | $cronofy->upsertEvent($recurrenceEventParams); 134 | echo "\n"; 135 | } 136 | 137 | if($testRTS){ 138 | echo "Checking RTS\n"; 139 | 140 | $event = [ 141 | "event_id" => "php-smoke-test-002", 142 | "summary" => "Add to Calendar test event", 143 | ]; 144 | 145 | $availability = [ 146 | "participants" => [ 147 | [ 148 | "members" => [ 149 | [ 150 | "sub" => $sub, 151 | "calendar_ids" => [$calendarId] 152 | ] 153 | ], 154 | "required" => "all" 155 | ] 156 | ], 157 | "event" => $event, 158 | "required_duration" => [ 159 | "minutes" => 60 160 | ], 161 | "available_periods" => [ 162 | [ 163 | "start" => $start, 164 | "end" => $end 165 | ] 166 | ] 167 | ]; 168 | $target_calendars = [ 169 | [ 170 | "sub" => $sub, 171 | "calendar_id" => $calendarId 172 | ] 173 | ]; 174 | $tzid = 'Europe/London'; 175 | 176 | $params = [ 177 | "event" => $event, 178 | "target_calendars" => $target_calendars, 179 | "availability" => $availability, 180 | "tzid" => $tzid, 181 | "oauth" => [ 182 | "redirect_uri" => "http://local.cronofy.com/redirect" 183 | ], 184 | "formatting" => [ 185 | "hour_format" => "H", 186 | ], 187 | "minimum_notice" => [ 188 | "hours" => 2 189 | ], 190 | "redirect_urls" => [ 191 | "completed_url" => "http://local.cronofy.com/complete", 192 | "no_times_suitable_url" => "http://local.cronofy.com/need_more_times_please", 193 | ], 194 | "callback_urls" => [ 195 | "completed_url" => "http://local.cronofy.com/callback/complete", 196 | ], 197 | "event_creation" => "single", 198 | ]; 199 | 200 | $rts = $cronofy->realTimeScheduling($params); 201 | echo "RTS Created:\n"; 202 | print_r($rts); 203 | 204 | $rts_id = $rts["real_time_scheduling"]["real_time_scheduling_id"]; 205 | 206 | echo "Cancelling RTS\n"; 207 | $params =[ 208 | "id" => $rts_id, 209 | "display_message" => "Testing disable" 210 | ]; 211 | 212 | $cronofy->realTimeSchedulingDisable($params); 213 | } 214 | -------------------------------------------------------------------------------- /init: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | brew install php jq 4 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tests 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The Cronofy coding standard based on PSR2 but relaxed slightly. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Batch/Batch.php: -------------------------------------------------------------------------------- 1 | buildEventsPath($calendarId); 20 | 21 | return $this->addPostRequest($path, $data); 22 | } 23 | 24 | public function deleteEvent(string $calendarId, string $eventId): self 25 | { 26 | $path = $this->buildEventsPath($calendarId); 27 | $data = ['event_id' => $eventId]; 28 | 29 | return $this->addDeleteRequest($path, $data); 30 | } 31 | 32 | public function updateExternalEvent(string $calendarId, array $data): self 33 | { 34 | $path = $this->buildEventsPath($calendarId); 35 | 36 | return $this->addPostRequest($path, $data); 37 | } 38 | 39 | public function deleteExternalEvent(string $calendarId, string $eventUid): self 40 | { 41 | $path = $this->buildEventsPath($calendarId); 42 | $data = ['event_uid' => $eventUid]; 43 | 44 | return $this->addDeleteRequest($path, $data); 45 | } 46 | 47 | public function upsertAvailablePeriod(array $data): self 48 | { 49 | $path = $this->buildAvailablePeriodsPath(); 50 | 51 | return $this->addPostRequest($path, $data); 52 | } 53 | 54 | public function deleteAvailablePeriod(string $availablePeriodId): self 55 | { 56 | $path = $this->buildAvailablePeriodsPath(); 57 | $data = ['available_period_id' => $availablePeriodId]; 58 | 59 | return $this->addDeleteRequest($path, $data); 60 | } 61 | 62 | private function buildEventsPath(string $calendarId): string 63 | { 64 | return sprintf('/%s/calendars/%s/events', Cronofy::API_VERSION, $calendarId); 65 | } 66 | 67 | private function buildAvailablePeriodsPath(): string 68 | { 69 | return sprintf('/%s/available_periods', Cronofy::API_VERSION); 70 | } 71 | 72 | private function addPostRequest(string $relativeUrl, array $data): self 73 | { 74 | return $this->addRequest('POST', $relativeUrl, $data); 75 | } 76 | 77 | private function addDeleteRequest(string $relativeUrl, array $data): self 78 | { 79 | return $this->addRequest('DELETE', $relativeUrl, $data); 80 | } 81 | 82 | private function addRequest(string $method, string $relativeUrl, array $data): self 83 | { 84 | $this->requests[] = new BatchRequest($method, $relativeUrl, $data); 85 | 86 | return $this; 87 | } 88 | 89 | public function requests(): array 90 | { 91 | return $this->requests; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Batch/BatchRequest.php: -------------------------------------------------------------------------------- 1 | method = $method; 14 | $this->relativeUrl = $relativeUrl; 15 | $this->data = $data; 16 | } 17 | 18 | public function method(): string 19 | { 20 | return $this->method; 21 | } 22 | 23 | public function relativeUrl(): string 24 | { 25 | return $this->relativeUrl; 26 | } 27 | 28 | public function data(): array 29 | { 30 | return $this->data; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Batch/BatchResponse.php: -------------------------------------------------------------------------------- 1 | status = $status; 15 | $this->headers = $headers; 16 | $this->data = $data; 17 | $this->request = $request; 18 | } 19 | 20 | public function status(): int 21 | { 22 | return $this->status; 23 | } 24 | 25 | public function hasSuccessStatus(): bool 26 | { 27 | $status = $this->status(); 28 | 29 | return $status >= 200 && $status < 300; 30 | } 31 | 32 | public function hasErrorStatus(): bool 33 | { 34 | return !$this->hasSuccessStatus(); 35 | } 36 | 37 | public function headers(): ?array 38 | { 39 | return $this->headers; 40 | } 41 | 42 | public function data(): ?array 43 | { 44 | return $this->data; 45 | } 46 | 47 | public function request(): BatchRequest 48 | { 49 | return $this->request; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Batch/BatchResult.php: -------------------------------------------------------------------------------- 1 | responses = $responses; 13 | } 14 | 15 | public function responses(): array 16 | { 17 | return $this->responses; 18 | } 19 | 20 | public function errors(): array 21 | { 22 | if ($this->errors === null) { 23 | $this->errors = []; 24 | 25 | foreach ($this->responses as $response) { 26 | if ($response->hasErrorStatus()) { 27 | $this->errors[] = $response; 28 | } 29 | } 30 | } 31 | 32 | return $this->errors; 33 | } 34 | 35 | public function hasErrors(): bool 36 | { 37 | return count($this->errors()) > 0; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Exception/CronofyException.php: -------------------------------------------------------------------------------- 1 | errorDetails = $errorDetails; 14 | 15 | parent::__construct($message, $code, null); 16 | } 17 | 18 | public function error_details() 19 | { 20 | return $this->errorDetails; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Exception/PartialBatchFailureException.php: -------------------------------------------------------------------------------- 1 | result = $result; 15 | 16 | parent::__construct($message); 17 | } 18 | 19 | public function result(): BatchResult 20 | { 21 | return $this->result; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Http/CurlRequest.php: -------------------------------------------------------------------------------- 1 | useragent = $useragent; 14 | } 15 | 16 | public function httpGet($url, array $auth_headers) 17 | { 18 | $curl = curl_init(); 19 | curl_setopt($curl, CURLOPT_URL, $url); 20 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 21 | curl_setopt($curl, CURLOPT_HTTPHEADER, $auth_headers); 22 | curl_setopt($curl, CURLOPT_USERAGENT, $this->useragent); 23 | // empty string means send all supported encoding types 24 | curl_setopt($curl, CURLOPT_ENCODING, ''); 25 | $result = curl_exec($curl); 26 | if (curl_errno($curl) > 0) { 27 | throw new CronofyException(curl_error($curl), 2); 28 | } 29 | $status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); 30 | curl_close($curl); 31 | 32 | return [$result, $status_code]; 33 | } 34 | 35 | public function getPage($url, array $auth_headers, $url_params = "") 36 | { 37 | return $this->httpGet($url.$url_params, $auth_headers); 38 | } 39 | 40 | public function httpPost($url, array $params, array $auth_headers) 41 | { 42 | $curl = curl_init(); 43 | curl_setopt($curl, CURLOPT_URL, $url); 44 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 45 | curl_setopt($curl, CURLOPT_HTTPHEADER, $auth_headers); 46 | curl_setopt($curl, CURLOPT_USERAGENT, $this->useragent); 47 | curl_setopt($curl, CURLOPT_POST, 1); 48 | curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params)); 49 | curl_setopt($curl, CURLOPT_VERBOSE, false); 50 | // empty string means send all supported encoding types 51 | curl_setopt($curl, CURLOPT_ENCODING, ''); 52 | $result = curl_exec($curl); 53 | if (curl_errno($curl) > 0) { 54 | throw new CronofyException(curl_error($curl), 3); 55 | } 56 | $status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); 57 | curl_close($curl); 58 | 59 | return [$result, $status_code]; 60 | } 61 | 62 | public function httpDelete($url, array $params, array $auth_headers) 63 | { 64 | $curl = curl_init(); 65 | curl_setopt($curl, CURLOPT_URL, $url); 66 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 67 | curl_setopt($curl, CURLOPT_HTTPHEADER, $auth_headers); 68 | curl_setopt($curl, CURLOPT_USERAGENT, $this->useragent); 69 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); 70 | curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params)); 71 | // empty string means send all supported encoding types 72 | curl_setopt($curl, CURLOPT_ENCODING, ''); 73 | $result = curl_exec($curl); 74 | if (curl_errno($curl) > 0) { 75 | throw new CronofyException(curl_error($curl), 4); 76 | } 77 | $status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); 78 | curl_close($curl); 79 | 80 | return [$result, $status_code]; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Http/HttpRequest.php: -------------------------------------------------------------------------------- 1 | cronofy = $cronofy; 17 | $this->itemsKey = $itemsKey; 18 | $this->authHeaders = $authHeaders; 19 | $this->url = $url; 20 | $this->urlParams = $urlParams; 21 | $this->firstPage = $this->getPage($url, $urlParams); 22 | } 23 | 24 | public function each() 25 | { 26 | $page = $this->firstPage; 27 | 28 | for ($i = 0; $i < count($page[$this->itemsKey]); $i++) { 29 | yield $page[$this->itemsKey][$i]; 30 | } 31 | 32 | while (isset($page["pages"]["next_page"])) { 33 | $page = $this->getPage($page["pages"]["next_page"]); 34 | 35 | for ($i = 0; $i < count($page[$this->itemsKey]); $i++) { 36 | yield $page[$this->itemsKey][$i]; 37 | } 38 | } 39 | } 40 | 41 | public function getIterator() 42 | { 43 | return $this->each(); 44 | } 45 | 46 | private function getPage($url, $urlParams = '') 47 | { 48 | list ($result, $status_code) = $this->cronofy->httpClient->getPage($url, $this->authHeaders, $urlParams); 49 | 50 | return $this->cronofy->handleResponse($result, $status_code); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/AccessibleCalendarsTest.php: -------------------------------------------------------------------------------- 1 | createMock(HttpRequest::class); 35 | $http->expects($this->once()) 36 | ->method('httpGet') 37 | ->with( 38 | 'https://api.cronofy.com/v1/accessible_calendars?' 39 | . http_build_query(['profile_id' => $profileId = 'profile-id']), 40 | [ 41 | 'Authorization: Bearer accessToken', 42 | 'Host: api.cronofy.com', 43 | ] 44 | ) 45 | ->willReturn([$accessible_calendars_response, 200]); 46 | 47 | $cronofy = new Cronofy([ 48 | "client_id" => "clientId", 49 | "client_secret" => "clientSecret", 50 | "access_token" => "accessToken", 51 | "refresh_token" => "refreshToken", 52 | "http_client" => $http, 53 | ]); 54 | 55 | $accesibleCalendars = $cronofy->listAccessibleCalendars($profileId); 56 | 57 | $this->assertEquals( 58 | json_decode($accessible_calendars_response, true), 59 | $accesibleCalendars 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/ApplicationCalendarTest.php: -------------------------------------------------------------------------------- 1 | "clientId", 16 | "client_secret" => "clientSecret", 17 | "application_calendar_id" => $application_calendar_id, 18 | ]; 19 | 20 | $application_calendar_response = '{ 21 | "token_type":"bearer", 22 | "access_token":"fffff", 23 | "expires_in":3600, 24 | "refresh_token":"2222", 25 | "scope":"read_write", 26 | "application_calendar_id":"my-unique-string", 27 | "sub":"apc_567236000909002", 28 | "linking_profile":{ 29 | "provider_name":"cronofy", 30 | "profile_id":"pro_n23kjnwrw2", 31 | "profile_name":"n23kjnwrw2" 32 | } 33 | }'; 34 | 35 | $http = $this->createMock(HttpRequest::class); 36 | $http->expects($this->once()) 37 | ->method('httpPost') 38 | ->with( 39 | $this->equalTo('https://api.cronofy.com/v1/application_calendars'), 40 | $this->equalTo($request_params), 41 | $this->equalTo([ 42 | 'Host: api.cronofy.com', 43 | 'Content-Type: application/json; charset=utf-8' 44 | ]) 45 | ) 46 | ->will($this->returnValue([$application_calendar_response, 200])); 47 | 48 | $cronofy = new Cronofy([ 49 | "client_id" => "clientId", 50 | "client_secret" => "clientSecret", 51 | "http_client" => $http, 52 | ]); 53 | 54 | 55 | $actual = $cronofy->applicationCalendar($application_calendar_id); 56 | 57 | $this->assertEquals($actual['sub'], "apc_567236000909002"); 58 | $this->assertEquals($cronofy->accessToken, "fffff"); 59 | $this->assertEquals($cronofy->refreshToken, "2222"); 60 | $this->assertEquals($cronofy->expiresIn, 3600); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/BatchTest.php: -------------------------------------------------------------------------------- 1 | httpClient = $this->createMock(HttpRequest::class); 28 | 29 | $this->cronofy = new Cronofy([ 30 | 'client_id' => 'clientId', 31 | 'client_secret' => 'clientSecret', 32 | 'access_token' => 'accessToken', 33 | 'refresh_token' => 'refreshToken', 34 | 'http_client' => $this->httpClient, 35 | ]); 36 | } 37 | 38 | public function testUpsertEvent() 39 | { 40 | $calendarId = 'calendar_id'; 41 | $data = $this->getUpsertEventData(); 42 | 43 | $expectedRequestMethod = 'POST'; 44 | $expectedRequestRelativeUrl = sprintf('/v1/calendars/%s/events', $calendarId); 45 | $expectedRequestData = $data; 46 | 47 | $expectedRequests = [ 48 | [ 49 | 'method' => $expectedRequestMethod, 50 | 'relative_url' => $expectedRequestRelativeUrl, 51 | 'data' => $expectedRequestData, 52 | ], 53 | ]; 54 | 55 | $expectedResponseStatus = 202; 56 | 57 | $mockResponses = [ 58 | [ 59 | 'status' => $expectedResponseStatus, 60 | ], 61 | ]; 62 | 63 | $this->makeBatchRequestExpectation($expectedRequests, $mockResponses); 64 | 65 | $batch = Batch::create()->upsertEvent($calendarId, $data); 66 | $result = $this->cronofy->executeBatch($batch); 67 | 68 | $this->assertInstanceOf(BatchResult::class, $result); 69 | 70 | $responses = $result->responses(); 71 | 72 | $this->assertCount(1, $responses); 73 | 74 | $response = $responses[0]; 75 | 76 | $this->assertEquals($expectedResponseStatus, $response->status()); 77 | $this->assertNull($response->headers()); 78 | $this->assertNull($response->data()); 79 | 80 | $request = $response->request(); 81 | 82 | $this->assertInstanceOf(BatchRequest::class, $request); 83 | $this->assertEquals($expectedRequestMethod, $request->method()); 84 | $this->assertEquals($expectedRequestRelativeUrl, $request->relativeUrl()); 85 | $this->assertEquals($expectedRequestData, $request->data()); 86 | } 87 | 88 | public function testUpdateExternalEvent() 89 | { 90 | $calendarId = 'calendar_id'; 91 | $data = $this->getUpdateExternalEventData(); 92 | 93 | $expectedRequestMethod = 'POST'; 94 | $expectedRequestRelativeUrl = sprintf('/v1/calendars/%s/events', $calendarId); 95 | $expectedRequestData = $data; 96 | 97 | $expectedRequests = [ 98 | [ 99 | 'method' => $expectedRequestMethod, 100 | 'relative_url' => $expectedRequestRelativeUrl, 101 | 'data' => $expectedRequestData, 102 | ], 103 | ]; 104 | 105 | $expectedResponseStatus = 202; 106 | 107 | $mockResponses = [ 108 | [ 109 | 'status' => $expectedResponseStatus, 110 | ], 111 | ]; 112 | 113 | $this->makeBatchRequestExpectation($expectedRequests, $mockResponses); 114 | 115 | $batch = Batch::create()->updateExternalEvent($calendarId, $data); 116 | $result = $this->cronofy->executeBatch($batch); 117 | 118 | $this->assertInstanceOf(BatchResult::class, $result); 119 | 120 | $responses = $result->responses(); 121 | 122 | $this->assertCount(1, $responses); 123 | 124 | $response = $responses[0]; 125 | 126 | $this->assertEquals($expectedResponseStatus, $response->status()); 127 | $this->assertNull($response->headers()); 128 | $this->assertNull($response->data()); 129 | 130 | $request = $response->request(); 131 | 132 | $this->assertInstanceOf(BatchRequest::class, $request); 133 | $this->assertEquals($expectedRequestMethod, $request->method()); 134 | $this->assertEquals($expectedRequestRelativeUrl, $request->relativeUrl()); 135 | $this->assertEquals($expectedRequestData, $request->data()); 136 | } 137 | 138 | public function testDeleteEvent() 139 | { 140 | $calendarId = 'calendar_id'; 141 | $eventId = 'event_id'; 142 | 143 | $expectedRequestMethod = 'DELETE'; 144 | $expectedRequestRelativeUrl = sprintf('/v1/calendars/%s/events', $calendarId); 145 | $expectedRequestData = [ 146 | 'event_id' => $eventId, 147 | ]; 148 | 149 | $expectedRequests = [ 150 | [ 151 | 'method' => $expectedRequestMethod, 152 | 'relative_url' => $expectedRequestRelativeUrl, 153 | 'data' => $expectedRequestData, 154 | ], 155 | ]; 156 | 157 | $expectedResponseStatus = 204; 158 | 159 | $mockResponses = [ 160 | [ 161 | 'status' => $expectedResponseStatus, 162 | ], 163 | ]; 164 | 165 | $this->makeBatchRequestExpectation($expectedRequests, $mockResponses); 166 | 167 | $batch = Batch::create()->deleteEvent($calendarId, $eventId); 168 | $result = $this->cronofy->executeBatch($batch); 169 | 170 | $this->assertInstanceOf(BatchResult::class, $result); 171 | 172 | $responses = $result->responses(); 173 | 174 | $this->assertCount(1, $responses); 175 | 176 | $response = $responses[0]; 177 | 178 | $this->assertEquals($expectedResponseStatus, $response->status()); 179 | $this->assertNull($response->headers()); 180 | $this->assertNull($response->data()); 181 | 182 | $request = $response->request(); 183 | 184 | $this->assertInstanceOf(BatchRequest::class, $request); 185 | $this->assertEquals($expectedRequestMethod, $request->method()); 186 | $this->assertEquals($expectedRequestRelativeUrl, $request->relativeUrl()); 187 | $this->assertEquals($expectedRequestData, $request->data()); 188 | } 189 | 190 | public function testDeleteExternalEvent() 191 | { 192 | $calendarId = 'calendar_id'; 193 | $externalEventId = 'external_event_id'; 194 | 195 | $expectedRequestMethod = 'DELETE'; 196 | $expectedRequestRelativeUrl = sprintf('/v1/calendars/%s/events', $calendarId); 197 | $expectedRequestData = [ 198 | 'event_uid' => $externalEventId, 199 | ]; 200 | 201 | $expectedRequests = [ 202 | [ 203 | 'method' => $expectedRequestMethod, 204 | 'relative_url' => $expectedRequestRelativeUrl, 205 | 'data' => $expectedRequestData, 206 | ], 207 | ]; 208 | 209 | $expectedResponseStatus = 204; 210 | $expectedResponseHeaders = [ 211 | 'Header' => 'Value', 212 | ]; 213 | 214 | $mockResponses = [ 215 | [ 216 | 'status' => $expectedResponseStatus, 217 | 'headers' => $expectedResponseHeaders, 218 | ], 219 | ]; 220 | 221 | $this->makeBatchRequestExpectation($expectedRequests, $mockResponses); 222 | 223 | $batch = Batch::create()->deleteExternalEvent($calendarId, $externalEventId); 224 | $result = $this->cronofy->executeBatch($batch); 225 | 226 | $this->assertInstanceOf(BatchResult::class, $result); 227 | 228 | $responses = $result->responses(); 229 | 230 | $this->assertCount(1, $responses); 231 | 232 | $response = $responses[0]; 233 | 234 | $this->assertEquals($expectedResponseStatus, $response->status()); 235 | $this->assertEquals($expectedResponseHeaders, $response->headers()); 236 | $this->assertNull($response->data()); 237 | 238 | $request = $response->request(); 239 | 240 | $this->assertInstanceOf(BatchRequest::class, $request); 241 | $this->assertEquals($expectedRequestMethod, $request->method()); 242 | $this->assertEquals($expectedRequestRelativeUrl, $request->relativeUrl()); 243 | $this->assertEquals($expectedRequestData, $request->data()); 244 | } 245 | 246 | public function testUpsertAvailablePeriod() 247 | { 248 | $data = $this->getUpsertAvailablePeriodData(); 249 | 250 | $expectedRequestMethod = 'POST'; 251 | $expectedRequestRelativeUrl = '/v1/available_periods'; 252 | $expectedRequestData = $data; 253 | 254 | $expectedRequests = [ 255 | [ 256 | 'method' => $expectedRequestMethod, 257 | 'relative_url' => $expectedRequestRelativeUrl, 258 | 'data' => $expectedRequestData, 259 | ], 260 | ]; 261 | 262 | $expectedResponseStatus = 202; 263 | 264 | $mockResponses = [ 265 | [ 266 | 'status' => $expectedResponseStatus, 267 | ], 268 | ]; 269 | 270 | $this->makeBatchRequestExpectation($expectedRequests, $mockResponses); 271 | 272 | $batch = Batch::create()->upsertAvailablePeriod($data); 273 | $result = $this->cronofy->executeBatch($batch); 274 | 275 | $this->assertInstanceOf(BatchResult::class, $result); 276 | 277 | $responses = $result->responses(); 278 | 279 | $this->assertCount(1, $responses); 280 | 281 | $response = $responses[0]; 282 | 283 | $this->assertEquals($expectedResponseStatus, $response->status()); 284 | $this->assertNull($response->headers()); 285 | $this->assertNull($response->data()); 286 | 287 | $request = $response->request(); 288 | 289 | $this->assertInstanceOf(BatchRequest::class, $request); 290 | $this->assertEquals($expectedRequestMethod, $request->method()); 291 | $this->assertEquals($expectedRequestRelativeUrl, $request->relativeUrl()); 292 | $this->assertEquals($expectedRequestData, $request->data()); 293 | } 294 | 295 | public function testDeleteAvailablePeriod() 296 | { 297 | $availablePeriodId = 'available_period_id'; 298 | 299 | $expectedRequestMethod = 'DELETE'; 300 | $expectedRequestRelativeUrl = '/v1/available_periods'; 301 | $expectedRequestData = [ 302 | 'available_period_id' => $availablePeriodId, 303 | ]; 304 | 305 | $expectedRequests = [ 306 | [ 307 | 'method' => $expectedRequestMethod, 308 | 'relative_url' => $expectedRequestRelativeUrl, 309 | 'data' => $expectedRequestData, 310 | ], 311 | ]; 312 | 313 | $expectedResponseStatus = 204; 314 | 315 | $mockResponses = [ 316 | [ 317 | 'status' => $expectedResponseStatus, 318 | ], 319 | ]; 320 | 321 | $this->makeBatchRequestExpectation($expectedRequests, $mockResponses); 322 | 323 | $batch = Batch::create()->deleteAvailablePeriod($availablePeriodId); 324 | $result = $this->cronofy->executeBatch($batch); 325 | 326 | $this->assertInstanceOf(BatchResult::class, $result); 327 | 328 | $responses = $result->responses(); 329 | 330 | $this->assertCount(1, $responses); 331 | 332 | $response = $responses[0]; 333 | 334 | $this->assertEquals($expectedResponseStatus, $response->status()); 335 | $this->assertNull($response->headers()); 336 | $this->assertNull($response->data()); 337 | 338 | $request = $response->request(); 339 | 340 | $this->assertInstanceOf(BatchRequest::class, $request); 341 | $this->assertEquals($expectedRequestMethod, $request->method()); 342 | $this->assertEquals($expectedRequestRelativeUrl, $request->relativeUrl()); 343 | $this->assertEquals($expectedRequestData, $request->data()); 344 | } 345 | 346 | public function testPartialBatchFailure() 347 | { 348 | $data = $this->getUpsertAvailablePeriodData(); 349 | unset($data['start']); 350 | 351 | $expectedRequestMethod = 'POST'; 352 | $expectedRequestRelativeUrl = '/v1/available_periods'; 353 | $expectedRequestData = $data; 354 | 355 | $expectedRequests = [ 356 | [ 357 | 'method' => $expectedRequestMethod, 358 | 'relative_url' => $expectedRequestRelativeUrl, 359 | 'data' => $expectedRequestData, 360 | ], 361 | ]; 362 | 363 | $expectedResponseStatus = 422; 364 | $expectedResponseData = [ 365 | 'errors' => [ 366 | 'start' => [ 367 | [ 368 | 'key' => 'errors.required', 369 | 'description' => 'start must be specified', 370 | ], 371 | ], 372 | ], 373 | ]; 374 | 375 | $mockResponses = [ 376 | [ 377 | 'status' => $expectedResponseStatus, 378 | 'data' => $expectedResponseData, 379 | ], 380 | ]; 381 | 382 | $this->makeBatchRequestExpectation($expectedRequests, $mockResponses); 383 | 384 | $batch = Batch::create()->upsertAvailablePeriod($data); 385 | 386 | try { 387 | $this->cronofy->executeBatch($batch); 388 | } catch (PartialBatchFailureException $exception) { 389 | $result = $exception->result(); 390 | 391 | $this->assertInstanceOf(BatchResult::class, $result); 392 | $this->assertTrue($result->hasErrors()); 393 | 394 | $errors = $result->errors(); 395 | 396 | $this->assertCount(1, $errors); 397 | 398 | $error = $errors[0]; 399 | 400 | $this->assertInstanceOf(BatchResponse::class, $error); 401 | $this->assertEquals($expectedResponseStatus, $error->status()); 402 | $this->assertNull($error->headers()); 403 | $this->assertEquals($expectedResponseData, $error->data()); 404 | 405 | $request = $error->request(); 406 | 407 | $this->assertInstanceOf(BatchRequest::class, $request); 408 | $this->assertEquals($expectedRequestMethod, $request->method()); 409 | $this->assertEquals($expectedRequestRelativeUrl, $request->relativeUrl()); 410 | $this->assertEquals($expectedRequestData, $request->data()); 411 | 412 | return; 413 | } 414 | 415 | $this->fail(sprintf('Expected exception of type "%s" to be thrown.', PartialBatchFailureException::class)); 416 | } 417 | 418 | private function makeBatchRequestExpectation(array $requests, array $responses): void 419 | { 420 | $this->httpClient 421 | ->expects($this->once()) 422 | ->method('httpPost') 423 | ->with( 424 | $this->equalTo('https://api.cronofy.com/v1/batch'), 425 | $this->equalTo([ 426 | 'batch' => $requests, 427 | ]) 428 | ) 429 | ->will( 430 | $this->returnValue([ 431 | json_encode([ 432 | 'batch' => $responses, 433 | ]), 434 | 207, 435 | ]) 436 | ); 437 | } 438 | 439 | private function getUpsertEventData(): array 440 | { 441 | return array_merge( 442 | ['event_id' => 'event_id'], 443 | $this->getBaseEventData() 444 | ); 445 | } 446 | 447 | private function getUpdateExternalEventData(): array 448 | { 449 | return array_merge( 450 | ['event_uid' => 'external_event_id'], 451 | $this->getBaseEventData() 452 | ); 453 | } 454 | 455 | private function getBaseEventData(): array 456 | { 457 | return [ 458 | 'summary' => 'Upsert Event Test', 459 | 'description' => 'description example', 460 | 'start' => '2017-01-01T12:00:00Z', 461 | 'end' => '2017-01-01T15:00:00Z', 462 | 'tzid' => 'Europe/London', 463 | 'location' => [ 464 | 'description' => 'board room', 465 | 'latitude' => '12.2344', 466 | 'longitude' => '45.2444', 467 | ], 468 | 'reminders' => [ 469 | ['minutes' => 30], 470 | ['minutes' => 1440], 471 | ], 472 | 'attendees' => [ 473 | 'invite' => [ 474 | ['email' => 'new_invitee@test.com', 'display_name' => 'New Invitee'], 475 | ], 476 | 'reject' => [ 477 | ['email' => 'old_invitee@test.com', 'display_name' => 'Old Invitee'], 478 | ], 479 | ], 480 | 'event_private' => true, 481 | 'reminders_create_only' => true, 482 | 'transparency' => 'opaque', 483 | 'color' => '#c6040f', 484 | 'conferencing' => [ 485 | 'profile_id' => 'default', 486 | ], 487 | ]; 488 | } 489 | 490 | private function getUpsertAvailablePeriodData(): array 491 | { 492 | return [ 493 | 'available_period_id' => 'available_period_id', 494 | 'start' => '2021-04-14T15:30:00Z', 495 | 'end' => '2021-04-14T17:00:00Z', 496 | ]; 497 | } 498 | } 499 | -------------------------------------------------------------------------------- /tests/CronofyTest.php: -------------------------------------------------------------------------------- 1 | "clientId"]); 16 | $params = [ 17 | 'redirect_uri' => $redirect_uri, 18 | 'scope' => ['read_account','list_calendars'] 19 | ]; 20 | $auth = $cronofy->getAuthorizationURL($params); 21 | 22 | $this->assertEquals("https://app.cronofy.com/oauth/authorize?response_type=code&client_id=clientId" 23 | . "&redirect_uri=http%3A%2F%2Fyoursite.dev%2Foauth2%2Fcallback&scope=read_account%20list_calendars", $auth); 24 | } 25 | 26 | public function testDelegatedScopeInAuthorizationUrl() 27 | { 28 | $redirect_uri = "http://yoursite.dev/oauth2/callback"; 29 | 30 | $cronofy = new Cronofy(["client_id" => "clientId"]); 31 | $params = [ 32 | 'redirect_uri' => $redirect_uri, 33 | 'scope' => ['read_account','list_calendars'], 34 | 'delegated_scope' => ['create_calendar', 'read_free_busy'] 35 | ]; 36 | $auth = $cronofy->getAuthorizationURL($params); 37 | 38 | $this->assertEquals("https://app.cronofy.com/oauth/authorize?response_type=code&client_id=clientId" 39 | . "&redirect_uri=http%3A%2F%2Fyoursite.dev%2Foauth2%2Fcallback" 40 | . "&scope=read_account%20list_calendars&delegated_scope=create_calendar%20read_free_busy", $auth); 41 | } 42 | 43 | public function testProviderNameInAuthorizationUrl() 44 | { 45 | $redirect_uri = "http://yoursite.dev/oauth2/callback"; 46 | 47 | $cronofy = new Cronofy(["client_id" => "clientId"]); 48 | $params = [ 49 | 'redirect_uri' => $redirect_uri, 50 | 'scope' => ['read_account','list_calendars'], 51 | 'provider_name' => 'office365' 52 | ]; 53 | $auth = $cronofy->getAuthorizationURL($params); 54 | 55 | $this->assertEquals("https://app.cronofy.com/oauth/authorize?response_type=code&client_id=clientId" 56 | . "&redirect_uri=http%3A%2F%2Fyoursite.dev%2Foauth2%2Fcallback&scope=read_account%20list_calendars&provider_name=office365", $auth); 57 | } 58 | 59 | public function testErrorHandling() 60 | { 61 | $args = [ 62 | "profile_id" => "pro_123", 63 | "name" => "My Calendar", 64 | ]; 65 | 66 | $error_response = '{"errors":{"event_id":[{"key":"errors.required","description":"required"}]}}'; 67 | 68 | $http = $this->createMock(HttpRequest::class); 69 | $http->expects($this->once()) 70 | ->method('httpPost') 71 | ->with( 72 | $this->equalTo('https://api.cronofy.com/v1/calendars'), 73 | $this->equalTo($args), 74 | $this->equalTo([ 75 | 'Authorization: Bearer accessToken', 76 | 'Host: api.cronofy.com', 77 | 'Content-Type: application/json; charset=utf-8', 78 | ]) 79 | ) 80 | ->will($this->returnValue([$error_response, 422])); 81 | 82 | $cronofy = new Cronofy([ 83 | "client_id" => "clientId", 84 | "client_secret" => "clientSecret", 85 | "access_token" => "accessToken", 86 | "refresh_token" => "refreshToken", 87 | "http_client" => $http, 88 | ]); 89 | 90 | $raised_error = false; 91 | 92 | try { 93 | $cronofy->createCalendar($args); 94 | } catch (CronofyException $exception) { 95 | $raised_error = true; 96 | $this->assertEquals(json_decode($error_response, true), $exception->error_details()); 97 | $this->assertEquals(422, $exception->getCode()); 98 | } 99 | 100 | $this->assertTrue($raised_error); 101 | } 102 | 103 | public function testRequestToken() 104 | { 105 | $request_params = [ 106 | "client_id" => "clientId", 107 | "client_secret" => "clientSecret", 108 | "grant_type" => "authorization_code", 109 | "code" => "MY_SECRET_CODE", 110 | "redirect_uri" => "http://example.com", 111 | ]; 112 | 113 | $token_response = '{ 114 | "token_type":"bearer", 115 | "access_token":"fffff", 116 | "expires_in":3600, 117 | "refresh_token":"2222", 118 | "scope":"read_write", 119 | "application_calendar_id":"my-unique-string", 120 | "sub":"apc_567236000909002", 121 | "linking_profile":{ 122 | "provider_name":"cronofy", 123 | "profile_id":"pro_n23kjnwrw2", 124 | "profile_name":"n23kjnwrw2" 125 | } 126 | }'; 127 | 128 | $http = $this->createMock(HttpRequest::class); 129 | $http->expects($this->once()) 130 | ->method('httpPost') 131 | ->with( 132 | $this->equalTo('https://api.cronofy.com/oauth/token'), 133 | $this->equalTo($request_params), 134 | $this->equalTo([ 135 | 'Host: api.cronofy.com', 136 | 'Content-Type: application/json; charset=utf-8' 137 | ]) 138 | ) 139 | ->will($this->returnValue([$token_response, 200])); 140 | 141 | $cronofy = new Cronofy([ 142 | "client_id" => "clientId", 143 | "client_secret" => "clientSecret", 144 | "http_client" => $http, 145 | ]); 146 | 147 | $args = [ 148 | "code" => "MY_SECRET_CODE", 149 | "redirect_uri" => "http://example.com", 150 | ]; 151 | 152 | $actual = $cronofy->requestToken($args); 153 | $this->assertTrue($actual); 154 | $this->assertEquals($cronofy->accessToken, "fffff"); 155 | $this->assertEquals($cronofy->refreshToken, "2222"); 156 | $this->assertEquals($cronofy->expiresIn, 3600); 157 | $this->assertEquals($cronofy->tokens, json_decode($token_response, true)); 158 | } 159 | 160 | public function testGetAccount() 161 | { 162 | $http = $this->createMock(HttpRequest::class); 163 | $http->expects($this->once()) 164 | ->method('httpGet') 165 | ->with( 166 | $this->equalTo('https://api.cronofy.com/v1/account'), 167 | $this->equalTo([ 168 | 'Authorization: Bearer accessToken', 169 | 'Host: api.cronofy.com' 170 | ]) 171 | ) 172 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 173 | 174 | $cronofy = new Cronofy([ 175 | "client_id" => "clientId", 176 | "client_secret" => "clientSecret", 177 | "access_token" => "accessToken", 178 | "refresh_token" => "refreshToken", 179 | "http_client" => $http, 180 | ]); 181 | 182 | $actual = $cronofy->getAccount(); 183 | $this->assertNotNull($actual); 184 | } 185 | 186 | public function testRevokeAuthorizationWithString() 187 | { 188 | $http = $this->createMock(HttpRequest::class); 189 | $http->expects($this->once()) 190 | ->method('httpPost') 191 | ->with( 192 | $this->equalTo('https://api.cronofy.com/oauth/token/revoke'), 193 | $this->equalTo([ 194 | 'client_id' => 'clientId', 195 | 'client_secret' => 'clientSecret', 196 | 'token' => 'sometoken' 197 | ]), 198 | $this->equalTo([ 199 | 'Host: api.cronofy.com', 200 | 'Content-Type: application/json; charset=utf-8' 201 | ]) 202 | ) 203 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 204 | 205 | $cronofy = new Cronofy([ 206 | "client_id" => "clientId", 207 | "client_secret" => "clientSecret", 208 | "http_client" => $http, 209 | ]); 210 | 211 | $actual = $cronofy->revokeAuthorization('sometoken'); 212 | $this->assertNotNull($actual); 213 | } 214 | 215 | 216 | 217 | public function testRevokeAuthorizationWithToken() 218 | { 219 | $http = $this->createMock(HttpRequest::class); 220 | $http->expects($this->once()) 221 | ->method('httpPost') 222 | ->with( 223 | $this->equalTo('https://api.cronofy.com/oauth/token/revoke'), 224 | $this->equalTo([ 225 | 'client_id' => 'clientId', 226 | 'client_secret' => 'clientSecret', 227 | 'token' => 'sometoken' 228 | ]), 229 | $this->equalTo([ 230 | 'Host: api.cronofy.com', 231 | 'Content-Type: application/json; charset=utf-8' 232 | ]) 233 | ) 234 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 235 | 236 | $cronofy = new Cronofy([ 237 | "client_id" => "clientId", 238 | "client_secret" => "clientSecret", 239 | "http_client" => $http, 240 | ]); 241 | 242 | $actual = $cronofy->revokeAuthorization(['token' => 'sometoken']); 243 | $this->assertNotNull($actual); 244 | } 245 | 246 | 247 | 248 | public function testRevokeAuthorizationWithSub() 249 | { 250 | $http = $this->createMock(HttpRequest::class); 251 | $http->expects($this->once()) 252 | ->method('httpPost') 253 | ->with( 254 | $this->equalTo('https://api.cronofy.com/oauth/token/revoke'), 255 | $this->equalTo([ 256 | 'client_id' => 'clientId', 257 | 'client_secret' => 'clientSecret', 258 | 'sub' => 'somesub' 259 | ]), 260 | $this->equalTo([ 261 | 'Host: api.cronofy.com', 262 | 'Content-Type: application/json; charset=utf-8' 263 | ]) 264 | ) 265 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 266 | 267 | $cronofy = new Cronofy([ 268 | "client_id" => "clientId", 269 | "client_secret" => "clientSecret", 270 | "http_client" => $http, 271 | ]); 272 | 273 | $actual = $cronofy->revokeAuthorization(['sub' => 'somesub']); 274 | $this->assertNotNull($actual); 275 | } 276 | 277 | public function testDeleteEvent() 278 | { 279 | $params = ["event_id" => "evt_456"]; 280 | 281 | $http = $this->createMock(HttpRequest::class); 282 | $http->expects($this->once()) 283 | ->method('httpDelete') 284 | ->with( 285 | $this->equalTo('https://api.cronofy.com/v1/calendars/cal_123/events'), 286 | $this->equalTo($params), 287 | $this->equalTo([ 288 | 'Authorization: Bearer accessToken', 289 | 'Host: api.cronofy.com', 290 | 'Content-Type: application/json; charset=utf-8', 291 | ]) 292 | ) 293 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 294 | 295 | $cronofy = new Cronofy([ 296 | "client_id" => "clientId", 297 | "client_secret" => "clientSecret", 298 | "access_token" => "accessToken", 299 | "refresh_token" => "refreshToken", 300 | "http_client" => $http, 301 | ]); 302 | 303 | $actual = $cronofy->deleteEvent([ 304 | "calendar_id" => "cal_123", 305 | "event_id" => "evt_456", 306 | ]); 307 | $this->assertNotNull($actual); 308 | } 309 | 310 | public function testBulkDeleteEventsFromSpecificCalendar() 311 | { 312 | $params = ["calendar_ids" => ["cal_123"]]; 313 | 314 | $http = $this->createMock(HttpRequest::class); 315 | $http->expects($this->once()) 316 | ->method('httpDelete') 317 | ->with( 318 | $this->equalTo('https://api.cronofy.com/v1/events'), 319 | $this->equalTo($params), 320 | $this->equalTo([ 321 | 'Authorization: Bearer accessToken', 322 | 'Host: api.cronofy.com', 323 | 'Content-Type: application/json; charset=utf-8', 324 | ]) 325 | ) 326 | ->will($this->returnValue(["{'foo': 'bar'}", 202])); 327 | 328 | $cronofy = new Cronofy([ 329 | "client_id" => "clientId", 330 | "client_secret" => "clientSecret", 331 | "access_token" => "accessToken", 332 | "refresh_token" => "refreshToken", 333 | "http_client" => $http, 334 | ]); 335 | 336 | $actual = $cronofy->bulkDeleteEvents([ 337 | "calendar_ids" => ["cal_123"] 338 | ]); 339 | $this->assertNotNull($actual); 340 | } 341 | 342 | public function testBulkDeleteEventsFromAllCalendars() 343 | { 344 | $params = ["delete_all" => true]; 345 | 346 | $http = $this->createMock(HttpRequest::class); 347 | $http->expects($this->once()) 348 | ->method('httpDelete') 349 | ->with( 350 | $this->equalTo('https://api.cronofy.com/v1/events'), 351 | $this->equalTo($params), 352 | $this->equalTo([ 353 | 'Authorization: Bearer accessToken', 354 | 'Host: api.cronofy.com', 355 | 'Content-Type: application/json; charset=utf-8', 356 | ]) 357 | ) 358 | ->will($this->returnValue(["{'foo': 'bar'}", 202])); 359 | 360 | $cronofy = new Cronofy([ 361 | "client_id" => "clientId", 362 | "client_secret" => "clientSecret", 363 | "access_token" => "accessToken", 364 | "refresh_token" => "refreshToken", 365 | "http_client" => $http, 366 | ]); 367 | 368 | $actual = $cronofy->bulkDeleteEvents([ 369 | "delete_all" => true 370 | ]); 371 | $this->assertNotNull($actual); 372 | } 373 | 374 | public function testDeleteExternalEvent() 375 | { 376 | $params = ["event_uid" => "evt_456"]; 377 | 378 | $http = $this->createMock(HttpRequest::class); 379 | $http->expects($this->once()) 380 | ->method('httpDelete') 381 | ->with( 382 | $this->equalTo('https://api.cronofy.com/v1/calendars/cal_123/events'), 383 | $this->equalTo($params), 384 | $this->equalTo([ 385 | 'Authorization: Bearer accessToken', 386 | 'Host: api.cronofy.com', 387 | 'Content-Type: application/json; charset=utf-8', 388 | ]) 389 | ) 390 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 391 | 392 | $cronofy = new Cronofy([ 393 | "client_id" => "clientId", 394 | "client_secret" => "clientSecret", 395 | "access_token" => "accessToken", 396 | "refresh_token" => "refreshToken", 397 | "http_client" => $http, 398 | ]); 399 | 400 | $actual = $cronofy->deleteExternalEvent([ 401 | "calendar_id" => "cal_123", 402 | "event_uid" => "evt_456", 403 | ]); 404 | $this->assertNotNull($actual); 405 | } 406 | 407 | public function testFreeBusy() 408 | { 409 | $page_1 = '{ 410 | "pages": { 411 | "current": 1, 412 | "total": 1, 413 | }, 414 | "events": [ 415 | { 416 | "calendar_id": "cal_U9uuErStTG@EAAAB_IsAsykA2DBTWqQTf-f0kJw", 417 | "event_uid": "evt_external_event_one", 418 | "summary": "Company Retreat" 419 | } 420 | ] 421 | }'; 422 | 423 | $http = $this->createMock(HttpRequest::class); 424 | $http->expects($this->at(0)) 425 | ->method('getPage') 426 | ->with( 427 | $this->equalTo('https://api.cronofy.com/v1/free_busy'), 428 | $this->equalTo([ 429 | 'Authorization: Bearer accessToken', 430 | 'Host: api.cronofy.com' 431 | ]), 432 | "?localized_times=true" 433 | ) 434 | ->will($this->returnValue([$page_1, 200])); 435 | 436 | $cronofy = new Cronofy([ 437 | "client_id" => "clientId", 438 | "client_secret" => "clientSecret", 439 | "access_token" => "accessToken", 440 | "refresh_token" => "refreshToken", 441 | "http_client" => $http, 442 | ]); 443 | 444 | $params = [ "localized_times" => true ]; 445 | $actual = $cronofy->freeBusy($params); 446 | $this->assertNotNull($actual); 447 | } 448 | 449 | public function testGetSmartInvite() 450 | { 451 | $http = $this->createMock(HttpRequest::class); 452 | $http->expects($this->once()) 453 | ->method('httpGet') 454 | ->with( 455 | $this->equalTo('https://api.cronofy.com/v1/smart_invites?smart_invite_id=foo&recipient_email=foo%40example.com'), 456 | $this->equalTo([ 457 | 'Authorization: Bearer clientSecret', 458 | 'Host: api.cronofy.com' 459 | ]) 460 | ) 461 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 462 | 463 | $cronofy = new Cronofy([ 464 | "client_id" => "clientId", 465 | "client_secret" => "clientSecret", 466 | "access_token" => "accessToken", 467 | "refresh_token" => "refreshToken", 468 | "http_client" => $http, 469 | ]); 470 | 471 | $actual = $cronofy->getSmartInvite("foo", "foo@example.com"); 472 | $this->assertNotNull($actual); 473 | } 474 | 475 | public function testCancelSmartInvite() 476 | { 477 | $recipient = ["email" => "example@example.com"]; 478 | $smart_invite_id = "foo"; 479 | 480 | $request_params = [ 481 | "method" => "cancel", 482 | "recipient" => $recipient, 483 | "smart_invite_id" => $smart_invite_id, 484 | ]; 485 | 486 | $http = $this->createMock(HttpRequest::class); 487 | $http->expects($this->once()) 488 | ->method('httpPost') 489 | ->with( 490 | $this->equalTo('https://api.cronofy.com/v1/smart_invites'), 491 | $this->equalTo($request_params), 492 | $this->equalTo([ 493 | 'Authorization: Bearer clientSecret', 494 | 'Host: api.cronofy.com', 495 | 'Content-Type: application/json; charset=utf-8' 496 | ]) 497 | ) 498 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 499 | 500 | $cronofy = new Cronofy([ 501 | "client_id" => "clientId", 502 | "client_secret" => "clientSecret", 503 | "access_token" => "accessToken", 504 | "refresh_token" => "refreshToken", 505 | "http_client" => $http, 506 | ]); 507 | 508 | $params = [ 509 | "recipient" => $recipient, 510 | "smart_invite_id" => $smart_invite_id, 511 | ]; 512 | 513 | $actual = $cronofy->cancelSmartInvite($params); 514 | $this->assertNotNull($actual); 515 | } 516 | 517 | public function testCancelSmartInviteWithMultipleRecipients() 518 | { 519 | $recipients = array( 520 | array("email" => "example@example.com"), 521 | array("email" => "example@example.org"), 522 | ); 523 | $smart_invite_id = "foo"; 524 | 525 | $request_params = array( 526 | "method" => "cancel", 527 | "recipients" => $recipients, 528 | "smart_invite_id" => $smart_invite_id, 529 | ); 530 | 531 | $http = $this->createMock(HttpRequest::class); 532 | $http->expects($this->once()) 533 | ->method('httpPost') 534 | ->with( 535 | $this->equalTo('https://api.cronofy.com/v1/smart_invites'), 536 | $this->equalTo($request_params), 537 | $this->equalTo(array( 538 | 'Authorization: Bearer clientSecret', 539 | 'Host: api.cronofy.com', 540 | 'Content-Type: application/json; charset=utf-8' 541 | )) 542 | ) 543 | ->will($this->returnValue(array("{'foo': 'bar'}", 200))); 544 | 545 | $cronofy = new Cronofy(array( 546 | "client_id" => "clientId", 547 | "client_secret" => "clientSecret", 548 | "access_token" => "accessToken", 549 | "refresh_token" => "refreshToken", 550 | "http_client" => $http, 551 | )); 552 | 553 | $params = array( 554 | "recipients" => $recipients, 555 | "smart_invite_id" => $smart_invite_id, 556 | ); 557 | 558 | $actual = $cronofy->cancelSmartInvite($params); 559 | $this->assertNotNull($actual); 560 | } 561 | 562 | public function testCreateSmartInvite() 563 | { 564 | $event = [ 565 | "summary" => "Add to Calendar test event", 566 | "start" => "2017-01-01T12:00:00Z", 567 | "end" => "2017-01-01T15:00:00Z" 568 | ]; 569 | $recipient = ["email" => "example@example.com"]; 570 | $organizer = ["name" => "Smart invite application"]; 571 | $smart_invite_id = "foo"; 572 | $callback_url = "http://www.example.com/callback"; 573 | 574 | $params = [ 575 | "recipient" => $recipient, 576 | "event" => $event, 577 | "smart_invite_id" => $smart_invite_id, 578 | "callback_url" => $callback_url, 579 | "organizer" => $organizer, 580 | ]; 581 | 582 | $http = $this->createMock(HttpRequest::class); 583 | $http->expects($this->once()) 584 | ->method('httpPost') 585 | ->with( 586 | $this->equalTo('https://api.cronofy.com/v1/smart_invites'), 587 | $this->equalTo($params), 588 | $this->equalTo([ 589 | 'Authorization: Bearer clientSecret', 590 | 'Host: api.cronofy.com', 591 | 'Content-Type: application/json; charset=utf-8' 592 | ]) 593 | ) 594 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 595 | 596 | $cronofy = new Cronofy([ 597 | "client_id" => "clientId", 598 | "client_secret" => "clientSecret", 599 | "access_token" => "accessToken", 600 | "refresh_token" => "refreshToken", 601 | "http_client" => $http, 602 | ]); 603 | 604 | $actual = $cronofy->createSmartInvite($params); 605 | $this->assertNotNull($actual); 606 | } 607 | 608 | public function testCreateSmartInviteWithMultipleRecipients() 609 | { 610 | $event = [ 611 | "summary" => "Add to Calendar test event", 612 | "start" => "2017-01-01T12:00:00Z", 613 | "end" => "2017-01-01T15:00:00Z" 614 | ]; 615 | $organizer = ["name" => "Smart invite application"]; 616 | $smart_invite_id = "foo"; 617 | $callback_url = "http://www.example.com/callback"; 618 | 619 | $params = [ 620 | "recipients" => [ 621 | ["email" => "example@example.com"], 622 | ["email" => "example@example.org"], 623 | ], 624 | "event" => $event, 625 | "smart_invite_id" => $smart_invite_id, 626 | "callback_url" => $callback_url, 627 | "organizer" => $organizer, 628 | ]; 629 | 630 | $http = $this->createMock(HttpRequest::class); 631 | $http->expects($this->once()) 632 | ->method('httpPost') 633 | ->with( 634 | $this->equalTo('https://api.cronofy.com/v1/smart_invites'), 635 | $this->equalTo($params), 636 | $this->equalTo([ 637 | 'Authorization: Bearer clientSecret', 638 | 'Host: api.cronofy.com', 639 | 'Content-Type: application/json; charset=utf-8' 640 | ]) 641 | ) 642 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 643 | 644 | $cronofy = new Cronofy([ 645 | "client_id" => "clientId", 646 | "client_secret" => "clientSecret", 647 | "access_token" => "accessToken", 648 | "refresh_token" => "refreshToken", 649 | "http_client" => $http, 650 | ]); 651 | 652 | $actual = $cronofy->createSmartInvite($params); 653 | $this->assertNotNull($actual); 654 | } 655 | 656 | public function testRequestElementToken() 657 | { 658 | $params = [ 659 | "version" => "1", 660 | "permissions" => ["agenda", "availability"], 661 | 'subs' => ['acc_12345678'], 662 | "origin" => 'http://local.test' 663 | ]; 664 | 665 | $response = [ 666 | "element_token" => [ 667 | "permissions" => ["agenda", "availability"], 668 | "origin" => 'http://local.test', 669 | "token" => "ELEMENT_TOKEN", 670 | "expires_in" => 64800 671 | ] 672 | ]; 673 | 674 | $http = $this->createMock(HttpRequest::class); 675 | $http->expects($this->once()) 676 | ->method('httpPost') 677 | ->with( 678 | $this->equalTo('https://api.cronofy.com/v1/element_tokens'), 679 | $this->equalTo($params), 680 | $this->equalTo([ 681 | 'Authorization: Bearer clientSecret', 682 | 'Host: api.cronofy.com', 683 | 'Content-Type: application/json; charset=utf-8' 684 | ]) 685 | ) 686 | ->will($this->returnValue([json_encode($response), 200])) 687 | ; 688 | 689 | $cronofy = new Cronofy([ 690 | "client_id" => "clientId", 691 | "client_secret" => "clientSecret", 692 | "access_token" => "accessToken", 693 | "refresh_token" => "refreshToken", 694 | "http_client" => $http, 695 | ]); 696 | 697 | $actual = $cronofy->requestElementToken($params); 698 | $this->assertNotNull($actual); 699 | } 700 | 701 | public function testConferencingServiceAuthorization() 702 | { 703 | $request_params = [ 704 | "redirect_uri" => "http://example.com", 705 | ]; 706 | 707 | $response = [ 708 | "authorization_request" => [ 709 | "url" => "https://app.cronofy.com/conferencing_services/xxxxx" 710 | ], 711 | ]; 712 | 713 | $http = $this->createMock(HttpRequest::class); 714 | $http->expects($this->once()) 715 | ->method('httpPost') 716 | ->with( 717 | $this->equalTo('https://api.cronofy.com/v1/conferencing_service_authorizations'), 718 | $this->equalTo($request_params), 719 | $this->equalTo([ 720 | 'Authorization: Bearer accessToken', 721 | 'Host: api.cronofy.com', 722 | 'Content-Type: application/json; charset=utf-8', 723 | ]) 724 | ) 725 | ->will($this->returnValue([json_encode($response), 200])); 726 | 727 | $cronofy = new Cronofy([ 728 | "client_id" => "clientId", 729 | "client_secret" => "clientSecret", 730 | "access_token" => "accessToken", 731 | "refresh_token" => "refreshToken", 732 | "http_client" => $http, 733 | ]); 734 | 735 | $params = [ 736 | 'redirect_uri' => "http://example.com", 737 | ]; 738 | 739 | $actual = $cronofy->conferencingServiceAuthorization($params); 740 | $this->assertNotNull($actual); 741 | $this->assertEquals($actual, $response); 742 | } 743 | 744 | public function testDeleteAvailablePeriod() 745 | { 746 | $params = ["available_period_id" => "avp_456"]; 747 | 748 | $http = $this->createMock(HttpRequest::class); 749 | $http->expects($this->once()) 750 | ->method('httpDelete') 751 | ->with( 752 | $this->equalTo('https://api.cronofy.com/v1/available_periods/'), 753 | $this->equalTo($params), 754 | $this->equalTo([ 755 | 'Authorization: Bearer accessToken', 756 | 'Host: api.cronofy.com', 757 | 'Content-Type: application/json; charset=utf-8', 758 | ]) 759 | ) 760 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 761 | 762 | $cronofy = new Cronofy([ 763 | "client_id" => "clientId", 764 | "client_secret" => "clientSecret", 765 | "access_token" => "accessToken", 766 | "refresh_token" => "refreshToken", 767 | "http_client" => $http, 768 | ]); 769 | 770 | $actual = $cronofy->deleteAvailablePeriod([ 771 | "available_period_id" => "avp_456", 772 | ]); 773 | $this->assertNotNull($actual); 774 | } 775 | 776 | public function testHmacValidation() 777 | { 778 | $cronofy = new Cronofy([ 779 | "client_id" => "clientId", 780 | "client_secret" => "clientSecret", 781 | "access_token" => "accessToken", 782 | "refresh_token" => "refreshToken", 783 | "http_client" => null, 784 | ]); 785 | 786 | $body = '{"example":"well-known"}'; 787 | 788 | $actual = $cronofy->hmacValid("QuGlxxssNDaxUjd6RY4wxGf+5KDrmobMmjkGQPtB3WQ=", $body); 789 | $this->assertTrue($actual); 790 | 791 | $actual = $cronofy->hmacValid("something-else", $body); 792 | $this->assertFalse($actual); 793 | 794 | $actual = $cronofy->hmacValid("something-else,QuGlxxssNDaxUjd6RY4wxGf+5KDrmobMmjkGQPtB3WQ=", $body); 795 | $this->assertTrue($actual); 796 | 797 | $actual = $cronofy->hmacValid("something-else,something-else2", $body); 798 | $this->assertFalse($actual); 799 | 800 | $actual = $cronofy->hmacValid(null, $body); 801 | $this->assertFalse($actual); 802 | 803 | $actual = $cronofy->hmacValid("", $body); 804 | $this->assertFalse($actual); 805 | } 806 | } 807 | -------------------------------------------------------------------------------- /tests/DelegatedAuthorizationsTest.php: -------------------------------------------------------------------------------- 1 | $profileId, 21 | "email" => $email, 22 | "callback_url" => $callback_url, 23 | "scope" => $scopes, 24 | "state" => $state 25 | ]; 26 | 27 | $http = $this->createMock(HttpRequest::class); 28 | $http->expects($this->once()) 29 | ->method('httpPost') 30 | ->with( 31 | $this->equalTo('https://api.cronofy.com/v1/delegated_authorizations'), 32 | $this->equalTo([ 33 | "profile_id" => $profileId, 34 | "email" => $email, 35 | "callback_url" => $callback_url, 36 | "scope" => "list_calendars read_free_busy", 37 | "state" => $state 38 | ]), 39 | $this->equalTo([ 40 | 'Authorization: Bearer accessToken', 41 | 'Host: api.cronofy.com', 42 | 'Content-Type: application/json; charset=utf-8', 43 | ]) 44 | ) 45 | ->will($this->returnValue(["", 202])); 46 | 47 | $cronofy = new Cronofy([ 48 | "client_id" => "clientId", 49 | "client_secret" => "clientSecret", 50 | "access_token" => "accessToken", 51 | "refresh_token" => "refreshToken", 52 | "http_client" => $http, 53 | ]); 54 | 55 | $cronofy->requestDelegatedAuthorization($args); 56 | } 57 | 58 | public function testErrorHandling() 59 | { 60 | $profileId = "profileId"; 61 | $email = "emailOfAccountToAccess"; 62 | $callback_url = "http://www.example.com/callback"; 63 | $scopes = ["list_calendars", "read_free_busy"]; 64 | $state = "user-state"; 65 | 66 | $args = [ 67 | "profile_id" => $profileId, 68 | "email" => $email, 69 | "callback_url" => $callback_url, 70 | "scope" => $scopes, 71 | "state" => $state 72 | ]; 73 | 74 | $errorResponse = '{ 75 | "errors": { 76 | "email": [ 77 | { 78 | "key": "errors.required", 79 | "description": "required" 80 | } 81 | ] 82 | } 83 | }'; 84 | 85 | $http = $this->createMock(HttpRequest::class); 86 | $http->expects($this->once()) 87 | ->method('httpPost') 88 | ->with( 89 | $this->equalTo('https://api.cronofy.com/v1/delegated_authorizations'), 90 | $this->equalTo([ 91 | "profile_id" => $profileId, 92 | "email" => $email, 93 | "callback_url" => $callback_url, 94 | "scope" => "list_calendars read_free_busy", 95 | "state" => $state 96 | ]), 97 | $this->equalTo([ 98 | 'Authorization: Bearer accessToken', 99 | 'Host: api.cronofy.com', 100 | 'Content-Type: application/json; charset=utf-8', 101 | ]) 102 | ) 103 | ->will($this->returnValue([$errorResponse, 422])); 104 | 105 | $cronofy = new Cronofy([ 106 | "client_id" => "clientId", 107 | "client_secret" => "clientSecret", 108 | "access_token" => "accessToken", 109 | "refresh_token" => "refreshToken", 110 | "http_client" => $http, 111 | ]); 112 | 113 | $raised_error = false; 114 | 115 | try { 116 | $cronofy->requestDelegatedAuthorization($args); 117 | } catch (CronofyException $exception) { 118 | $raised_error = true; 119 | $this->assertEquals(json_decode($errorResponse, true), $exception->error_details()); 120 | $this->assertEquals(422, $exception->getCode()); 121 | } 122 | 123 | $this->assertTrue($raised_error); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /tests/ReadEventsTest.php: -------------------------------------------------------------------------------- 1 | createMock(HttpRequest::class); 27 | $http->expects($this->once()) 28 | ->method('getPage') 29 | ->with( 30 | $this->equalTo('https://api.cronofy.com/v1/events'), 31 | $this->equalTo([ 32 | 'Authorization: Bearer accessToken', 33 | 'Host: api.cronofy.com' 34 | ]), 35 | "?tzid=Etc%2FUTC" 36 | ) 37 | ->will($this->returnValue([$events_page, 200])); 38 | 39 | $cronofy = new Cronofy([ 40 | "client_id" => "clientId", 41 | "client_secret" => "clientSecret", 42 | "access_token" => "accessToken", 43 | "refresh_token" => "refreshToken", 44 | "http_client" => $http, 45 | ]); 46 | 47 | $params = [ 48 | 'tzid' => 'Etc/UTC' 49 | ]; 50 | 51 | $actual = $cronofy->readEvents($params); 52 | $this->assertNotNull($actual); 53 | $this->assertCount(1, $actual); 54 | $this->assertCount(1, $actual->each()); 55 | 56 | foreach ($actual->each() as $event) { 57 | $this->assertNotNull($event); 58 | $this->assertEquals($event["event_uid"], "evt_external_54008b1a4a41730f8d5c6037"); 59 | } 60 | 61 | foreach ($actual as $event) { 62 | $this->assertNotNull($event); 63 | $this->assertEquals($event["event_uid"], "evt_external_54008b1a4a41730f8d5c6037"); 64 | } 65 | } 66 | 67 | public function testReadEventsTwoPageSingleEvent() 68 | { 69 | $page_1 = '{ 70 | "pages": { 71 | "current": 1, 72 | "total": 2, 73 | "next_page": "https://api.cronofy.com/v1/events/pages/08a07b034306679e" 74 | }, 75 | "events": [ 76 | { 77 | "calendar_id": "cal_U9uuErStTG@EAAAB_IsAsykA2DBTWqQTf-f0kJw", 78 | "event_uid": "evt_external_event_one", 79 | "summary": "Company Retreat" 80 | } 81 | ] 82 | }'; 83 | 84 | $page_2 = '{ 85 | "pages": { 86 | "current": 2, 87 | "total": 2 88 | }, 89 | "events": [ 90 | { 91 | "calendar_id": "cal_U9uuErStTG@EAAAB_IsAsykA2DBTWqQTf-f0kJw", 92 | "event_uid": "evt_external_event_two", 93 | "summary": "Company Retreat" 94 | } 95 | ] 96 | }'; 97 | 98 | $http = $this->createMock(HttpRequest::class); 99 | $http->expects($this->exactly(2)) 100 | ->method('getPage') 101 | ->will($this->onConsecutiveCalls([$page_1, 200], [$page_2, 200])); 102 | 103 | $cronofy = new Cronofy([ 104 | "client_id" => "clientId", 105 | "client_secret" => "clientSecret", 106 | "access_token" => "accessToken", 107 | "refresh_token" => "refreshToken", 108 | "http_client" => $http, 109 | ]); 110 | 111 | $params = [ 112 | 'tzid' => 'Etc/UTC' 113 | ]; 114 | 115 | $actual = $cronofy->readEvents($params); 116 | $this->assertNotNull($actual); 117 | $this->assertCount(2, $actual); 118 | } 119 | 120 | public function testReadEventsCanBeConvertedToArray() 121 | { 122 | $page_1 = '{ 123 | "pages": { 124 | "current": 1, 125 | "total": 2, 126 | "next_page": "https://api.cronofy.com/v1/events/pages/08a07b034306679e" 127 | }, 128 | "events": [ 129 | { 130 | "calendar_id": "cal_U9uuErStTG@EAAAB_IsAsykA2DBTWqQTf-f0kJw", 131 | "event_uid": "evt_external_event_one", 132 | "summary": "Company Retreat" 133 | } 134 | ] 135 | }'; 136 | 137 | $page_2 = '{ 138 | "pages": { 139 | "current": 2, 140 | "total": 2 141 | }, 142 | "events": [ 143 | { 144 | "calendar_id": "cal_U9uuErStTG@EAAAB_IsAsykA2DBTWqQTf-f0kJw", 145 | "event_uid": "evt_external_event_two", 146 | "summary": "Company Retreat" 147 | } 148 | ] 149 | }'; 150 | 151 | $http = $this->createMock(HttpRequest::class); 152 | $http->expects($this->exactly(2)) 153 | ->method('getPage') 154 | ->will($this->onConsecutiveCalls([$page_1, 200], [$page_2, 200])); 155 | 156 | $cronofy = new Cronofy([ 157 | "client_id" => "clientId", 158 | "client_secret" => "clientSecret", 159 | "access_token" => "accessToken", 160 | "refresh_token" => "refreshToken", 161 | "http_client" => $http, 162 | ]); 163 | 164 | $params = [ 165 | 'tzid' => 'Etc/UTC' 166 | ]; 167 | 168 | $actual = $cronofy->readEvents($params); 169 | $event_uids = array_map(function (array $event) { 170 | return $event['event_uid']; 171 | }, iterator_to_array($actual)); 172 | 173 | $this->assertContains('evt_external_event_one', $event_uids); 174 | $this->assertContains('evt_external_event_two', $event_uids); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /tests/RulesTest.php: -------------------------------------------------------------------------------- 1 | [ 14 | "availability_rule_id" => "default", 15 | "tzid" => "Etc/UTC", 16 | "weekly_periods" => [ 17 | [ 18 | "day" => "monday", 19 | "start_time" => "09:00", 20 | "end_time" => "13:00" 21 | ], 22 | [ 23 | "day" => "monday", 24 | "start_time" => "14:00", 25 | "end_time" => "17:00" 26 | ], 27 | [ 28 | "day" => "tuesday", 29 | "start_time" => "09:00", 30 | "end_time" => "17:00" 31 | ] 32 | ] 33 | ] 34 | ]; 35 | $http = $this->createMock(HttpRequest::class); 36 | $http->expects($this->once()) 37 | ->method('httpGet') 38 | ->with( 39 | $this->equalTo('https://api.cronofy.com/v1/availability_rules/default') 40 | ) 41 | ->will($this->returnValue([json_encode($expected_rule), 200])); 42 | 43 | $cronofy = new Cronofy([ 44 | "client_id" => "clientId", 45 | "client_secret" => "clientSecret", 46 | "access_token" => "accessToken", 47 | "refresh_token" => "refreshToken", 48 | "http_client" => $http, 49 | ]); 50 | 51 | $response = $cronofy->getAvailabilityRule("default"); 52 | 53 | $this->assertNotNull($response); 54 | $this->assertEquals(3, count($response['availability_rule'])); 55 | $this->assertEquals("default", $response['availability_rule']['availability_rule_id']); 56 | } 57 | 58 | public function testListAvailabilityRules() 59 | { 60 | $expected_rules = [ 61 | "availability_rules" => [ 62 | [ 63 | "availability_rule_id" => "default", 64 | "calendar_ids" => ["cal_123"], 65 | "tzid" => "Etc/UTC", 66 | "weekly_periods" => [ 67 | [ 68 | "day" => "monday", 69 | "start_time" => "09:00", 70 | "end_time" => "13:00" 71 | ], 72 | [ 73 | "day" => "monday", 74 | "start_time" => "14:00", 75 | "end_time" => "17:00" 76 | ], 77 | [ 78 | "day" => "tuesday", 79 | "start_time" => "09:00", 80 | "end_time" => "17:00" 81 | ] 82 | ] 83 | ], 84 | [ 85 | "availability_rule_id" => "work_hours", 86 | "calendar_ids" => ["cal_321"], 87 | "tzid" => "Etc/UTC", 88 | "weekly_periods" => [ 89 | [ 90 | "day" => "tuesday", 91 | "start_time" => "09:00", 92 | "end_time" => "17:00" 93 | ], 94 | [ 95 | "day" => "wednesday", 96 | "start_time" => "09:00", 97 | "end_time" => "17:00" 98 | ] 99 | ] 100 | ], 101 | ] 102 | ]; 103 | $http = $this->createMock(HttpRequest::class); 104 | $http->expects($this->once()) 105 | ->method('httpGet') 106 | ->with( 107 | $this->equalTo('https://api.cronofy.com/v1/availability_rules') 108 | ) 109 | ->will($this->returnValue([json_encode($expected_rules), 200])); 110 | 111 | $cronofy = new Cronofy([ 112 | "client_id" => "clientId", 113 | "client_secret" => "clientSecret", 114 | "access_token" => "accessToken", 115 | "refresh_token" => "refreshToken", 116 | "http_client" => $http, 117 | ]); 118 | 119 | $response = $cronofy->listAvailabilityRules(); 120 | 121 | $this->assertNotNull($response); 122 | $this->assertEquals(2, count($response['availability_rules'])); 123 | $this->assertEquals(4, count($response['availability_rules'][0])); 124 | $this->assertEquals("default", $response['availability_rules'][0]['availability_rule_id']); 125 | $this->assertEquals("work_hours", $response['availability_rules'][1]['availability_rule_id']); 126 | } 127 | 128 | public function testDeleteAvailabilityRules() 129 | { 130 | 131 | $http = $this->createMock(HttpRequest::class); 132 | $http->expects($this->once()) 133 | ->method('httpDelete') 134 | ->with( 135 | $this->equalTo('https://api.cronofy.com/v1/availability_rules/rule_123') 136 | ) 137 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 138 | 139 | $cronofy = new Cronofy([ 140 | "client_id" => "clientId", 141 | "client_secret" => "clientSecret", 142 | "access_token" => "accessToken", 143 | "refresh_token" => "refreshToken", 144 | "http_client" => $http, 145 | ]); 146 | 147 | $actual = $cronofy->deleteAvailabilityRule("rule_123"); 148 | $this->assertNotNull($actual); 149 | } 150 | 151 | public function testCreateAvailabilityRule() 152 | { 153 | $expected_output = [ 154 | "availability_rule_id" => "default", 155 | "calendar_ids" => ["cal_123"], 156 | "tzid" => "America/Chicago", 157 | "weekly_periods" => [ 158 | [ 159 | "day" => "monday", 160 | "start_time" => "09:30", 161 | "end_time" => "12:30" 162 | ], 163 | [ 164 | "day" => "wednesday", 165 | "start_time" => "09:30", 166 | "end_time" => "12:30" 167 | ] 168 | ] 169 | ]; 170 | 171 | $params = [ 172 | "availability_rule_id" => "default", 173 | "calendar_ids" => ["cal_123"], 174 | "tzid" => "America/Chicago", 175 | "weekly_periods" => [ 176 | [ 177 | "day" => "monday", 178 | "start_time" => "09:30", 179 | "end_time" => "12:30" 180 | ], 181 | [ 182 | "day" => "wednesday", 183 | "start_time" => "09:30", 184 | "end_time" => "12:30" 185 | ] 186 | ] 187 | ]; 188 | 189 | $http = $this->createMock(HttpRequest::class); 190 | $http->expects($this->once()) 191 | ->method('httpPost') 192 | ->with( 193 | $this->equalTo('https://api.cronofy.com/v1/availability_rules'), 194 | $this->equalTo($params) 195 | ) 196 | ->will($this->returnValue([json_encode($expected_output), 200])); 197 | 198 | $cronofy = new Cronofy([ 199 | "client_id" => "clientId", 200 | "client_secret" => "clientSecret", 201 | "access_token" => "accessToken", 202 | "refresh_token" => "refreshToken", 203 | "http_client" => $http, 204 | ]); 205 | 206 | $response = $cronofy->createAvailabilityRule($params); 207 | 208 | $this->assertNotNull($response); 209 | $this->assertEquals(4, count($response)); 210 | $this->assertEquals("default", $response['availability_rule_id']); 211 | $this->assertEquals(2, count($response['weekly_periods'])); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /tests/SchedulingTest.php: -------------------------------------------------------------------------------- 1 | "PERIODS", 15 | "participants" => "PARTICIPANTS", 16 | "required_duration" => "DURATION", 17 | "response_format" => "FORMAT" 18 | ]; 19 | 20 | $http = $this->createMock(HttpRequest::class); 21 | $http->expects($this->once()) 22 | ->method('httpPost') 23 | ->with( 24 | $this->equalTo('https://api.cronofy.com/v1/availability'), 25 | $this->equalTo($parsedParams) 26 | ) 27 | ->will($this->returnValue([json_encode($parsedParams), 200])); 28 | 29 | $cronofy = new Cronofy([ 30 | "client_id" => "clientId", 31 | "client_secret" => "clientSecret", 32 | "access_token" => "accessToken", 33 | "refresh_token" => "refreshToken", 34 | "http_client" => $http, 35 | ]); 36 | 37 | $params = [ 38 | "participants" => "PARTICIPANTS", 39 | "available_periods" => "PERIODS", 40 | "required_duration" => "DURATION", 41 | "response_format" => "FORMAT" 42 | ]; 43 | 44 | $response = $cronofy->availability($params); 45 | $this->assertNotNull($response); 46 | } 47 | 48 | public function testRealTimeScheduling() 49 | { 50 | $oauth = [ 51 | "redirect_uri" => "http://test.com/", 52 | "scope" => "test_scope" 53 | ]; 54 | $event = [ 55 | "event_id" => "test_event_id", 56 | "summary" => "Add to Calendar test event", 57 | ]; 58 | $availability = [ 59 | "participants" => [ 60 | [ 61 | "members" => [ 62 | [ 63 | "sub" => "acc_567236000909002", 64 | "calendar_ids" => ["cal_n23kjnwrw2_jsdfjksn234"] 65 | ] 66 | ], 67 | "required" => "all" 68 | ] 69 | ], 70 | "required_duration" => [ 71 | "minutes" => 60 72 | ], 73 | "start_interval" => [ 74 | "minutes" => 60 75 | ], 76 | "buffer" => [ 77 | "before" => [ 78 | "minutes" => 60 79 | ] 80 | ], 81 | "available_periods" => [ 82 | [ 83 | "start" => "2017-01-01T09:00:00Z", 84 | "end" => "2017-01-01T17:00:00Z" 85 | ] 86 | ] 87 | ]; 88 | $target_calendars = [ 89 | [ 90 | "sub" => "acc_567236000909002", 91 | "calendar_id" => "cal_n23kjnwrw2_jsdfjksn234" 92 | ] 93 | ]; 94 | $tzid = 'Europe/London'; 95 | $callback_url = "http://example.com/callback"; 96 | $completed_redirect_url = "http://example.com/redirect"; 97 | 98 | $params = [ 99 | "client_id" => "clientId", 100 | "client_secret" => "clientSecret", 101 | "event" => $event, 102 | "target_calendars" => $target_calendars, 103 | "availability" => $availability, 104 | "oauth" => $oauth, 105 | "tzid" => $tzid, 106 | "callback_urls" => [ 107 | "completed_url" => $callback_url, 108 | ], 109 | "redirect_urls" => [ 110 | "completed_url" => $completed_redirect_url, 111 | ], 112 | "formatting" => [ 113 | "hour_format" => "12", 114 | ], 115 | "minimum_notice" => [ 116 | "hours" => 2 117 | ], 118 | "event_creation" => "single", 119 | ]; 120 | 121 | $http = $this->createMock(HttpRequest::class); 122 | $http->expects($this->once()) 123 | ->method('httpPost') 124 | ->with( 125 | $this->equalTo('https://api.cronofy.com/v1/real_time_scheduling'), 126 | $this->equalTo($params), 127 | $this->equalTo([ 128 | 'Authorization: Bearer accessToken', 129 | 'Host: api.cronofy.com', 130 | 'Content-Type: application/json; charset=utf-8' 131 | ]) 132 | ) 133 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 134 | 135 | $cronofy = new Cronofy([ 136 | "client_id" => "clientId", 137 | "client_secret" => "clientSecret", 138 | "access_token" => "accessToken", 139 | "refresh_token" => "refreshToken", 140 | "http_client" => $http, 141 | ]); 142 | 143 | $actual = $cronofy->realTimeScheduling($params); 144 | $this->assertNotNull($actual); 145 | } 146 | 147 | public function testRealTimeSchedulingWhenDeprecatedCallbackUrlPassed() 148 | { 149 | $oauth = [ 150 | "redirect_uri" => "http://test.com/", 151 | "scope" => "test_scope" 152 | ]; 153 | $event = [ 154 | "event_id" => "test_event_id", 155 | "summary" => "Add to Calendar test event", 156 | ]; 157 | $availability = [ 158 | "participants" => [ 159 | [ 160 | "members" => [ 161 | [ 162 | "sub" => "acc_567236000909002", 163 | "calendar_ids" => ["cal_n23kjnwrw2_jsdfjksn234"] 164 | ] 165 | ], 166 | "required" => "all" 167 | ] 168 | ], 169 | "required_duration" => [ 170 | "minutes" => 60 171 | ], 172 | "start_interval" => [ 173 | "minutes" => 60 174 | ], 175 | "buffer" => [ 176 | "before" => [ 177 | "minutes" => 60 178 | ] 179 | ], 180 | "available_periods" => [ 181 | [ 182 | "start" => "2017-01-01T09:00:00Z", 183 | "end" => "2017-01-01T17:00:00Z" 184 | ] 185 | ] 186 | ]; 187 | $target_calendars = [ 188 | [ 189 | "sub" => "acc_567236000909002", 190 | "calendar_id" => "cal_n23kjnwrw2_jsdfjksn234" 191 | ] 192 | ]; 193 | $tzid = 'Europe/London'; 194 | $callback_url = "http://example.com/callback"; 195 | 196 | $params = [ 197 | "client_id" => "clientId", 198 | "client_secret" => "clientSecret", 199 | "event" => $event, 200 | "target_calendars" => $target_calendars, 201 | "availability" => $availability, 202 | "oauth" => $oauth, 203 | "tzid" => $tzid, 204 | "callback_url" => "http://example.com/THIS_ONE_GETS_OVERRIDEN", 205 | "callback_urls" => [ 206 | "completed_url" => $callback_url 207 | ] 208 | ]; 209 | 210 | $sentParams = $params; 211 | unset($sentParams["callback_url"]); 212 | 213 | $http = $this->createMock(HttpRequest::class); 214 | $http->expects($this->once()) 215 | ->method('httpPost') 216 | ->with( 217 | $this->equalTo('https://api.cronofy.com/v1/real_time_scheduling'), 218 | $this->equalTo($sentParams), 219 | $this->equalTo([ 220 | 'Authorization: Bearer accessToken', 221 | 'Host: api.cronofy.com', 222 | 'Content-Type: application/json; charset=utf-8' 223 | ]) 224 | ) 225 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 226 | 227 | $cronofy = new Cronofy([ 228 | "client_id" => "clientId", 229 | "client_secret" => "clientSecret", 230 | "access_token" => "accessToken", 231 | "refresh_token" => "refreshToken", 232 | "http_client" => $http, 233 | ]); 234 | 235 | $actual = $cronofy->realTimeScheduling($params); 236 | $this->assertNotNull($actual); 237 | } 238 | 239 | public function testRealTimeSchedulingWhenDeprecatedCallbackUrlPassedAndNewCallbackUrlsCompletedUrlPassed() 240 | { 241 | $oauth = [ 242 | "redirect_uri" => "http://test.com/", 243 | "scope" => "test_scope" 244 | ]; 245 | $event = [ 246 | "event_id" => "test_event_id", 247 | "summary" => "Add to Calendar test event", 248 | ]; 249 | $availability = [ 250 | "participants" => [ 251 | [ 252 | "members" => [ 253 | [ 254 | "sub" => "acc_567236000909002", 255 | "calendar_ids" => ["cal_n23kjnwrw2_jsdfjksn234"] 256 | ] 257 | ], 258 | "required" => "all" 259 | ] 260 | ], 261 | "required_duration" => [ 262 | "minutes" => 60 263 | ], 264 | "start_interval" => [ 265 | "minutes" => 60 266 | ], 267 | "buffer" => [ 268 | "before" => [ 269 | "minutes" => 60 270 | ] 271 | ], 272 | "available_periods" => [ 273 | [ 274 | "start" => "2017-01-01T09:00:00Z", 275 | "end" => "2017-01-01T17:00:00Z" 276 | ] 277 | ] 278 | ]; 279 | $target_calendars = [ 280 | [ 281 | "sub" => "acc_567236000909002", 282 | "calendar_id" => "cal_n23kjnwrw2_jsdfjksn234" 283 | ] 284 | ]; 285 | $tzid = 'Europe/London'; 286 | $callback_url = "http://example.com/callback"; 287 | 288 | $params = [ 289 | "client_id" => "clientId", 290 | "client_secret" => "clientSecret", 291 | "event" => $event, 292 | "target_calendars" => $target_calendars, 293 | "availability" => $availability, 294 | "oauth" => $oauth, 295 | "tzid" => $tzid, 296 | "callback_url" => "http://example.com/THIS_GETS_MOVED_TO_CALLBACK_URLS_COMPLETED_URL", 297 | "callback_urls" => [ 298 | "no_times_suitable_url" => "https://example.com/no_times_suitable" 299 | ] 300 | ]; 301 | 302 | $sentParams = $params; 303 | $sentParams["callback_urls"]["completed_url"] = $params["callback_url"]; 304 | unset($sentParams["callback_url"]); 305 | 306 | $http = $this->createMock(HttpRequest::class); 307 | $http->expects($this->once()) 308 | ->method('httpPost') 309 | ->with( 310 | $this->equalTo('https://api.cronofy.com/v1/real_time_scheduling'), 311 | $this->equalTo($sentParams), 312 | $this->equalTo([ 313 | 'Authorization: Bearer accessToken', 314 | 'Host: api.cronofy.com', 315 | 'Content-Type: application/json; charset=utf-8' 316 | ]) 317 | ) 318 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 319 | 320 | $cronofy = new Cronofy([ 321 | "client_id" => "clientId", 322 | "client_secret" => "clientSecret", 323 | "access_token" => "accessToken", 324 | "refresh_token" => "refreshToken", 325 | "http_client" => $http, 326 | ]); 327 | 328 | $actual = $cronofy->realTimeScheduling($params); 329 | $this->assertNotNull($actual); 330 | } 331 | 332 | public function testDisableRealTimeScheduling() 333 | { 334 | $rts_id = 'sch_1234567890123456786453'; 335 | $display_message = "rtsDisplayMessage"; 336 | 337 | $params = [ 338 | "id" => $rts_id, 339 | "display_message" => $display_message 340 | ]; 341 | 342 | $postFields = [ 343 | "display_message" => $display_message 344 | ]; 345 | 346 | $http = $this->createMock(HttpRequest::class); 347 | $http->expects($this->once()) 348 | ->method('httpPost') 349 | ->with( 350 | $this->equalTo('https://api.cronofy.com/v1/real_time_scheduling/'.$rts_id.'/disable'), 351 | $this->equalTo($postFields), 352 | $this->equalTo([ 353 | 'Authorization: Bearer clientSecret', 354 | 'Host: api.cronofy.com', 355 | 'Content-Type: application/json; charset=utf-8', 356 | ]) 357 | ) 358 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 359 | 360 | $cronofy = new Cronofy([ 361 | "client_id" => "clientId", 362 | "client_secret" => "clientSecret", 363 | "access_token" => "accessToken", 364 | "refresh_token" => "refreshToken", 365 | "http_client" => $http, 366 | ]); 367 | 368 | $actual = $cronofy->disableRealTimeScheduling($params); 369 | $this->assertNotNull($actual); 370 | } 371 | 372 | public function testRealTimeSequencing() 373 | { 374 | $oauth = [ 375 | "redirect_uri" => "http://test.com/", 376 | "scope" => "test_scope" 377 | ]; 378 | $event = [ 379 | "event_id" => "test_event_id", 380 | "summary" => "Add to Calendar test event", 381 | ]; 382 | $availability = [ 383 | "sequence" => [ 384 | [ 385 | "sequence_id" => "123", 386 | "ordinal" => 1, 387 | "participants" => [ 388 | [ 389 | "members" => [ 390 | [ 391 | "sub" => "acc_567236000909002", 392 | "calendar_ids" => ["cal_n23kjnwrw2_jsdfjksn234"] 393 | ] 394 | ], 395 | "required" => "all" 396 | ] 397 | ], 398 | "event" => $event, 399 | "required_duration" => [ 400 | "minutes" => 60 401 | ], 402 | ], 403 | ], 404 | "available_periods" => [ 405 | [ 406 | "start" => "2017-01-01T09:00:00Z", 407 | "end" => "2017-01-01T17:00:00Z" 408 | ] 409 | ] 410 | ]; 411 | $target_calendars = [ 412 | [ 413 | "sub" => "acc_567236000909002", 414 | "calendar_id" => "cal_n23kjnwrw2_jsdfjksn234" 415 | ] 416 | ]; 417 | $tzid = 'Europe/London'; 418 | 419 | $params = [ 420 | "client_id" => "clientId", 421 | "client_secret" => "clientSecret", 422 | "event" => $event, 423 | "target_calendars" => $target_calendars, 424 | "availability" => $availability, 425 | "oauth" => $oauth, 426 | "tzid" => $tzid, 427 | ]; 428 | 429 | $http = $this->createMock(HttpRequest::class); 430 | $http->expects($this->once()) 431 | ->method('httpPost') 432 | ->with( 433 | $this->equalTo('https://api.cronofy.com/v1/real_time_sequencing'), 434 | $this->equalTo($params), 435 | $this->equalTo([ 436 | 'Authorization: Bearer accessToken', 437 | 'Host: api.cronofy.com', 438 | 'Content-Type: application/json; charset=utf-8' 439 | ]) 440 | ) 441 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 442 | 443 | $cronofy = new Cronofy([ 444 | "client_id" => "clientId", 445 | "client_secret" => "clientSecret", 446 | "access_token" => "accessToken", 447 | "refresh_token" => "refreshToken", 448 | "http_client" => $http, 449 | ]); 450 | 451 | $actual = $cronofy->realTimeSequencing($params); 452 | $this->assertNotNull($actual); 453 | } 454 | } 455 | -------------------------------------------------------------------------------- /tests/UpsertEventTest.php: -------------------------------------------------------------------------------- 1 | "board room", 14 | "latitude" => "12.2344", 15 | "longitude" => "45.2444", 16 | ]; 17 | 18 | $reminders = [ 19 | ["minutes" => 30], 20 | ["minutes" => 1440] 21 | ]; 22 | 23 | $attendees = [ 24 | "invite" => [["email" => "new_invitee@test.com", "display_name" => "New Invitee"]], 25 | "reject" => [["email" => "old_invitee@test.com", "display_name" => "Old Invitee"]] 26 | ]; 27 | 28 | $calendarId = "cal_123"; 29 | 30 | $event = [ 31 | "event_id" => "partner_event_id", 32 | "summary" => "Upsert Event Test", 33 | "description" => "description example", 34 | "start" => "2017-01-01T12:00:00Z", 35 | "end" => "2017-01-01T15:00:00Z", 36 | "tzid" => "Europe/London", 37 | "location" => $location, 38 | "reminders" => $reminders, 39 | "attendees" => $attendees, 40 | "event_private" => true, 41 | "reminders_create_only" => true, 42 | "transparency" => "opaque", 43 | "color" => "#c6040f", 44 | "conferencing" => [ 45 | "profile_id" => "default" 46 | ], 47 | "locale" => "it", 48 | "event_classes" => "event_classes" 49 | ]; 50 | 51 | $params = $event + ["calendar_id" => $calendarId]; 52 | 53 | $http = $this->createMock(HttpRequest::class); 54 | $http->expects($this->once()) 55 | ->method('httpPost') 56 | ->with( 57 | $this->equalTo('https://api.cronofy.com/v1/calendars/'.$calendarId.'/events'), 58 | $this->equalTo($event), 59 | $this->equalTo([ 60 | 'Authorization: Bearer accessToken', 61 | 'Host: api.cronofy.com', 62 | 'Content-Type: application/json; charset=utf-8' 63 | ]) 64 | ) 65 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 66 | 67 | $cronofy = new Cronofy([ 68 | "client_id" => "clientId", 69 | "client_secret" => "clientSecret", 70 | "access_token" => "accessToken", 71 | "refresh_token" => "refreshToken", 72 | "http_client" => $http, 73 | ]); 74 | 75 | $actual = $cronofy->upsertEvent($params); 76 | $this->assertNotNull($actual); 77 | } 78 | 79 | public function testUpsertExternalEvent() 80 | { 81 | $location = [ 82 | "description" => "board room", 83 | "latitude" => "12.2344", 84 | "longitude" => "45.2444", 85 | ]; 86 | 87 | $reminders = [ 88 | ["minutes" => 30], 89 | ["minutes" => 1440] 90 | ]; 91 | 92 | $attendees = [ 93 | "invite" => [["email" => "new_invitee@test.com", "display_name" => "New Invitee"]], 94 | "reject" => [["email" => "old_invitee@test.com", "display_name" => "Old Invitee"]] 95 | ]; 96 | 97 | $calendarId = "cal_123"; 98 | 99 | $event = [ 100 | "event_uid" => "evt_external_22343948494", 101 | "summary" => "Upsert Event Test", 102 | "description" => "description example", 103 | "start" => "2017-01-01T12:00:00Z", 104 | "end" => "2017-01-01T15:00:00Z", 105 | "tzid" => "Europe/London", 106 | "location" => $location, 107 | "reminders" => $reminders, 108 | "attendees" => $attendees, 109 | "event_private" => true, 110 | "reminders_create_only" => true, 111 | "transparency" => "opaque", 112 | ]; 113 | 114 | $params = $event + ["calendar_id" => $calendarId]; 115 | 116 | $http = $this->createMock(HttpRequest::class); 117 | $http->expects($this->once()) 118 | ->method('httpPost') 119 | ->with( 120 | $this->equalTo('https://api.cronofy.com/v1/calendars/'.$calendarId.'/events'), 121 | $this->equalTo($event), 122 | $this->equalTo([ 123 | 'Authorization: Bearer accessToken', 124 | 'Host: api.cronofy.com', 125 | 'Content-Type: application/json; charset=utf-8' 126 | ]) 127 | ) 128 | ->will($this->returnValue(["{'foo': 'bar'}", 200])); 129 | 130 | $cronofy = new Cronofy([ 131 | "client_id" => "clientId", 132 | "client_secret" => "clientSecret", 133 | "access_token" => "accessToken", 134 | "refresh_token" => "refreshToken", 135 | "http_client" => $http, 136 | ]); 137 | 138 | $actual = $cronofy->upsertExternalEvent($params); 139 | $this->assertNotNull($actual); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 |