Шаблон проектирования для использования результатов различных API - PullRequest
0 голосов
/ 04 ноября 2018

У меня есть два API с разными ресурсами:

  • www.api-A.com**/consumers * * 1006

    , который возвращает: {consumers: ['mike', 'Anna', 'Danilo']}

  • www.api-B.com**/clients

    , который возвращает: {clients: ['Jack', 'Bruce', 'Mary']}

Я хотел бы использовать эти два результата в одном контроллере. Я хочу относиться к ним как к одному.

Должен ли я создавать оболочку для каждого API, например:

module ApiAWrapper
  #code here
end

module ApiBWrapper
  #code here
end

и вызывать следующее в моем контроллере?

MyController
  def index
    @clients << ApiAWrapper.most_recent
    @clients << ApiBWrapper.most_recent
    @clients
  end
end

При этом @clients будет:

['mike', 'Anna', 'Danilo', 'Jack', 'Bruce', 'Mary']

Это правильный способ использовать эти разные API с одинаковыми ответами? Есть ли шаблон дизайна, который я могу использовать, или мне следует прочесть руководство?

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Когда мне нужны внешние сервисы для общего ответа, я использую парсер. В других языках вы могли бы использовать интерфейсы для принудительного выполнения контракта подписи метода, , но Ruby не имеет этой функции из-за утки, набирающей .

Этот парсер может быть функцией или модулем. Например:

module GitHub
  class Service
    BASE_URI = 'https://api.github.com'

    def self.fetch
      response = HTTP.get("#{BASE_URI}/clients")
      raise GitHub::ApiError unless response.ok?
      Parser.new(response).to_common
    end
  end

  class Parser
    def initialize(response)
      @response = response
    end

    def to_common
      json_response = JSON.parse(@response)
      json_response[:customers] = json_response.delete :clients
      # more rules
      # ...
      json_response
    end
  end
end

Хорошо, вот и все. Теперь у вас есть служба для извлечения и обработки части HTTP и анализатор, который обрабатывает тело ответа из HTTP-запроса. Теперь предположим, что вы хотите использовать другой API, например BitBucket API:

module BitBucket
  class Service
    BASE_URI = 'https://bitbucket.com/api'

    def self.fetch
      response = HTTP.get("#{BASE_URI}/customers")
      raise BitBucket::ApiError unless response.ok?
      Parser.new(response).to_common
    end
  end

  class Parser
    def initialize(response)
      @response = response
    end

    def to_common
      json_response = JSON.parse(@response)
      json_response[:clients] = (json_response.delete(:data).delete(:clients))
      # more rules
      # ...
      json_response
    end
  end
end

Таким образом, обе службы будут возвращаться с использованием одного и того же интерфейса. Чтобы присоединиться к результатам, вы можете сделать:

data = [GitHub::Service.fetch, BitBucket::Service.fetch, ...]
names = data.map { |customer_list| customer_list[:name] }
names.uniq
0 голосов
/ 04 ноября 2018

В любом случае у вас должны быть обертки для вызовов API, потому что контроллер должен иметь как можно меньше логики. В любом случае, я бы создал класс Client с методом десериализации массива jsons клиента в массив клиентов. Таким образом, в обеих оболочках вы вызываете этот метод и возвращаете массив клиентов, готовых к конкататуре в контроллере.

Что-то вроде:

class Client
  attr_accessor :name

  def initialize(client_json)
    @name = client_json['name']
  end

  def self.deserialize_clients(clients_json)
    clients_json.map{ |c| Client.new(c) }
  end
end

Тогда для упаковщиков:

module ApiAWrapper
   def self.most_recent
     response = #api call here
     Client.deserialize_clients(JSON.parse(response.body))
   end
end

Что ты думаешь?

...