ruby-office365 0.1.5 → 0.1.7

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: 86ee47df4ac96c36024adb5c0595afa78889dc9659aebcdbdf01a59e407a0db9
4
- data.tar.gz: 42ab9271c0d32b6035ad9caecc837137774bde57d0894fd49f34782d8c0f1f1b
3
+ metadata.gz: '0490fef4bbf78efa42231c69e2b12fdb9f12ec37805c5e846225568e85cb03ba'
4
+ data.tar.gz: a40082526d63f968875194b969146f787de82090207132c69b7d62c344719c6f
5
5
  SHA512:
6
- metadata.gz: 8dd5a3b6d974b91bf6c2846b1b9b3f433fcc1abe11d6dfc85f3fe3a7b023313db0079b6056a1e27d3d8a36506a434a4ef0e45f327f4d2dd25b0416f681ff5906
7
- data.tar.gz: fc2997832814ec584184584546ca7443841914191056f5fd3c080017cf21b273a05f9dd5612b928ad7570fb3cbc202ff0cbaba01e86a62b250c409f1e87776b9
6
+ metadata.gz: 1a5220233fdf3095a0423a0433b54007ad251cc1bd3dfea051a6c2b2ef3e83586d3142198deda976ffa3425e9c6b97723e0e78af9a564872d2c42e1a9bba7a68
7
+ data.tar.gz: 445c96c052a74d5ca866769182c98a3da62de4308f83d52db760b5d2bac1fbce3c7be02cdecdfde933ea0d0c3e1c98c264f3175bba68dfbd933c8e29b55f8d15
data/.rubocop.yml CHANGED
@@ -22,4 +22,7 @@ Metrics/BlockLength:
22
22
  Max: 150
23
23
 
24
24
  Metrics/MethodLength:
25
- Max: 50
25
+ Max: 50
26
+
27
+ Metrics/CyclomaticComplexity:
28
+ Max: 20
data/CHANGELOG.md CHANGED
@@ -15,12 +15,12 @@
15
15
 
16
16
  - Integrate REST API to get mailbox with pagination
17
17
  - get mailbox data with next link `client.messages({next_link: 'xxx'})`
18
- - get mailbox data with next link `client.calenders({next_link: 'xxx'})`
18
+ - get calenders data with next link `client.calenders({next_link: 'xxx'})`
19
19
 
20
20
  ## [0.1.3] - (2022-10-26)
21
21
 
22
22
  - Integrate REST API to get contacts
23
- - get profile `client.contacts`
23
+ - get contacts `client.contacts`
24
24
  - get contacts data with next link `client.contacts({next_link: 'xxx'})`
25
25
 
26
26
  ## [0.1.5] - (2022-10-27)
@@ -28,4 +28,16 @@
28
28
  - Generate URLs for token and able to refresh token
29
29
  - get authorize URL `client.authorize_url`
30
30
  - get token URL `client.token_url`
31
- - be able to refresh token `client.refresh_token!`
31
+ - be able to refresh token `client.refresh_token!`
32
+
33
+ ## [0.1.6] - (2022-11-01)
34
+
35
+ - Integrate REST API to get events
36
+ - get events `client.events`
37
+ - get events data with next link `client.events({next_link: 'xxx'})`
38
+
39
+ ## [0.1.7] - (2022-11-11)
40
+
41
+ - Improve performance: supports select and sort in REST APIs
42
+ - get messages by select fields `client.messages({ select: %[id] })`
43
+ - get messages by custom order `client.messages({ order: 'id asc' })`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-office365 (0.1.5)
4
+ ruby-office365 (0.1.7)
5
5
  faraday
6
6
  faraday_middleware
7
7
 
