Skip to content

Instantly share code, notes, and snippets.

@NGMarmaduke
Created March 19, 2015 23:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save NGMarmaduke/a088943edbe4e703129d to your computer and use it in GitHub Desktop.
Save NGMarmaduke/a088943edbe4e703129d to your computer and use it in GitHub Desktop.
Office 365 API connection example in Ruby
class OfficeAPIError < StandardError; end;
class OfficeAPITimoutError < StandardError
def initialize(msg = "Office API timed out")
super(msg)
end
end
class OfficeAPI
attr_accessor :access_token, :messages_filter, :mail_box, :fetch_from
def initialize(mail_box)
@mail_box = mail_box
end
def get_root_path
"EWS/ODATA/users/#{@mail_box}"
end
def message_endpoint
end_point = "/folders/inbox/messages?" +
"$select=Id,Subject,BodyPreview,UniqueBody,HasAttachments,Sender,DateTimeReceived,DateTimeSent"
end_point += "&$filter=DateTimeReceived ge #{@fetch_from}" if @fetch_from
return end_point
end
def fetch_from_date(date)
@fetch_from = (date+1.second).strftime("%Y-%m-%dT%H:%M:%SZ")
end
def request_messages
begin
t1 = Time.now
res = api_connection.get do |req|
req.url "#{get_root_path}#{message_endpoint}"
req.headers = OfficeAPI.request_headers
end
t2 = Time.now
OfficeAPI.do_log "Polling request time: #{((t2 - t1) * 1000.0).to_i}ms"
return (JSON.parse res.body)['value']
rescue StandardError => e
OfficeAPI.log_exception(e)
OfficeAPI.do_log("Response: #{res.inspect}", 'E')
return []
end
end
def request_message(id)
filters = "?$select=Id,Subject,BodyPreview,Body,UniqueBody,HasAttachments,Sender,DateTimeReceived,DateTimeSent"
res = api_connection.get do |req|
req.url "#{get_root_path}/messages/#{id}#{filters}"
req.headers = OfficeAPI.request_headers
end
OfficeAPI.do_log "#{res.inspect}".red
return (JSON.parse res.body)
end
def mark_as_read(id)
res = api_connection.patch do |req|
req.url "#{get_root_path}/messages/#{id}"
req.headers = OfficeAPI.request_headers
req.body = '{ "IsRead": true }'
end
return (res.status == 200)
end
def request_attachment_details(id)
res = api_connection.get do |req|
req.url "#{get_root_path}/messages/#{id}/attachments"
req.headers = OfficeAPI.request_headers
end
return (JSON.parse res.body)['value']
end
def request_attachment_data(message_id, attachment_id)
res = api_connection.get do |req|
req.url "#{get_root_path}/messages/#{message_id}/attachments/#{attachment_id}"
req.headers = OfficeAPI.request_headers
end
return (JSON.parse res.body)['ContentBytes']
end
def api_connection
Faraday.new(:url => 'https://outlook.office365.com') do |faraday|
faraday.adapter :net_http # make requests with Net::HTTP
faraday.options[:timeout] = 10
end
end
class << self
def cert_path
File.join(Rails.root, "certs", "office_365")
end
def client_key
pk = File.join(cert_path, "pk.pem")
return OpenSSL::PKey::RSA.new(File.read(pk))
end
def request_headers
{
:user_agent => 'spreadyO365Poller/1.0',
'client-request-id' => SecureRandom.uuid,
'Date' => Time.now.httpdate,
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{get_jwt}"
}
end
def get_jwt
request_token unless access_token_valid?
@access_token['access_token']
end
def access_token_valid?
return (@access_token.present? and DateTime.now < DateTime.strptime(@access_token['expires_on'], '%s') - 1.minute)
end
def request_token
login = Faraday.new(:url => 'https://login.windows.net') do |faraday|
faraday.request :url_encoded # form-encode POST param
faraday.adapter :net_http # make requests with Net::HTTP
end
self.do_log "Reqesting OAuth Token"
response = login.post '/TENANT_ID/oauth2/token?api-version=1.0', {
:grant_type => 'client_credentials',
:redirect_uri => 'http://spready.dev',
:resource => 'https://outlook.office365.com/',
:client_id => 'YOUR CLIENT ID',
:client_assertion_type => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
:client_assertion => generate_jwt
}
@access_token = (JSON.parse response.body)
self.do_log "Recieved Token".green
self.do_log @access_token.inspect
end
def generate_jwt
header = {
'alg' => 'RS256',
'x5t' => 'CERT_THUMBPRINT'
}.to_json
payload = {
'aud' => 'https://login.windows.net/TENANT_ID/oauth2/token',
'nbf' => Time.now.to_i,
'exp' => (Time.now + 15.minutes).to_i,
'jti' => SecureRandom.uuid,
'sub' => 'YOUR CLIENT ID',
'iss' => 'YOUR CLIENT ID',
}.to_json
base64_token = "#{Base64.strict_encode64(header)}.#{Base64.strict_encode64(payload)}"
digest = OpenSSL::Digest::SHA256.new
signature = client_key.sign digest, base64_token
base64_signature = Base64.strict_encode64(signature)
@request_token = "#{base64_token}.#{base64_signature}"
end
def logger=(logger)
@logger = logger
end
def logger
@logger || Rails.logger
end
def log_exception(e)
do_log("Exception occured while retrieving messages:".red, "E")
do_log([e, *e.backtrace].join("\n"), "E")
if e.class == Net::ReadTimeout || e.class == Faraday::Error::TimeoutError
airbrakeException = OfficeAPITimoutError.new()
else
airbrakeException = OfficeAPIError.new()
end
airbrakeException.set_backtrace e.backtrace
Airbrake.notify(airbrakeException)
end
def do_log(message, flag='I')
logger.info "#{flag}: #{DateTime.now}" + " #{message}"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment