Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The ``read_data`` function expects the following arguments:
"2026-01-02"
)

You can optionally specify an API key for instances that require one.
You can specify an API key for instances that require one.

.. code:: python

Expand All @@ -48,6 +48,19 @@ You can optionally specify an API key for instances that require one.
api_key="<api key>"
)

You can specify a username and password for instances that require basic auth.

.. code:: python

df = read_data(
"https://lasp.colorado.edu/lisird/latis/dap2",
"bremen_composite_mgii",
"2026-01-01",
"2026-01-02",
username="<username>",
password="<password>"
)

For queries more complicated than time selections, you can optionally specify an additional `DAP2 query fragment <https://lasp.colorado.edu/lisird/about/latis>`__ that will be appended to the query sent to LaTiS. It must be URL-encoded.

.. code:: python
Expand Down
56 changes: 45 additions & 11 deletions src/latis/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
import numpy as np
import pandas as pd
import requests
from requests.auth import AuthBase
from requests.auth import HTTPBasicAuth
import urllib.parse
from typing import List, Dict, Any, Union, Optional


def read_data(base_url, dataset, start_time, end_time,
api_key=None, api_key_header="x-api-key", query=None):
def read_data(base_url, dataset, start_time, end_time, api_key=None,
api_key_header="x-api-key", username=None, password=None,
query=None):
"""
Make a request to a LaTiS instance for data.

Expand All @@ -31,6 +34,10 @@ def read_data(base_url, dataset, start_time, end_time,
api_key (str): If set, the API key sent with the request.
api_key_header (str): The HTTP header used to include the API
key given by ``api_key``.
username (str): If set, the username used for basic auth when
making the request.
password (str): If set, the password used for basic auth when
making the request.
query (str): Optional additional OPeNDAP DAP2 query fragment.
Must be URL-encoded.

Expand All @@ -49,17 +56,21 @@ def read_data(base_url, dataset, start_time, end_time,
rest="" if query is None else "&" + query
)

headers = {}
# No auth by default.
auth = None

if api_key is not None:
headers[api_key_header] = api_key
auth = APIKeyAuth(api_key, api_key_header)
elif username is not None and password is not None:
auth = HTTPBasicAuth(username, password)

# If dataset is a string, make a DAP2 query. If dataset is a list
# or a tuple, make a join query.
if isinstance(dataset, str):
res = _make_dap2_query(base_url, dataset, query, headers)
res = _make_dap2_query(base_url, dataset, query, auth)
elif isinstance(dataset, (list, tuple)):
if len(dataset) == 1:
res = _make_dap2_query(base_url, dataset[0], query, headers)
res = _make_dap2_query(base_url, dataset[0], query, auth)
else:
# Joining is only supported for LaTiS 3 instances. The
# only way we can check whether an instance is a LaTiS 3
Expand All @@ -74,7 +85,7 @@ def read_data(base_url, dataset, start_time, end_time,
# slash.
join_url = base_url.rstrip("/dap2") + "/join"

res = _make_join_query(join_url, dataset, query, headers)
res = _make_join_query(join_url, dataset, query, auth)
else:
# Probably not a LaTiS 3 instance.
raise ValueError(
Expand All @@ -95,7 +106,7 @@ def read_data(base_url, dataset, start_time, end_time,
return df


def _make_dap2_query(base, dataset, query, headers):
def _make_dap2_query(base, dataset, query, auth):
"""
Makes a DAP 2 request to a LaTiS instance.
"""
Expand All @@ -106,22 +117,45 @@ def _make_dap2_query(base, dataset, query, headers):
query=query
)

res = requests.get(url, headers=headers)
res = requests.get(url, auth=auth)
return res


def _make_join_query(base, datasets, query, headers):
def _make_join_query(base, datasets, query, auth):
"""
Makes a request to the join service of a LaTiS 3 instance.
"""

url = "{base}?{query}".format(base=base, query=query)
body = {"datasets": datasets}

res = requests.post(url, json=body, headers=headers)
res = requests.post(url, json=body, auth=auth)
return res


class APIKeyAuth(AuthBase):
"""Authentication using an API key passed in an HTTP header."""

def __init__(self, key, header='x-api-key'):
"""
Construct an APIKeyAuth instance.

Args:
key (str): The API key
header (str): The HTTP header used to pass the API key.
Defaults to ``x-api-key``.

Returns:
An APIKeyAuth instance.
"""
self.key = key
self.header = header

def __call__(self, req):
req.headers[self.header] = self.key
return req


class LatisInstance:
"""Represents a LaTiS instance.

Expand Down