cronofy 0.37.1 → 0.37.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a805a11bb064f04901d8cb57e9941bb71db91224c3c61f351b827c75074d1db5
4
- data.tar.gz: ec146b1c4c31ced2d208bca1f066b15a4b162edacaac0bc0f7cf1da079f0d1d2
3
+ metadata.gz: 1338eaab0825a963c74f2c5fd3c434d01185a4bbc36fa57504b0e1edd539fe9e
4
+ data.tar.gz: 55a045316dbb1545ee7e4f14284ebbbc0aa7b91848ba28d165968125d2221bb3
5
5
  SHA512:
6
- metadata.gz: 38b1b55c2f736c9692c0e11ebd03d803709e485b7d51ce2522c0a32b2333b75faa2ca56658315f9390b44b7f6a0a1b3b2abfdeca98f22a94d8e4dddf6cada60c
7
- data.tar.gz: 22645a8c38f429465ec6bcbacc2f8a9463a1895597d68b21f853a4c57c2630cb4db0adcbd1204d9a2187a33e598df0de9c94e20dd63794b9e807b621648df110
6
+ metadata.gz: 38ee7c99226bfe70c6e970a22477de0130e93fe07130b9d2c0a596e17178f66435ec58a97be5f18e9b6ac5624eefef7dde96443a8cc91c85f02818016ed64f7d
7
+ data.tar.gz: 8d0d1641e57b0751988ac5f7978571d2dbd6d6ea12b96ff2314a33d0d9b87c7a11eb5e16775c55b070a312c2efb6b8b353dc3c3f7a8cbe9b3f42c943d6afa21a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.37.4]
2
+
3
+ * Support client_secret only clients being able to authorize `#availability` calls. [#97]
4
+
5
+ ## [0.37.3]
6
+
7
+ * Support `hmac_valid` as well as the original `hmac_match` for Client to verify a HMAC from a push notification using the client's secret.[#95]
8
+
9
+ ## [0.37.2]
10
+
11
+ * Support `query_periods` as well as the original `available_periods` for Availability Query and Sequenced Availability [#91]
12
+
1
13
  ## [0.37.1]
2
14
 
3
15
  * Rename `data_centre` to `data_centre` (with aliases for backwards compatibility) [#90]
@@ -182,6 +194,9 @@
182
194
  [0.36.1]: https://github.com/cronofy/cronofy-ruby/releases/tag/v0.36.1
183
195
  [0.37.0]: https://github.com/cronofy/cronofy-ruby/releases/tag/v0.37.0
184
196
  [0.37.1]: https://github.com/cronofy/cronofy-ruby/releases/tag/v0.37.1
197
+ [0.37.2]: https://github.com/cronofy/cronofy-ruby/releases/tag/v0.37.2
198
+ [0.37.3]: https://github.com/cronofy/cronofy-ruby/releases/tag/v0.37.3
199
+ [0.37.4]: https://github.com/cronofy/cronofy-ruby/releases/tag/v0.37.4
185
200
 
186
201
  [#13]: https://github.com/cronofy/cronofy-ruby/pull/13
187
202
  [#16]: https://github.com/cronofy/cronofy-ruby/pull/16
@@ -225,3 +240,6 @@
225
240
  [#85]: https://github.com/cronofy/cronofy-ruby/pull/85
226
241
  [#86]: https://github.com/cronofy/cronofy-ruby/pull/86
227
242
  [#90]: https://github.com/cronofy/cronofy-ruby/pull/90
243
+ [#91]: https://github.com/cronofy/cronofy-ruby/pull/91
244
+ [#95]: https://github.com/cronofy/cronofy-ruby/pull/95
245
+ [#97]: https://github.com/cronofy/cronofy-ruby/pull/97
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Cronofy
2
2
 
3
- [![Build Status](https://travis-ci.org/cronofy/cronofy-ruby.svg?branch=master)](https://travis-ci.org/cronofy/cronofy-ruby)
3
+ [![ruby CI](https://github.com/cronofy/cronofy-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/cronofy/cronofy-ruby/actions/workflows/ci.yml)
4
4
  [![Gem Version](https://badge.fury.io/rb/cronofy.svg)](http://badge.fury.io/rb/cronofy)
5
5
 
6
6
  [Cronofy](https://www.cronofy.com) - the scheduling platform for business
@@ -134,6 +134,15 @@ To delete an event from user's calendar:
134
134
  cronofy.delete_event(calendar_id, 'uniq-id')
135
135
  ```
136
136
 
137
+ ## A feature I want is not in the SDK, how do I get it?
138
+
139
+ We add features to this SDK as they are requested, to focus on developing the Cronofy API.
140
+
141
+ If you're comfortable contributing support for an endpoint or attribute, then we love to receive pull requests!
142
+ Please create a PR mentioning the feature/API endpoint you’ve added and we’ll review it as soon as we can.
143
+
144
+ 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).
145
+
137
146
  ## Links
138
147
 
139
148
  * [API documentation](https://www.cronofy.com/developers/api)
@@ -471,24 +471,33 @@ module Cronofy
471
471
  parse_json(Channel, "channel", response)
472
472
  end
473
473
 
474
+ # DEPRECATED: Please use hmac_valid instead.
475
+ def hmac_match?(args)
476
+ warn "[DEPRECATION] `hmac_match?` is deprecated. Please use `hmac_valid?` instead."
477
+ hmac_valid?(args)
478
+ end
479
+
474
480
  # Public: Verifies a HMAC from a push notification using the client secret.
475
481
  #
476
482
  # args - A Hash containing the details of the push notification:
477
483
  # :body - A String of the body of the notification.
478
- # :hmac - A String of the HMAC of the notification taken from the
484
+ # :hmac - A String containing comma-separated values describing HMACs of the notification taken from the
479
485
  # Cronofy-HMAC-SHA256 header.
480
486
  #
481
- # Returns true if the HMAC provided matches the one calculated using the
487
+ # Returns true if one of the HMAC provided matches the one calculated using the
482
488
  # client secret, otherwise false.
483
- def hmac_match?(args)
489
+ def hmac_valid?(args)
484
490
  body = args[:body]
485
491
  hmac = args[:hmac]
486
492
 
493
+ return false if hmac.nil? || hmac.empty?
494
+
487
495
  sha256 = OpenSSL::Digest.new('sha256')
488
496
  digest = OpenSSL::HMAC.digest(sha256, @client_secret, body)
489
497
  calculated = Base64.encode64(digest).strip
490
498
 
491
- calculated == hmac
499
+ hmac_list = hmac.split(',')
500
+ hmac_list.include?(calculated)
492
501
  end
493
502
 
494
503
  # Public: Lists all the notification channels for the account.
@@ -803,7 +812,7 @@ module Cronofy
803
812
  # for a single participant group.
804
813
  # :required_duration - An Integer representing the minimum number
805
814
  # of minutes of availability required.
806
- # :available_periods - An Array of available time periods Hashes,
815
+ # :query_periods - An Array of available time periods Hashes,
807
816
  # each must specify a start and end Time.
808
817
  # :start_interval - An Integer representing the start interval
809
818
  # of minutes for the availability query.
@@ -833,9 +842,9 @@ module Cronofy
833
842
  options[:buffer] = map_availability_buffer(buffer)
834
843
  end
835
844
 
836
- translate_available_periods(options[:available_periods])
845
+ translate_available_periods(options[:query_periods] || options[:available_periods])
837
846
 
838
- response = post("/v1/availability", options)
847
+ response = availability_post("/v1/availability", options)
839
848
 
840
849
  parse_collections(
841
850
  response,
@@ -847,7 +856,7 @@ module Cronofy
847
856
  # Public: Performs an sequenced availability query.
848
857
  #
849
858
  # options - The Hash options used to refine the selection (default: {}):
850
- # :sequence - An Array of sequence defintions containing
859
+ # :sequence - An Array of sequence defintions containing
851
860
  # a Hash of:
852
861
  # :sequence_id - A String to uniquely identify this part
853
862
  # of the proposed sequence.
@@ -861,7 +870,7 @@ module Cronofy
861
870
  # of minutes for the availability query.
862
871
  # :buffer - An Hash containing the buffer to apply to
863
872
  # the availability query.
864
- # :available_periods - An Array of available time periods Hashes,
873
+ # :query_periods - An Array of available time periods Hashes,
865
874
  # each must specify a start and end Time.
866
875
  #
867
876
  # Returns an Array of Sequences.
@@ -878,9 +887,9 @@ module Cronofy
878
887
  def sequenced_availability(options = {})
879
888
  options[:sequence] = map_availability_sequence(options[:sequence])
880
889
 
881
- translate_available_periods(options[:available_periods])
890
+ translate_available_periods(options[:query_periods] || options[:available_periods])
882
891
 
883
- response = post("/v1/sequenced_availability", options)
892
+ response = availability_post("/v1/sequenced_availability", options)
884
893
  parse_collection(Sequence, "sequences", response)
885
894
  end
886
895
 
@@ -1802,6 +1811,17 @@ module Cronofy
1802
1811
  wrapped_request { @auth.api_client.request(:post, url, json_request_args(body)) }
1803
1812
  end
1804
1813
 
1814
+ # Availability Query could originally be authenticated via an access_token
1815
+ # Whilst it should be authed via an API key now, we try access_token first
1816
+ # for backward compatibility
1817
+ def availability_post(url, body)
1818
+ if @auth.access_token
1819
+ post(url, body)
1820
+ else
1821
+ wrapped_request { api_key!.post(url, json_request_args(body)) }
1822
+ end
1823
+ end
1824
+
1805
1825
  def wrapped_request
1806
1826
  yield
1807
1827
  rescue OAuth2::Error => e
@@ -1,3 +1,3 @@
1
1
  module Cronofy
2
- VERSION = "0.37.1".freeze
2
+ VERSION = "0.37.4".freeze
3
3
  end
@@ -266,6 +266,7 @@ describe Cronofy::Client do
266
266
  ],
267
267
  }
268
268
  end
269
+
269
270
  let(:request_body) do
270
271
  {
271
272
  :event_id => "qTtZdczOccgaPncGJaCiLg",
@@ -1256,6 +1257,17 @@ describe Cronofy::Client do
1256
1257
  let(:request_url) { 'https://api.cronofy.com/v1/availability' }
1257
1258
  let(:request_headers) { json_request_headers }
1258
1259
 
1260
+ let(:client_id) { 'example_id' }
1261
+ let(:client_secret) { 'example_secret' }
1262
+ let(:token) { client_secret }
1263
+
1264
+ let(:client) do
1265
+ Cronofy::Client.new(
1266
+ client_id: client_id,
1267
+ client_secret: client_secret,
1268
+ )
1269
+ end
1270
+
1259
1271
  let(:request_body) do
1260
1272
  {
1261
1273
  "participants" => [
@@ -1718,6 +1730,138 @@ describe Cronofy::Client do
1718
1730
  it_behaves_like 'a Cronofy request'
1719
1731
  it_behaves_like 'a Cronofy request with mapped return value'
1720
1732
  end
1733
+
1734
+ context "when given query_periods instead of available_periods" do
1735
+ let(:participants) do
1736
+ { members: %w{acc_567236000909002 acc_678347111010113} }
1737
+ end
1738
+
1739
+ let(:required_duration) { 60 }
1740
+
1741
+ let(:query_periods) do
1742
+ [
1743
+ { start: Time.parse("2017-01-03T09:00:00Z"), end: Time.parse("2017-01-03T18:00:00Z") },
1744
+ { start: Time.parse("2017-01-04T09:00:00Z"), end: Time.parse("2017-01-04T18:00:00Z") },
1745
+ ]
1746
+ end
1747
+
1748
+ let(:request_body) do
1749
+ {
1750
+ "participants" => [
1751
+ {
1752
+ "members" => [
1753
+ { "sub" => "acc_567236000909002" },
1754
+ { "sub" => "acc_678347111010113" }
1755
+ ],
1756
+ "required" => "all"
1757
+ }
1758
+ ],
1759
+ "query_periods" => [
1760
+ {
1761
+ "start" => "2017-01-03T09:00:00Z",
1762
+ "end" => "2017-01-03T18:00:00Z"
1763
+ },
1764
+ {
1765
+ "start" => "2017-01-04T09:00:00Z",
1766
+ "end" => "2017-01-04T18:00:00Z"
1767
+ }
1768
+ ],
1769
+ "required_duration" => { "minutes" => 60 },
1770
+ }
1771
+ end
1772
+
1773
+ subject do
1774
+ client.availability(
1775
+ participants: participants,
1776
+ required_duration: required_duration,
1777
+ query_periods: query_periods
1778
+ )
1779
+ end
1780
+
1781
+ it_behaves_like 'a Cronofy request'
1782
+ it_behaves_like 'a Cronofy request with mapped return value'
1783
+ end
1784
+
1785
+ context "when trying to auth with only an access_token, as originally implemented" do
1786
+ let(:access_token) { "access_token_123"}
1787
+ let(:client) { Cronofy::Client.new(access_token: access_token) }
1788
+ let(:request_headers) do
1789
+ {
1790
+ "Authorization" => "Bearer #{access_token}",
1791
+ "User-Agent" => "Cronofy Ruby #{::Cronofy::VERSION}",
1792
+ "Content-Type" => "application/json; charset=utf-8",
1793
+ }
1794
+ end
1795
+
1796
+ let(:participants) do
1797
+ { members: %w{acc_567236000909002 acc_678347111010113} }
1798
+ end
1799
+
1800
+ let(:required_duration) { 60 }
1801
+
1802
+ let(:available_periods) do
1803
+ [
1804
+ { start: Time.parse("2017-01-03T09:00:00Z"), end: Time.parse("2017-01-03T18:00:00Z") },
1805
+ { start: Time.parse("2017-01-04T09:00:00Z"), end: Time.parse("2017-01-04T18:00:00Z") },
1806
+ ]
1807
+ end
1808
+
1809
+ it_behaves_like 'a Cronofy request'
1810
+ it_behaves_like 'a Cronofy request with mapped return value'
1811
+ end
1812
+
1813
+ context "when trying to auth with both a client_secret and access_token" do
1814
+ let(:access_token) { "access_token_123" }
1815
+ let(:client_secret) { "client_secret_456" }
1816
+ let(:client) { Cronofy::Client.new(access_token: access_token, client_secret: client_secret) }
1817
+ let(:request_headers) do
1818
+ {
1819
+ "Authorization" => "Bearer #{access_token}",
1820
+ "User-Agent" => "Cronofy Ruby #{::Cronofy::VERSION}",
1821
+ "Content-Type" => "application/json; charset=utf-8",
1822
+ }
1823
+ end
1824
+
1825
+ let(:participants) do
1826
+ { members: %w{acc_567236000909002 acc_678347111010113} }
1827
+ end
1828
+
1829
+ let(:required_duration) { 60 }
1830
+
1831
+ let(:available_periods) do
1832
+ [
1833
+ { start: Time.parse("2017-01-03T09:00:00Z"), end: Time.parse("2017-01-03T18:00:00Z") },
1834
+ { start: Time.parse("2017-01-04T09:00:00Z"), end: Time.parse("2017-01-04T18:00:00Z") },
1835
+ ]
1836
+ end
1837
+
1838
+ describe "it prefers the access_token for backward compatibility" do
1839
+ it_behaves_like 'a Cronofy request'
1840
+ it_behaves_like 'a Cronofy request with mapped return value'
1841
+ end
1842
+ end
1843
+
1844
+ context "when trying to auth without a client_secret or access_token" do
1845
+ let(:client) { Cronofy::Client.new }
1846
+
1847
+ let(:participants) do
1848
+ { members: %w{acc_567236000909002 acc_678347111010113} }
1849
+ end
1850
+
1851
+ let(:required_duration) { 60 }
1852
+
1853
+ let(:available_periods) do
1854
+ [
1855
+ { start: Time.parse("2017-01-03T09:00:00Z"), end: Time.parse("2017-01-03T18:00:00Z") },
1856
+ { start: Time.parse("2017-01-04T09:00:00Z"), end: Time.parse("2017-01-04T18:00:00Z") },
1857
+ ]
1858
+ end
1859
+
1860
+
1861
+ it "raises an API Key error" do
1862
+ expect{ subject }.to raise_error(Cronofy::CredentialsMissingError)
1863
+ end
1864
+ end
1721
1865
  end
1722
1866
  end
1723
1867
 
@@ -1727,6 +1871,17 @@ describe Cronofy::Client do
1727
1871
  let(:request_url) { 'https://api.cronofy.com/v1/sequenced_availability' }
1728
1872
  let(:request_headers) { json_request_headers }
1729
1873
 
1874
+ let(:client_id) { 'example_id' }
1875
+ let(:client_secret) { 'example_secret' }
1876
+ let(:token) { client_secret }
1877
+
1878
+ let(:client) do
1879
+ Cronofy::Client.new(
1880
+ client_id: client_id,
1881
+ client_secret: client_secret,
1882
+ )
1883
+ end
1884
+
1730
1885
  let(:request_body) do
1731
1886
  {
1732
1887
  "sequence" => [
@@ -1873,6 +2028,57 @@ describe Cronofy::Client do
1873
2028
 
1874
2029
  it_behaves_like 'a Cronofy request'
1875
2030
  it_behaves_like 'a Cronofy request with mapped return value'
2031
+
2032
+ context "when passing query_periods instead" do
2033
+ before do
2034
+ args[:query_periods] = args[:available_periods]
2035
+ args.delete(:available_periods)
2036
+ request_body["query_periods"] = request_body["available_periods"]
2037
+ request_body.delete("available_periods")
2038
+ end
2039
+
2040
+ it_behaves_like 'a Cronofy request'
2041
+ it_behaves_like 'a Cronofy request with mapped return value'
2042
+ end
2043
+
2044
+ context "when trying to auth with access_token only" do
2045
+ let(:access_token) { "access_token_123"}
2046
+ let(:client) { Cronofy::Client.new(access_token: access_token) }
2047
+ let(:request_headers) do
2048
+ {
2049
+ "Authorization" => "Bearer #{access_token}",
2050
+ "User-Agent" => "Cronofy Ruby #{::Cronofy::VERSION}",
2051
+ "Content-Type" => "application/json; charset=utf-8",
2052
+ }
2053
+ end
2054
+
2055
+ it_behaves_like 'a Cronofy request'
2056
+ it_behaves_like 'a Cronofy request with mapped return value'
2057
+ end
2058
+
2059
+ context "when trying to auth with both access_token and client_secret provided" do
2060
+ let(:client_id) { 'example_id' }
2061
+ let(:client_secret) { 'example_secret' }
2062
+ let(:access_token) { "access_token_123"}
2063
+
2064
+ let(:client) do
2065
+ Cronofy::Client.new(
2066
+ client_id: client_id,
2067
+ client_secret: client_secret,
2068
+ access_token: access_token,
2069
+ )
2070
+ end
2071
+ let(:request_headers) do
2072
+ {
2073
+ "Authorization" => "Bearer #{access_token}",
2074
+ "User-Agent" => "Cronofy Ruby #{::Cronofy::VERSION}",
2075
+ "Content-Type" => "application/json; charset=utf-8",
2076
+ }
2077
+ end
2078
+
2079
+ it_behaves_like 'a Cronofy request'
2080
+ it_behaves_like 'a Cronofy request with mapped return value'
2081
+ end
1876
2082
  end
1877
2083
  end
1878
2084
 
@@ -2417,11 +2623,27 @@ describe Cronofy::Client do
2417
2623
  let(:body) { "{\"example\":\"well-known\"}" }
2418
2624
 
2419
2625
  it "verifies the correct HMAC" do
2420
- expect(client.hmac_match?(body: body, hmac: "6r2/HjBkqymGegX0wOfifieeUXbbHwtV/LohHS+jv6c=")).to be true
2626
+ expect(client.hmac_valid?(body: body, hmac: "6r2/HjBkqymGegX0wOfifieeUXbbHwtV/LohHS+jv6c=")).to be true
2421
2627
  end
2422
2628
 
2423
2629
  it "rejects an incorrect HMAC" do
2424
- expect(client.hmac_match?(body: body, hmac: "something-else")).to be false
2630
+ expect(client.hmac_valid?(body: body, hmac: "something-else")).to be false
2631
+ end
2632
+
2633
+ it "verifies the correct HMAC when one of the multiple HMACs splitted by ',' match" do
2634
+ expect(client.hmac_valid?(body: body, hmac: "6r2/HjBkqymGegX0wOfifieeUXbbHwtV/LohHS+jv6c=,something-else")).to be true
2635
+ end
2636
+
2637
+ it "rejects incorrect when multiple HMACs splitted by ',' don't match" do
2638
+ expect(client.hmac_valid?(body: body, hmac: "something-else,something-else2")).to be false
2639
+ end
2640
+
2641
+ it "rejects if empty HMAC" do
2642
+ expect(client.hmac_valid?(body: body, hmac: "")).to be false
2643
+ end
2644
+
2645
+ it "rejects if nil HMAC" do
2646
+ expect(client.hmac_valid?(body: body, hmac: nil)).to be false
2425
2647
  end
2426
2648
  end
2427
2649
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cronofy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.37.1
4
+ version: 0.37.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergii Paryzhskyi
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-01-14 00:00:00.000000000 Z
12
+ date: 2022-03-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: hashie
@@ -156,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
156
156
  - !ruby/object:Gem::Version
157
157
  version: '0'
158
158
  requirements: []
159
- rubygems_version: 3.2.4
159
+ rubygems_version: 3.2.32
160
160
  signing_key:
161
161
  specification_version: 4
162
162
  summary: Cronofy - the scheduling platform for business