Diseño de APIs con Ruby

Post on 10-May-2015

856 views 5 download

Tags:

Transcript of Diseño de APIs con Ruby

Diseño de APIs con Ruby

Edwin Cruz @softr8

#SGRuby

Friday, June 15, 2012

PLAN

•Que es una API

• Como implementar una buena API

• Usando Ruby on Rails para implementar una API

• Patrones de diseño

Friday, June 15, 2012

ApplicationProgrammingInterface

Friday, June 15, 2012

API

Es una manera para comunicar dos aplicaciones entre ellas.

Friday, June 15, 2012

TIPOS DE API

• Library

• SDK

•Web services

Friday, June 15, 2012

PRIMERAS API - SOAP

SimpleObjectAccessProtocol

Friday, June 15, 2012

REST

REpresentationStateTransfer

Friday, June 15, 2012

CRUD

CreateReadUpdateDelete

AltasBajasCambiosConsultas

Friday, June 15, 2012

REST ⋍ CRUD

Friday, June 15, 2012

Recursos a través de URIUso de verbos HTTP

REST ⋍ CRUD

Friday, June 15, 2012

REST-ISH + JSON

Friday, June 15, 2012

REST-ISH + JSON

=

Friday, June 15, 2012

REST-ISH + JSON

=Cosas Increibles

Friday, June 15, 2012

RECURSO

Friday, June 15, 2012

RECURSO

Cualquier cosa expuesta mediante web

Friday, June 15, 2012

RECURSO

Cualquier cosa expuesta mediante webTienen una representación en datos

Friday, June 15, 2012

RECURSO

Cualquier cosa expuesta mediante webTienen una representación en datosCon un servicio web intercambiamos representaciones de recursos

Friday, June 15, 2012

REQUISITOS REST

Friday, June 15, 2012

REQUISITOS REST

Separación de responsabilidades

Friday, June 15, 2012

REQUISITOS REST

Separación de responsabilidades Cliente/Servidor

Friday, June 15, 2012

REQUISITOS REST

Separación de responsabilidades Cliente/ServidorSin estado

Friday, June 15, 2012

REQUISITOS REST

Separación de responsabilidades Cliente/ServidorSin estado“Cacheable”

Friday, June 15, 2012

REQUISITOS REST

Separación de responsabilidades Cliente/ServidorSin estado“Cacheable”Sistema a capas

Friday, June 15, 2012

REQUISITOS REST

Separación de responsabilidades Cliente/ServidorSin estado“Cacheable”Sistema a capasInterface uniforme

Friday, June 15, 2012

¿PORQUÉ UN API?

Friday, June 15, 2012

¿PORQUÉ UN API?

Aumenta la flexibilidad de un servicio

Friday, June 15, 2012

¿PORQUÉ UN API?

Aumenta la flexibilidad de un servicioAumenta la utilidad de un servicio

Friday, June 15, 2012

¿PORQUÉ UN API?

Aumenta la flexibilidad de un servicioAumenta la utilidad de un servicio

Libera los datos de usuario

Friday, June 15, 2012

¿PORQUÉ UN API?

Aumenta la flexibilidad de un servicioAumenta la utilidad de un servicio

Libera los datos de usuarioProporciona valor de negocio

Friday, June 15, 2012

¿QUÉ CARACTERÍSTICAS?

Friday, June 15, 2012

¿QUÉ CARACTERÍSTICAS?

Fácil de implementar y mantener

Friday, June 15, 2012

¿QUÉ CARACTERÍSTICAS?

Fácil de implementar y mantenerBuen rendimiento

Friday, June 15, 2012

¿QUÉ CARACTERÍSTICAS?

Fácil de implementar y mantenerBuen rendimientoEscalable

Friday, June 15, 2012

¿QUÉ CARACTERÍSTICAS?

Fácil de implementar y mantenerBuen rendimientoEscalableFácil de entender y usar

Friday, June 15, 2012

¿CUÁLES SON LOS RETOS?