data/README.md CHANGED
@@ -1,8 +1,5 @@
1
- # Office 365
2
-
3
- A simple ruby library to interact with Microsoft Graph and Office 365 API.
4
-
5
- - https://developer.microsoft.com/en-us/graph/graph-explorer
1
+ # Office 365 (2022) <img src="https://i.ibb.co/g3mpswn/microsoft-office-365-logo-2016-100727915-large.webp" align="right" width="250" height="150">
2
+ A simple ruby library to interact with Microsoft Graph **[Explorer](https://developer.microsoft.com/en-us/graph/graph-explorer)** and Office 365 API
6
3
 
7
4
  ## Installation
8
5
 
@@ -14,35 +11,57 @@ If bundler is not being used to manage dependencies, install the gem by executin
14
11
 
15
12
  $ gem install ruby-office365
16
13
 
14
+ ## Features
15
+
16
+ - Supports user's mailboxes, calendars, contacts, events
17
+ - Supports refresh token
18
+
17
19
  ## Configuration
18
20
 
19
21
  You can pass configuration options as a block to `Office365::REST::Client.new`.
20
22
 
23
+ - tenant_id: optional, only required for refresh_token
24
+ - client_id: optional, only required for refresh_token
25
+ - client_secret: optional, only required for refresh_token
26
+ - access_token: required for fetch mailbox, calendars, contacts data
27
+ - refresh_token: optional, only required for refresh_token
28
+ - debug: optional, default false, output the request information
29
+
30
+ ### Used to get data from MS Graph API
31
+
21
32
  ```ruby
22
33
  client = Office365::REST::Client.new do |config|
23
- config.tenant_id = "YOUR_ORG_TENANT_ID"
24
- config.client_id = "YOUR_APP_CLIENT_ID"
25
- config.client_secret = "YOUR_APP_CLIENT_SECRET"
26
- config.redirect_uri = "YOUR_APP_REDIRECT_URL"
27
- config.access_token = "YOUR_ACCESS_TOKEN"
28
- config.refresh_token = "YOUR_REFRESH_TOKEN"
34
+ config.access_token = "YOUR_ACCESS_TOKEN"
35
+ config.debug = "true/false" # Optional, default to false, output the information in the request
29
36
  end
30
37
  ```
31
38
 
32
- ## Usage Examples
39
+ ### For refresh token
40
+
41
+ ```ruby
42
+ client = Office365::REST::Client.new do |config|
43
+ config.tenant_id = "YOUR_ORG_TENANT_ID"
44
+ config.client_id = "YOUR_APP_CLIENT_ID"
45
+ config.client_secret = "YOUR_APP_CLIENT_SECRET"
46
+ config.refresh_token = "YOUR_REFRESH_TOKEN"
47
+ config.debug = "true/false" # Optional, default to false, output the information in the request
48
+ end
49
+ ```
50
+
51
+ ## Usage
33
52
 
34
53
  After configuring a `client`, you can do the following things.
35
54
 
36
- **response**
55
+ **Response structure**
37
56
 
38
- - `results`: wrap all data into results
39
- - `next_link`: get the new link for the next page of data
57
+ - results: wrap all data into results
58
+ - next_link: get the new link for the next page of data
40
59
 
41
- **to JSON**
60
+ **A simple way to convert an office365 object to JSON**
42
61
 
43
- - `as_json`: convert office365 object to JSON format
62
+ - as_json: convert office365 object to JSON format
44
63
 
45
- **Get Profile (as the authenticated user)**
64
+ **Get my profile by access token**
46
65
 
47
66
  ```ruby
48
67
  irb(main):004:0> response = client.me
@@ -65,7 +84,7 @@ irb(main):004:0> response.as_json
65
84
  }
66
85
  ```
67
86
 
68
- **Get my calendars**
87
+ **Get my calendars by access token**
69
88
 
70
89
  ```ruby
71
90
  irb(main):005:0> client.calendars
@@ -73,7 +92,15 @@ irb(main):005:0> client.calendars[:results]
73
92
  irb(main):005:0> client.calendars[:next_link]
74
93
  ```
75
94
 
76
- **Get my mails**
95
+ **Get my events by access token**
96
+
97
+ ```ruby
98
+ irb(main):005:0> client.events
99
+ irb(main):005:0> client.events[:results]
100
+ irb(main):005:0> client.events[:next_link]
101
+ ```
102
+
103
+ **Get my mails by access token**
77
104
 
78
105
  ```ruby
79
106
  irb(main):005:0> client.messages
@@ -82,7 +109,7 @@ irb(main):005:0> client.messages({ filter: "createdDateTime lt 2022-01-01" })
82
109
  irb(main):005:0> client.messages({ filter: "createdDateTime lt 2022-01-01", next_link: 'https://....' })
83
110
  ```
84
111
 
85
- **Get my contacts**
112
+ **Get my contacts by access token**
86
113
 
87
114
  ```ruby
88
115
  irb(main):018:0> response = client.contacts
@@ -132,7 +159,7 @@ irb(main):018:0> response[:results][0].as_json
132
159
  }
133
160
  ```
134
161
 
135
- **Refresh User Token**
162
+ **Refresh access token by refresh token**
136
163
 
137
164
  ```ruby
138
165
  irb(main):005:0> response = client.refresh_token!
@@ -144,6 +171,12 @@ irb(main):005:0> response.refresh_token
144
171
  => "0.ARgA7EiQdLv1qECnFqPfrznKsT9ERYaGfG9Ki5WzQtEllj8YAJk.AgABAAEAAAD--DLA3VO7QrddgJg7WevrAgDs_wQA9P-Q1ODlBsrdZi-5s2mfLtEsavBgiEhGcz1KEf26fMrGFU3LM_og5l6wjSAtQ83XHLuje0_KYGol26_LGV_uH0F1MwCFR1N3ctwg4_...."
145
172
  ```
146
173
 
174
+ ## Development
175
+
176
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
177
+
178
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
179
+
147
180
  ## Copyright
148
181
 
149
182
  Copyright (c) 2022 Encore Shao. See LICENSE for details.
@@ -6,7 +6,7 @@ module Office365
6
6
  module Models
7
7
  class Base < OpenStruct
8
8
  def initialize(response = {})
9
- super(response.transform_keys(&:o_underscore))
9
+ super(response.transform_keys(&:rails_underscore))
10
10
  end
11
11
 
12
12
  def as_json
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Office365
4
+ module Models
5
+ class Event < Base
6
+ # attr_accessor :id, :subject, :body, :start, :end, :location, :attendees, :organizer
7
+ end
8
+ end
9
+ end
@@ -11,5 +11,6 @@ module Office365
11
11
  autoload :EmailAddress, "office365/models/email_address"
12
12
  autoload :Contact, "office365/models/contact"
13
13
  autoload :AccessToken, "office365/models/access_token"
14
+ autoload :Event, "office365/models/event"
14
15
  end
15
16
  end
@@ -4,6 +4,7 @@ require_relative "./user"
4
4
  require_relative "./mailbox"
5
5
  require_relative "./calendar"
6
6
  require_relative "./contact"
7
+ require_relative "./event"
7
8
  require_relative "./token"
8
9
 
9
10
  module Office365
@@ -12,6 +13,7 @@ module Office365
12
13
  include Office365::REST::User
13
14
  include Office365::REST::Mailbox
14
15
  include Office365::REST::Calendar
16
+ include Office365::REST::Event
15
17
  include Office365::REST::Contact
16
18
  include Office365::REST::Token
17
19
  end
@@ -10,12 +10,7 @@ module Office365
10
10
  # params: args => { next_link: (nil / next_page_url) }
11
11
  # response { results: [], next_link: '...' }
12
12
  def calendars(args = {})
13
- response = message_response(args: args.merge(base_uri: "/me/calendars"))
14
-
15
- {
16
- results: response["value"].map { |v| Models::Calendar.new(v) },
17
- next_link: response["@odata.nextLink"]
18
- }
13
+ wrap_results(args.merge(kclass: Models::Calendar, base_uri: "/me/calendars"))
19
14
  end
20
15
  end
21
16
  end
@@ -6,21 +6,51 @@ module Office365
6
6
  module Base
7
7
  private
8
8
 
9
- def message_response(args: {})
10
- next_link = args.delete(:next_link)
9
+ def wrap_results(args)
10
+ kclass = args.delete(:kclass)
11
+ response = get_request(args: args)
11
12
 
12
- request_uri = if next_link
13
- next_link
14
- else
15
- base_uri = args.delete(:base_uri)
13
+ {
14
+ results: response["value"].map { |v| kclass.new(v) },
15
+ next_link: response["@odata.nextLink"]
16
+ }
17
+ end
16
18
 
17
- req_uri = ["/", Office365::API_VERSION, base_uri].join
18
- req_uri += ["?", args.to_query].join if args.any?
19
- req_uri
20
- end
19
+ def get_request(args: {})
20
+ request_uri = parse_and_build_request_url(args)
21
21
 
22
22
  Request.new(access_token, debug: debug).get(request_uri)
23
23
  end
24
+
25
+ # https://learn.microsoft.com/en-us/graph/query-parameters?view=graph-rest-1.0
26
+ # OData system query options
27
+ # Click the examples to try them in [Graph Explorer](https://developer.microsoft.com/zh-cn/graph/graph-explorer)
28
+
29
+ # $count Retrieves the total count of matching resources. /me/messages?$top=2&$count=true
30
+ # $expand Retrieves related resources. /groups?$expand=members
31
+ # $filter Filters results (rows). /users?$filter=startswith(givenName,'J')
32
+ # $format Returns the results in the specified media format. /users?$format=json
33
+ # $orderby Orders results. /users?$orderby=displayName desc
34
+ # $search Returns results based on search criteria. /me/messages?$search=pizza
35
+ # $select Filters properties (columns). /users?$select=givenName,surname
36
+ # $skip Indexes into a result set.
37
+ # Also used by some APIs to implement paging and can be used together with $top to manually page results. /me/messages?$skip=11
38
+ # $top Sets the page size of results. /users?$top=2
39
+ def parse_and_build_request_url(args)
40
+ next_link = args.delete(:next_link)
41
+ return next_link unless next_link.nil?
42
+
43
+ # organize params
44
+ base_uri = args.delete(:base_uri)
45
+ select_fields = args.delete(:select)
46
+ order_by = args.delete(:order)
47
+ args[:select] = select_fields.map(&:rails_camelize).join(",") if select_fields.is_a?(Array)
48
+ args[:orderby] = order_by if order_by
49
+
50
+ request_uri = ["/", Office365::API_VERSION, base_uri].join
51
+ request_uri += ["?", args.to_query].join if args.any?
52
+ request_uri
53
+ end
24
54
  end
25
55
  end
26
56
  end
@@ -10,12 +10,7 @@ module Office365
10
10
  # params: args => { next_link: (nil / next_page_url) }
11
11
  # response { results: [], next_link: '...' }
12
12
  def contacts(args = {})
13
- response = message_response(args: args.merge(base_uri: "/me/contacts"))
14
-
15
- {
16
- results: response["value"].map { |v| Models::Contact.new(v) },
17
- next_link: response["@odata.nextLink"]
18
- }
13
+ wrap_results(args.merge(kclass: Models::Contact, base_uri: "/me/contacts"))
19
14
  end
20
15
  end
21
16
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./concerns/base"
4
+
5
+ module Office365
6
+ module REST
7
+ module Event
8
+ include Concerns::Base
9
+
10
+ # params: args => { next_link: (nil / next_page_url) }
11
+ # response { results: [], next_link: '...' }
12
+ def events(args = {})
13
+ wrap_results(args.merge(kclass: Models::Event, base_uri: "/me/events"))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -10,12 +10,7 @@ module Office365
10
10
  # params: args => { next_link: (nil / next_page_url) }
11
11
  # response { results: [], next_link: '...' }
12
12
  def messages(args = {})
13
- response = message_response(args: args.merge(base_uri: "/me/messages"))
14
-
15
- {
16
- results: response["value"].map { |v| Models::Mailbox.new(v) },
17
- next_link: response["@odata.nextLink"]
18
- }
13
+ wrap_results(args.merge(kclass: Models::Mailbox, base_uri: "/me/messages"))
19
14
  end
20
15
  end
21
16
  end
@@ -45,8 +45,14 @@ module Office365
45
45
  resp_body = response.body
46
46
 
47
47
  return resp_body if response.status == 200
48
- raise InvalidAuthenticationTokenError, resp_body.dig("error", "message") if response.status == 401
48
+
49
49
  raise InvaliRequestError, resp_body["error_description"] if response.status == 400
50
+ raise InvalidAuthenticationTokenError, resp_body.dig("error", "message") if response.status == 401
51
+ raise AccessDeniedError, resp_body.dig("error", "message") if response.status == 403
52
+ raise NotFoundError, resp_body.dig("error", "message") if response.status == 404
53
+ raise ThrottlingError, resp_body.dig("error", "message") if response.status == 429
54
+
55
+ raise ServiceUnavailableError, resp_body.dig("error", "message") if response.status == 503
50
56
 
51
57
  raise Error, resp_body["error"]
52
58
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class String
4
- def o_underscore
4
+ def rails_underscore
5
5
  gsub(/::/, "/")
6
6
  .gsub(/@/, "")
7
7
  .gsub(/\./, "_")
@@ -10,4 +10,18 @@ class String
10
10
  .tr("-", "_")
11
11
  .downcase
12
12
  end
13
+
14
+ # rubocop:disable Style/SymbolProc
15
+ def rails_camelize(uppercase_first_letter: false)
16
+ string = self
17
+ return string if string !~ /_/ && string =~ /[A-Z]+.*/
18
+
19
+ string = if uppercase_first_letter
20
+ string.sub(/^[a-z\d]*/) { |match| match.capitalize }
21
+ else
22
+ string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
23
+ end
24
+ string.gsub(%r{(?:_|(/))([a-z\d]*)}) { "#{::Regexp.last_match(1)}#{::Regexp.last_match(2).capitalize}" }.gsub("/", "::")
25
+ end
26
+ # rubocop:enable Style/SymbolProc
13
27
  end
@@ -11,7 +11,7 @@ class Object
11
11
  # Converts an object into a string suitable for use as a URL query string,
12
12
  # using the given <tt>key</tt> as the param name.
13
13
  def to_query(key)
14
- "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
14
+ "$#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
15
15
  end
16
16
  end
17
17
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Office365
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.7"
5
5
  end
data/lib/office365.rb CHANGED
@@ -12,6 +12,12 @@ module Office365
12
12
  class InvalidAuthenticationTokenError < StandardError; end
13
13
  class InvaliRequestError < StandardError; end
14
14
 
15
+ # Handling expected errors -> https://learn.microsoft.com/en-us/graph/best-practices-concept#handling-expected-errors
16
+ class AccessDeniedError < StandardError; end
17
+ class NotFoundError < StandardError; end
18
+ class ThrottlingError < StandardError; end
19
+ class ServiceUnavailableError < StandardError; end
20
+
15
21
  API_HOST = "https://graph.microsoft.com"
16
22
  API_VERSION = "v1.0"
17
23
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-office365
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Encore Shao
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-27 00:00:00.000000000 Z
11
+ date: 2022-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -119,6 +119,7 @@ files:
119
119
  - lib/office365/models/contact.rb
120
120
  - lib/office365/models/directory.rb
121
121
  - lib/office365/models/email_address.rb
122
+ - lib/office365/models/event.rb
122
123
  - lib/office365/models/mailbox.rb
123
124
  - lib/office365/models/owner.rb
124
125
  - lib/office365/models/user.rb
@@ -128,6 +129,7 @@ files:
128
129
  - lib/office365/rest/client.rb
129
130
  - lib/office365/rest/concerns/base.rb
130
131
  - lib/office365/rest/contact.rb
132
+ - lib/office365/rest/event.rb
131
133
  - lib/office365/rest/mailbox.rb
132
134
  - lib/office365/rest/request.rb
133
135
  - lib/office365/rest/token.rb
@@ -161,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
161
163
  - !ruby/object:Gem::Version
162
164
  version: '0'
163
165
  requirements: []
164
- rubygems_version: 3.1.6
166
+ rubygems_version: 3.2.3
165
167
  signing_key:
166
168
  specification_version: 4
167
169
  summary: A simple ruby library to interact with Microsoft Graph and Office 365 API.