cronofy 0.37.1 → 0.37.4

Sign up to get free protection for your applications and to get access to all the features.
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