ruby-office365 0.1.6 → 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: b25c6ebfd49566d52252acf88d545334c3836df70727334f6fe7dc420b3ec2d2
4
- data.tar.gz: f7513d00478b3db8206dade494d02d401ebf998b9e22b2707c19a0ee8d017b12
3
+ metadata.gz: '0490fef4bbf78efa42231c69e2b12fdb9f12ec37805c5e846225568e85cb03ba'
4
+ data.tar.gz: a40082526d63f968875194b969146f787de82090207132c69b7d62c344719c6f
5
5
  SHA512:
6
- metadata.gz: 84ebf0fc3fb83ea4a529a38880ea4dc10c932dbb7c0b6a57804d0e53b337dba1ff7669981af84c5b04cfff519a1e9062199c5d9f70cdd506906460b3b1cd7ca0
7
- data.tar.gz: 7af1f230a6ba4e5f76a23f1cd28ad0399efd64ff1c91a1da9c05c2c903806b070dbc860a82945c527b8c5efd581e0c611a6db530424c3a9e5fb6b0ceffd05cf0
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
@@ -34,4 +34,10 @@
34
34
 
35
35
  - Integrate REST API to get events
36
36
  - get events `client.events`
37
- - get events data with next link `client.events({next_link: 'xxx'})`
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.6)
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 (2022)
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,6 +11,11 @@ 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`.
@@ -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
@@ -3,6 +3,7 @@
3
3
  module Office365
4
4
  module Models
5
5
  class Event < Base
6
+ # attr_accessor :id, :subject, :body, :start, :end, :location, :attendees, :organizer
6
7
  end
7
8
  end
8
9
  end
@@ -4,8 +4,8 @@ require_relative "./user"
4
4
  require_relative "./mailbox"
5
5
  require_relative "./calendar"
6
6
  require_relative "./contact"
7
- require_relative "./token"
8
7
  require_relative "./event"
8
+ require_relative "./token"
9
9
 
10
10
  module Office365
11
11
  module REST
@@ -13,9 +13,9 @@ module Office365
13
13
  include Office365::REST::User
14
14
  include Office365::REST::Mailbox
15
15
  include Office365::REST::Calendar
16
+ include Office365::REST::Event
16
17
  include Office365::REST::Contact
17
18
  include Office365::REST::Token
18
- include Office365::REST::Event
19
19
  end
20
20
  end
21
21
  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
@@ -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 events(args = {})
13
- response = message_response(args: args.merge(base_uri: "/me/events"))
14
-
15
- {
16
- results: response["value"].map { |v| Models::Event.new(v) },
17
- next_link: response["@odata.nextLink"]
18
- }
13
+ wrap_results(args.merge(kclass: Models::Event, base_uri: "/me/events"))
19
14
  end
20
15
  end
21
16
  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.6"
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.6
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-11-01 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