defmodule SPARQL.Client.Request do
@moduledoc """
A struct representing a HTTP request of a SPARQL protocol operation.
For now, you won't have to deal with this struct that much.
You'll encounter it only as a member of a `SPARQL.Client.HTTPError` or when writing a custom
config function for the HTTP headers (see documentation of the configuration options for `SPARQL.Client`).
"""
defstruct [
:sparql_operation_type,
:sparql_operation_form,
:sparql_operation_payload,
:sparql_endpoint,
:sparql_protocol_version,
:sparql_graph_params,
:http_method,
:http_headers,
:http_content_type_header,
:http_accept_header,
:http_body,
:http_status,
:http_response_content_type,
:http_response_body,
:result
]
@type protocol_version :: String.t()
@type http_method :: :get | :post
@type t :: %__MODULE__{
sparql_operation_type: module,
sparql_operation_form: atom,
sparql_operation_payload: String.t(),
sparql_endpoint: String.t(),
sparql_protocol_version: protocol_version,
sparql_graph_params: list,
http_method: http_method,
http_headers: map,
http_content_type_header: String.t(),
http_accept_header: String.t(),
http_body: String.t() | nil,
http_status: pos_integer,
http_response_content_type: String.t(),
http_response_body: String.t(),
result: SPARQL.Query.Result.t() | RDF.Data.t()
}
@doc false
def build(type, form, payload, endpoint, opts \\ []) do
%__MODULE__{
sparql_endpoint: endpoint,
sparql_operation_type: type,
sparql_operation_form: form,
sparql_operation_payload: payload,
sparql_graph_params: graph_params(opts)
}
|> init_operation(opts)
|> add_http_headers(opts)
end
defp init_operation(request, opts) do
request.sparql_operation_type.init(request, opts)
end
@doc false
def operation_http_headers(request, opts \\ []) do
request.sparql_operation_type.http_headers(request, opts)
end
@doc false
def query_parameter_key(request) do
request.sparql_operation_type.query_parameter_key()
end
defp add_http_headers({:ok, request}, opts) do
with {:ok, headers} <- operation_http_headers(request, opts) do
{:ok,
%{
request
| http_headers: Map.merge(headers, default_http_headers(request, headers, opts))
}}
end
end
defp add_http_headers(error, _opts), do: error
defp default_http_headers do
Application.get_env(:sparql_client, :http_headers)
end
defp default_http_headers(request, headers, opts) do
case Keyword.get(opts, :headers, default_http_headers()) do
fun when is_function(fun) -> fun.(request, headers)
nil -> %{}
default_headers -> default_headers
end
end
defp graph_params(opts) do
opts
|> Enum.reduce([], fn
{:default_graph, graph_uris}, acc when is_list(graph_uris) ->
Enum.reduce(graph_uris, acc, fn graph_uri, acc ->
[{"default-graph-uri", graph_uri} | acc]
end)
{:default_graph, graph_uri}, acc ->
[{"default-graph-uri", graph_uri} | acc]
{:named_graph, graph_uris}, acc when is_list(graph_uris) ->
Enum.reduce(graph_uris, acc, fn graph_uri, acc ->
[{"named-graph-uri", graph_uri} | acc]
end)
{:named_graph, graph_uri}, acc ->
[{"named-graph-uri", graph_uri} | acc]
_, acc ->
acc
end)
|> Enum.reverse()
end
@doc false
def call(%__MODULE__{} = request, opts) do
case SPARQL.Client.Tesla.call(request, opts) do
{:ok, %__MODULE__{http_status: status} = request} when status in 200..299 ->
request.sparql_operation_type.evaluate_response(request, opts)
{:ok, request} ->
{:error, %SPARQL.Client.HTTPError{request: request, status: request.http_status}}
error ->
error
end
end
end