Skip to content

Instantly share code, notes, and snippets.

@NGMarmaduke
Created March 19, 2015 23:43
Show Gist options
  • Select an option

  • Save NGMarmaduke/a088943edbe4e703129d to your computer and use it in GitHub Desktop.

Select an option

Save NGMarmaduke/a088943edbe4e703129d to your computer and use it in GitHub Desktop.

Revisions

  1. NGMarmaduke created this gist Mar 19, 2015.
    194 changes: 194 additions & 0 deletions office_api.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,194 @@
    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