Friday, June 15, 2012

¿CUÁLES SON LOS RETOS?

La red es un eslabón débil

Friday, June 15, 2012

¿CUÁLES SON LOS RETOS?

La red es un eslabón débilAPI incompleta pone estrés en el cliente

Friday, June 15, 2012

¿CUÁLES SON LOS RETOS?

La red es un eslabón débilAPI incompleta pone estrés en el clienteAdministrar Cambios

Friday, June 15, 2012

DISEÑAR UNA BUENA API

Friday, June 15, 2012

CONVENCIONES REST

Friday, June 15, 2012

GET /api/products #Listado

REST ⋍ CRUD

{"products"  :      [        {"product"  :  {  "id"  :  1,  "name"  :  "Producto  1",  "status"  :  "archived"}  },        {"product"  :  {  "id"  :  2,  "name"  :  "Producto  2",  "status"  :  "active"    }  }    ]}

Friday, June 15, 2012

REST ⋍ CRUD

GET /api/products/2 #Ver

{"product"  :  {  "id"  :  2,  "name"  :  "Producto  2",  "status"  :  "active"    }  }

Friday, June 15, 2012

REST ⋍ CRUD

POST /api/products #Crear

{    "name"  :  "Producto  2"}

Friday, June 15, 2012

REST ⋍ CRUD

PUT /api/products/2 #Actualizar

{    "name"  :  "Producto  dos"}

Friday, June 15, 2012

REST ⋍ CRUD

DELETE /api/products/2 #Eliminar

Friday, June 15, 2012

VERSIONANDO TU API

• API Interna (entre aplicaciones)

• API Externa (aplicacion movil ? )

• API Usuarios (publico en general)

Friday, June 15, 2012

VERSIONANDO TU API

/int/api/products/ext/api/products/pub/api/products

Friday, June 15, 2012

VERSIONANDO TU API

/int/api/products?version=2/ext/api/products?version=2/pub/api/products?version=2

Friday, June 15, 2012

VERSIONANDO TU API

/v2/int/api/products/v2/ext/api/products/v2/pub/api/products

Friday, June 15, 2012

MUNDO IDEAL

Friday, June 15, 2012

MUNDO IDEAL

Uso de: Accept Header

Friday, June 15, 2012

MUNDO IDEAL

Uso de: Accept Header

Accept: application/vnd.mycompany.com;version=2,application/json

Friday, June 15, 2012

CODIGOS HTTP200 OK201 Created202 Accepted

400 Bad Request401 Unauthorized402 Payment Required404 Not Found409 Conflict422 Unprocessable Entity

500 Internal Server Error503 Service Unavailable

Friday, June 15, 2012

CODIGOS HTTP

HTTP/1.1 401 Unauthorized

{ “errors”: [ “api_key not found” ]}

Friday, June 15, 2012

CODIGOS HTTPHTTP/1.1 401 Unauthorized

{ “errors”: [ “api_key no valida”, “api_key no puede contener espacios” ]}

Friday, June 15, 2012

CODIGOS HTTP

HTTP/1.1 401 Unauthorized

