API INTEGRATIONS

(FOR THE EXCESSIVELY PARANOID) 

@ALXJRVS

API Integrations

MAKE ME
SUPER PARANOID 

Your Code

Your Code

API INTEGRATIONS IN PLAIN RUBY 

token = "Slack Token"
message_query = {
  query: {
    token: token,
    message: "message",
    channel: "channel ID"
  }
}
message_response = HTTParty.get("https://slack.com/api/chat.postMessage")

list_query = {
  query: {
    token: token
  }
}
list_response = HTTParty.get("https://slack.com/api/channel.list")

1. Get relevant Data from our app

token = "Slack Token"
message_query = {
  query: {
    token: token,
    message: "message",
    channel: "channel ID"
  }
}
message_response = HTTParty.get("https://slack.com/api/chat.postMessage")

list_query = {
  query: {
    token: token
  }
}
list_response = HTTParty.get("https://slack.com/api/channel.list")

2. Arrange that data into API-friendly parameters

token = "Slack Token"
message_query = {
  query: {
    token: token,
    message: "message",
    channel: "channel ID"
  }
}
message_response = HTTParty.get("https://slack.com/api/chat.postMessage")

list_query = {
  query: {
    token: token
  }
}
list_response = HTTParty.get("https://slack.com/api/channel.list")

3. Execute the HTTP CALL

token = "Slack Token"
message_query = {
  query: {
    token: token,
    message: "message",
    channel: "channel ID"
  }
}
message_response = HTTParty.get("https://slack.com/api/chat.postMessage")

list_query = {
  query: {
    token: token
  }
}
list_response = HTTParty.get("https://slack.com/api/channel.list")

4. USE THE RESULTS IN OUR code 

token = "Slack Token"
message_query = {
  query: {
    token: token,
    message: "message",
    channel: "channel ID"
  }
}
message_response = HTTParty.get("https://slack.com/api/chat.postMessage")

list_query = {
  query: {
    token: token
  }
}
list_response = HTTParty.get("https://slack.com/api/channel.list")

Service Objects! 

class SlackService
  BASE = 'https://slack.com/api'

  def initialize(token)
    @token
  end

  def send_message(message:, to:)
    query = {
      query: {
        token: @token,
        message: message,
        channel: to
      }
    }
    HTTParty.get(BASE + "/chat.postMessage")
  end

  def channels_list
    query = {
      query: {
        token: @token
      }
    }
    HTTParty.get(BASE + "/channels.list")
  end
end

TEsting?

Stub!

Mock!

VCR! 

Service Object

VCR Cassette

YOUR CODE

ANALYTICS

API

PAYMENTS

API

SOCIAL

API

DATA

API

YOUR CODE

DISORDER

ANARCHY

CHAOS

PAIN

YOUR CODE

their API

 

Airlocks

Make

Great
API 
Integrations

pattern

A Service
A wrapper 
a fake

A Service

class Slack
  @@api = SlackApi
  cattr_accessor :api

  def initialize(token)
    @token = token
  end

  def send(message:, to:)
    query = {
      query: {
        token: token,
        channel: to,
        message: message
      }
    }
    api.chat_post_message query
  end

  def list_channels
    query = {
      query: {
        token: token
      }
    }
    api.channels_list query
  end

  private
  attr_reader :token
end

Service: Interface to the API

@@api = SlackApi
cattr_accessor :api

A WRAPPER

class SlackApi
  include HTTParty
  base_uri 'https://slack.com/api'

  CHANNEL_LIST      = "/channels.list"
  CHAT_POST_MESSAGE = "/chat.postMessage"

  def self.chat_post_message(data)
    get CHAT_POST_MESSAGE, data
  end

  def self.channels_list(data)
    get CHANNEL_LIST, data
  end
end

Wrapper: Thin wrapper over HTTP calls

A FAKE

class SlackApiFake
  DEFAULT_FAIL = {"ok"=>false, "error"=>"not_authed"}
  @@fail = false

  def self.chat_post_message(data)
    if @@fail
      DEFAULT_FAIL
    else
      {...}
    end
  end

  def self.channels_list(data)
    if @@fail
      DEFAULT_FAIL
    else
      {
        "ok" => true,
        "channels" => [
          {...}
          }
        ]
      }
    end
  end

  def self.fail!
    @@fail = true
  end

  def self.reset!
    @@fail = false
  end
end

Fake: a data formatter 

Testing!

UNit test the Service
VCR the wrapper 
Maintain the fake

UNIT TEST THE Service

@@api = SlackApi
cattr_accessor :api
Slack.api = SlackFake

VCR THE WRAPPER

Maintain the fake

 !=

YOUR CODE

conclusions

integrations should contain as little domain truth as possible

API's are just other people's code

Made with Slides.com