# `SpaceTAP`
[🔗](https://github.com/jaimeiniesta/space_tap/blob/v0.1.0/lib/space_tap.ex#L1)

Elixir client for the IVOA Table Access Protocol (TAP), the HTTP protocol
astronomical archives use to expose tabular data.

`query/3` sends a query written in ADQL to a TAP service and returns its
result as a `SpaceTAP.Result`, or a `SpaceTAP.Error` when it fails.

SpaceTAP follows [TAP 1.1](https://www.ivoa.net/documents/TAP/20190927/REC-TAP-1.1.html)
and works with TAP 1.0 services.

# `option`

```elixir
@type option() :: {:maxrec, non_neg_integer()} | {:req_options, keyword()}
```

An option for `query/3`.

# `query`

```elixir
@spec query(String.t(), String.t(), [option()]) ::
  {:ok, SpaceTAP.Result.t()} | {:error, SpaceTAP.Error.t()}
```

Runs a sync query: sends `adql` to the TAP service at `service_url` and
returns its result.

`service_url` is the base URL of the service, such as
`"https://gea.esac.esa.int/tap-server/tap"` for the Gaia archive. The query
goes to its `/sync` endpoint (TAP 1.1 §2.4), appended after trimming any
trailing `/`, so pass the base URL even if you know the endpoint.

Use the `https://` URL when the service has one. Req follows a 301 or 302
redirect with a GET that drops the query (RFC 9110 §15.4.2, §15.4.3), so a
service that redirects `http://` to `https://` gets no query, and the
request fails.

    SpaceTAP.query(
      "https://gea.esac.esa.int/tap-server/tap",
      "SELECT TOP 3 source_id, ra, dec FROM gaiadr3.gaia_source"
    )
    #=> {:ok, %SpaceTAP.Result{columns: ["source_id", "ra", "dec"], rows: [...]}}

The result comes as CSV, so every value is a string and an empty cell is
`nil` (see `SpaceTAP.Result`). Errors come as `{:error, %SpaceTAP.Error{}}`,
whose `:type` says what went wrong.

## Options

  * `:maxrec` - the maximum number of rows to return, a non-negative
    integer. The service may apply a lower limit of its own, and `0`
    returns the columns with no rows (DALI 1.1 §3.4.4).
  * `:req_options` - a keyword list of options for `Req.new/1`, such as
    `:receive_timeout` to wait longer than Req's default 15 seconds.
    SpaceTAP sets `:method`, `:url`, `:form`, `:user_agent`,
    `:decode_body`, `:http_errors` and `:into` itself, and ignores them
    here. Other options go to Req as given, and some change the request:
    a `user-agent` in `:headers` replaces SpaceTAP's, and `:params` adds
    parameters to the URL.

An unknown option, or a `:maxrec` or `:req_options` that isn't what the
list above says, raises `ArgumentError`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