{ “errors”: [ “api_key no encontrada, por favor visita http://account.myapp.com/api para obtenerla” ]}

Friday, June 15, 2012

CODIGOS HTTP

HTTP/1.1 400 Bad Request

{ “errors”: [ “Estructura JSON no valida”, “unexpected TSTRING, expected ‘}’ at line 2” ]}

Friday, June 15, 2012

DOCUMENTACIONHTTP/1.1 422 Unprocessable Entity

{ “errors”: [ “vendor_code: no puede estar vacio” ], “documentacion”: [ “vendor_code”: [ “descripcion” : “Codigo asignado por proveedor”, “formato” : “Combinacion de tres letras seguidas de 4 numeros”, “ejemplo” : “SOL1234” ] ]}

Friday, June 15, 2012

CODIGOS HTTP

HTTP/1.1 503 Service Unavailable

{ “messages”: [ “En mantenimiento” ]}

Friday, June 15, 2012

OPCIONES AVANZADAS

• Simuladores

• Autenticación

• Validadores

• Limite de uso

• Rapidez

• Balanceo

Friday, June 15, 2012

USANDO RUBY ON RAILS PARA

IMLEMENTAR UNA API

Friday, June 15, 2012

APIS ON RAILS - REST

products GET /products(.:format) products#index POST /products(.:format) products#create new_product GET /products/new(.:format) products#newedit_product GET /products/:id/edit(.:format) products#edit product GET /products/:id(.:format) products#show PUT /products/:id(.:format) products#update DELETE /products/:id(.:format) products#destroy

MyApp::Application.routes.draw do resources :productsend

Friday, June 15, 2012

APIS ON RAILS - REST

products GET /api/products(.:format) products#index POST /api/products(.:format) products#create new_product GET /api/products/new(.:format) products#newedit_product GET /api/products/:id/edit(.:format) products#edit product GET /api/products/:id(.:format) products#show PUT /api/products/:id(.:format) products#update DELETE /api/products/:id(.:format) products#destroy

MyApp::Application.routes.draw do scope ‘/api’ do resources :products endend

Friday, June 15, 2012

APIS ON RAILS - VERSIONES

v2_products GET /v2/products(.:format) V1/products#index POST /v2/products(.:format) V1/products#create new_v2_product GET /v2/products/new(.:format) V1/products#newedit_v2_product GET /v2/products/:id/edit(.:format) V1/products#edit v2_product GET /v2/products/:id(.:format) V1/products#show PUT /v2/products/:id(.:format) V1/products#update DELETE /v2/products/:id(.:format) V1/products#destroy

gem 'versionist'MyApp::Application.routes.draw do api_version(:module => 'V1', :path => 'v2') do resources :products endend

Friday, June 15, 2012

APIS ON RAILS, CONTROLADOR

class V2::ProductsController < ApplicationController respond_to :json def index @products = V2::Product.paginate(:page => (params[:page] || 1), :per_page => (params[:per_page] || 100)).all respond_with @products end def show @product = V2::Product.find(params[:id]) respond_with @product end def update @product = V2::Product.find(params[:id]) @product.update_attributes(params[:product]) respond_with @product end def destroy @product = V2::Product.find(params[:id]) respond_with @product.destroy endend

Friday, June 15, 2012

APIS ON RAILS - MODELO

class V2::Product < Product

JSON_ATTRIBUTES = { properties: [ :id, :upc, :sku, :list_cost, :color, :dimension, :size, :created_at, :updated_at, ], methods: [ :units_on_hand ] }

end

Friday, June 15, 2012

APIS ON RAILS, CONTROLADOR

gem ‘rabl’class V3::ProductsController < ApplicationController respond_to :json, :xml def index @products = V3::Product.paginate(:page => (params[:page] || 1), :per_page => (params[:per_page] || 100)).all end def show @product = V3::Product.find(params[:id]) end def update @product = V3::Product.find(params[:id]) @product.update_attributes(params[:product]) end def destroy @product = V3::Product.find(params[:id]) render json: {}, status: @product.destroy ? :ok : :unprocessable_entity endend

Friday, June 15, 2012

APIS ON RAILSVISTAS

#app/views/api/v3/products/index.rabl

collection @productsattributes :id, :name, :statusnode(:url) {|product| product_url(product) }node(:current_stock) {|product| product.variants.map(&:on_hand).sum }child :variants do attributes :upc, :color, :size, :on_handend

{"products"  :      [        {"product"  :  {  "id"  :  1,  "name"  :  "Producto  1",  "status"  :  "archived",  “current_stock”  :  10,            “variants”  :  [  {“upc”  :  “ASDFS”,  “color”  :  “negro”,  “size”  :  “M”,  “on_hand”  :  10}  ]            }        }    ]}

Friday, June 15, 2012

APIS ON RAILSVISTAS

gem ‘jbuilder’Jbuilder.encode do |json| json.content format_content(@message.content) json.(@message, :created_at, :updated_at)

json.author do |json| json.name @message.creator.name.familiar json.email_address @message.creator.email_address_with_name json.url url_for(@message.creator, format: :json) end

if current_user.admin? json.visitors calculate_visitors(@message) end

json.comments @message.comments, :content, :created_at

end

Friday, June 15, 2012

APIS ON RAILSVISTAS

gem ‘active_model_serializer’class PostSerializer < ActiveModel::Serializer attributes :id, :body attribute :title, :key => :name

has_many :comments

def tags tags.order :name endend

Friday, June 15, 2012

APIS ON RAILSSEGURIDAD

DeviseAutenticacion Flexible para aplicaciones RailsCompuesta de 12 modulos: database authenticable, token authenticable, omniauthable, confirmable, recoverable, registerable, trackable, timeoutable, validatable, lockable

Friday, June 15, 2012

APIS ON RAILSSEGURIDAD

DeviseAutenticacion Flexible para aplicaciones RailsCompuesta de 12 modulos: database authenticable, token authenticable, omniauthable, confirmable, recoverable, registerable, trackable, timeoutable, validatable, lockable

Friday, June 15, 2012

APIS ON RAILSSEGURIDAD

gem ‘rabl’class V3::ProductsController < ApplicationController before_filter :authenticate_user! respond_to :json, :xml def index @products = V3::Product.paginate(:page => (params[:page] || 1), :per_page => (params[:per_page] || 100)).all end def show @product = V3::Product.find(params[:id]) end def update @product = V3::Product.find(params[:id]) @product.update_attributes(params[:product]) end def destroy @product = V3::Product.find(params[:id]) render json: {}, status: @product.destroy ? :ok : :unprocessable_entity endend

Friday, June 15, 2012

APIS ON RAILSPRUEBAS

describe V3::ProductsController do

before do @request.env["HTTP_ACCEPT"] = "application/json" end

describe "#index" do context "cuando no se pasa ningun atributo" do it "regresa los registros en paginas" do get :index response.should be_success data = JSON.parse(response.body) Product.count.should > 0 data['products'].length.should == Product.count end end endend

Friday, June 15, 2012

APIS ON RAILSPRUEBAS

describe V3::ProductsController do

before do @request.env["HTTP_ACCEPT"] = "application/json" end

describe "#show" do context "pasando un id inexistente" do it "responde con http 404 y un mensaje de error" do get :show, id: -1 response.code.should == "404" JSON.parse(response.body)['error'].should == "ActiveRecord::RecordNotFound" JSON.parse(response.body)['message'].should == "Couldn't find Product with id=-1" end end endend

Friday, June 15, 2012

APIS ON RAILSPRUEBAS

describe V3::ProductsController do

before do @request.env["HTTP_ACCEPT"] = "application/json" end

describe "#create" do context "con malos atributos" do it "responde con un error" do post :create, product: {bad_key: "foo"} response.code.should == "400" json_response = JSON.parse(response.body) json_response['error'].should == "ActiveRecord::UnknownAttributeError" json_response['message'].should == "unknown attribute: bad_key" end end endend

Friday, June 15, 2012

APIS ON RAILSPRUEBAS

describe V3::ProductsController do

before do @request.env["HTTP_ACCEPT"] = "application/json" end

describe "#create" do context "con atributos correctos" do it "responde correctamente y crea el producto" do expect { post :create, product: {name: "productito"} }.to change(Product, :count).by(1) end end endend

Friday, June 15, 2012

RAILS A DIETA

Friday, June 15, 2012

RAILS A DIETA

Rails es modular

Friday, June 15, 2012

RAILS A DIETA

Rails es modularPara crear APIs, algunos Middlewares no son

necesarios

Friday, June 15, 2012

RAILS A DIETA

Rails es modularPara crear APIs, algunos Middlewares no son

necesariosrails-api

Friday, June 15, 2012

<Module:0x007ff271221e40>, ActionDispatch::Routing::Helpers, #<Module:0x007ff2714ad268>, ActionController::Base, ActionDispatch::Routing::RouteSet::MountedHelpers, HasScope, ActionController::Compatibility, ActionController::ParamsWrapper, ActionController::Instrumentation, ActionController::Rescue, ActiveSupport::Rescuable, ActionController::HttpAuthentication::Token::ControllerMethods, ActionController::HttpAuthentication::Digest::ControllerMethods, ActionController::HttpAuthentication::Basic::ControllerMethods, ActionController::RecordIdentifier, ActionController::DataStreaming, ActionController::Streaming, ActionController::ForceSSL, ActionController::RequestForgeryProtection, AbstractController::Callbacks, ActiveSupport::Callbacks, ActionController::Flash, ActionController::Cookies, ActionController::MimeResponds, ActionController::ImplicitRender, ActionController::Caching, ActionController::Caching::Fragments, ActionController::Caching::ConfigMethods, ActionController::Caching::Pages, ActionController::Caching::Actions, ActionController::ConditionalGet, ActionController::Head, ActionController::Renderers::All, ActionController::Renderers, ActionController::Rendering, ActionController::Redirecting, ActionController::RackDelegation, ActiveSupport::Benchmarkable, AbstractController::Logger, ActionController::UrlFor, AbstractController::UrlFor, ActionDispatch::Routing::UrlFor, ActionDispatch::Routing::PolymorphicRoutes, ActionController::HideActions, ActionController::Helpers, AbstractController::Helpers, AbstractController::AssetPaths, AbstractController::Translation, AbstractController::Layouts, AbstractController::Rendering, AbstractController::ViewPaths, ActionController::Metal, AbstractController::Base, ActiveSupport::Configurable, Object, ActiveSupport::Dependencies::Loadable, Mongoid::Extensions::Object::Yoda, Mongoid::Extensions::Object::Substitutable, Mongoid::Extensions::Object::Reflections, Mongoid::Extensions::Object::DeepCopy, Mongoid::Extensions::Object::Checks, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject

Friday, June 15, 2012

<Module:0x007ff271221e40>, ActionDispatch::Routing::Helpers, #<Module:0x007ff2714ad268>, ActionController::Base, ActionDispatch::Routing::RouteSet::MountedHelpers, HasScope, ActionController::Compatibility, ActionController::ParamsWrapper, ActionController::Instrumentation, ActionController::Rescue, ActiveSupport::Rescuable, ActionController::HttpAuthentication::Token::ControllerMethods, ActionController::HttpAuthentication::Digest::ControllerMethods, ActionController::HttpAuthentication::Basic::ControllerMethods, ActionController::RecordIdentifier, ActionController::DataStreaming, ActionController::Streaming, ActionController::ForceSSL, ActionController::RequestForgeryProtection, AbstractController::Callbacks, ActiveSupport::Callbacks, ActionController::Flash, ActionController::Cookies, ActionController::MimeResponds, ActionController::ImplicitRender, ActionController::Caching, ActionController::Caching::Fragments, ActionController::Caching::ConfigMethods, ActionController::Caching::Pages, ActionController::Caching::Actions, ActionController::ConditionalGet, ActionController::Head, ActionController::Renderers::All, ActionController::Renderers, ActionController::Rendering, ActionController::Redirecting, ActionController::RackDelegation, ActiveSupport::Benchmarkable, AbstractController::Logger, ActionController::UrlFor, AbstractController::UrlFor, ActionDispatch::Routing::UrlFor, ActionDispatch::Routing::PolymorphicRoutes, ActionController::HideActions, ActionController::Helpers, AbstractController::Helpers, AbstractController::AssetPaths, AbstractController::Translation, AbstractController::Layouts, AbstractController::Rendering, AbstractController::ViewPaths, ActionController::Metal, AbstractController::Base, ActiveSupport::Configurable, Object, ActiveSupport::Dependencies::Loadable, Mongoid::Extensions::Object::Yoda, Mongoid::Extensions::Object::Substitutable, Mongoid::Extensions::Object::Reflections, Mongoid::Extensions::Object::DeepCopy, Mongoid::Extensions::Object::Checks, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject

#<Module:0x007f9211d5cd70>, ActionDispatch::Routing::Helpers, #<Module:0x007f9211f7b5e8>, ActionController::API, ActiveRecord::Railties::ControllerRuntime, ActionDispatch::Routing::RouteSet::MountedHelpers, ActionController::Instrumentation, ActionController::Rescue, ActiveSupport::Rescuable, ActionController::DataStreaming, ActionController::ForceSSL, AbstractController::Callbacks, ActiveSupport::Callbacks, ActionController::ConditionalGet, ActionController::Head, ActionController::Renderers::All, ActionController::Renderers, ActionController::Rendering, AbstractController::Rendering, AbstractController::ViewPaths, ActionController::Redirecting, ActionController::RackDelegation, ActiveSupport::Benchmarkable, AbstractController::Logger, ActionController::UrlFor, AbstractController::UrlFor, ActionDispatch::Routing::UrlFor, ActionDispatch::Routing::PolymorphicRoutes, ActionController::HideActions, ActionController::Metal, AbstractController::Base, ActiveSupport::Configurable, Object, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Dependencies::Loadable, PP::ObjectMixin, Kernel, BasicObject

Friday, June 15, 2012

use ActionDispatch::Staticuse Rack::Lockuse #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007fd3b32928c0>use Rack::Runtimeuse Rack::MethodOverrideuse ActionDispatch::RequestIduse Rails::Rack::Loggeruse ActionDispatch::ShowExceptionsuse ActionDispatch::DebugExceptionsuse ActionDispatch::RemoteIpuse ActionDispatch::Reloaderuse ActionDispatch::Callbacksuse ActionDispatch::Cookiesuse ActionDispatch::Session::CookieStoreuse ActionDispatch::Flashuse ActionDispatch::ParamsParseruse ActionDispatch::Headuse Rack::ConditionalGetuse Rack::ETaguse ActionDispatch::BestStandardsSupportuse Rack::Mongoid::Middleware::IdentityMap

Friday, June 15, 2012

use ActionDispatch::Staticuse Rack::Lockuse #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007fd3b32928c0>use Rack::Runtimeuse Rack::MethodOverrideuse ActionDispatch::RequestIduse Rails::Rack::Loggeruse ActionDispatch::ShowExceptionsuse ActionDispatch::DebugExceptionsuse ActionDispatch::RemoteIpuse ActionDispatch::Reloaderuse ActionDispatch::Callbacksuse ActionDispatch::Cookiesuse ActionDispatch::Session::CookieStoreuse ActionDispatch::Flashuse ActionDispatch::ParamsParseruse ActionDispatch::Headuse Rack::ConditionalGetuse Rack::ETaguse ActionDispatch::BestStandardsSupportuse Rack::Mongoid::Middleware::IdentityMap

use ActionDispatch::Staticuse Rack::Lockuse #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007fe74448cf50>use Rack::Runtimeuse ActionDispatch::RequestIduse Rails::Rack::Loggeruse ActionDispatch::ShowExceptionsuse ActionDispatch::DebugExceptionsuse ActionDispatch::RemoteIpuse ActionDispatch::Reloaderuse ActionDispatch::Callbacksuse ActiveRecord::ConnectionAdapters::ConnectionManagementuse ActiveRecord::QueryCacheuse ActionDispatch::ParamsParseruse ActionDispatch::Headuse Rack::ConditionalGetuse Rack::ETag

Friday, June 15, 2012

Gracias!

Preguntas?

Edwin Cruzedwin@crowdint.com

@softr8

Friday, June 15, 2012