如何获取Enum.reduce直到列表中的空Elixir

时间:2019-03-07 07:54:23

标签: elixir

我正在尝试从对讲机中获取所有公司列表。使用per_page时一次给出的数量不会超过60。还有另一个端点 https://api.intercom.io/companies/scroll,这使scroll_param可以吸引其他公司,依此类推。

滚动参数随第一个请求一起提供,然后您可以使用它,直到公司成为[]

我创建的方法是

  defp companies(scroll_param) do
    url = if scroll_param, do: "#{@intercom_url}/companies/scroll?scroll_param=#{scroll_param}", else: "#{@intercom_url}/companies/scroll"
    headers = ["Authorization": "Bearer #{@intercom_token}", "Accept": "Accept:application/json"]
    response = HTTPoison.get(url, headers) |> elem(1)
    case response.status_code do
      200 -> {:ok, response}
      _ -> {:error, response}
    end
  end

我正在尝试诸如

companies = companies(nil)
scroll_param = companies["scroll_param"]
all_companies =
  Enum.reduce(companies["companies"], [], fn company, acc ->
    if companies["companies"] == [] || is_nil(companies["companies"]) do
      {:halt, acc}
    else
      acc ++ company["companies"]
    end
  end)

首先,获得所有公司的scroll_param为零的公司,然后进行Enum.reduce,直到公司成为[]

没有可显示的公司时,请求给出

%{"type":"company.list","companies":[],"scroll_param":"b85a7745-d423-49bf-91b2-72a513b781e4"}

我的问题是我不知道如何在此处使用递归,所以我可以将所有公司的变量设为all_companies

第一个第一个请求给出了这样的值

%{ "companies": [{ "type":"company",
  "company_id":"smithbrothersltd.co.uk",
  "id":"5c7817185170c3ed1cf9d07a",
  "app_id":"f9c1fd60de50d31bcbc3f4d8d74c9c6dbc40e95a",
  "name":"Smith Brothers",
  "remote_created_at":1551374104,
  "created_at":1551374104,
  "updated_at":1551795637,
  "last_request_at":1551795636,
  "monthly_spend":0,
  "session_count":3,
  "user_count":6,
  "tags":{"type":"tag.list","tags":[]},
  "segments":{"type":"segment.list","segments":[{"type":"segment","id":"53834904c1bbf82df800b256"}]},"plan":{},"custom_attributes":{"creation_source":"api"}}
  ],
  "scroll_param":"b85a7745-d423-49bf-91b2-72a513b781e4"
  }

2 个答案:

答案 0 :(得分:0)

如果scroll_param在所有请求中都保持不变(当然,第一个请求除外),则

all_companies = [scroll_param]  # The scroll param acquired by the first request
                |> Stream.cycle()
                |> Stream.map(&companies/1)
                |> Stream.take_while(fn 
                     {:ok, %{"companies" => []}} -> false
                     {:ok, _} -> true
                     {:error, _} -> false
                   end)
                |> Enum.map(fn{:ok, %{"companies" => companies}} -> companies end)

然后,您将第一个请求所获取的公司放在前面,并List.flatten作为结果。

答案 1 :(得分:0)

作为gen_server可能会更好,因为gen_server可以在发生错误(退避,重试等)的情况下控制请求流,但这意味着知道在哪里使用它以及如何使用它。我认为使用Enum.reduce并没有比声明执行此操作的模块有任何好处。您可能希望辨别200以外的错误和状态代码,并明确决定是否值得重试,或记录错误/状态代码等。因为这样,如果发生错误,它将开枪5次,第5次失败连续错误或非200响应代码。我假设HTTPoison在提供标头时将主体解码为JSON,否则您还需要为响应添加解码步骤。

defmodule MyApp.Intercom do

  @intercom_scroll_url @intercom_url <> "/companies/scroll?scroll_param="
  @intercom_base_url   @intercom_url <> "/companies/scroll"
  @intercom_headers    ["Authorization": "Bearer #{@intercom_token}", "Accept": "Accept:application/json"]

  @max_retries 5

  alias HTTPoison.Response, as: Resp

  def get_companies(acc \\ [], scroll_param \\ nil, errors \\ [], retries \\ 0)
  def get_companies(acc, scroll_param, errors, retries) when retries < @max_retries do
    case get_companies_request(scroll_param) do
      {:ok, %Resp{status_code: 200, body: %{"companies" => []}}} ->
        {:ok, :lists.flatten(acc)}

      {:ok, %Resp{status_code: 200, body: %{"companies" => companies, "scroll_param" => n_scroll_param}}} ->
        get_companies([companies | acc], n_scroll_param, errors, 0)

      {:ok, %Resp{status_code: sc, body: body}} ->
        {:error, {:unexpected_status_code, {sc, body}}}

      {:error, %HTTPoison.Error{reason: reason}} ->
        get_companies(acc, scroll_param, [reason | errors], retries + 1)
    end
  end

  def get_companies(acc, _scroll_param, errors, _), do: {:error, {errors, acc}}

  defp get_companies_request(nil), do: HTTPoison.get(@intercom_base_url, @intercom_headers)
  defp get_companies_request(sp), do: HTTPoison.get(@intercom_scroll_url <> sp, @intercom_headers)
end

MyApp.Intercom.get_companies()