Skip to content
Merged
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
8 changes: 5 additions & 3 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ API reference
Exceptions
==========

.. autoclass:: BrregException
.. autoclass:: BrregError
:members:
:undoc-members:


.. autoclass:: BrregRestException
.. autoclass:: BrregRestError
:members:
:undoc-members:

Expand All @@ -22,7 +22,9 @@ Enhetsregisteret

.. automodule:: brreg.enhetsregisteret

.. autofunction:: brreg.enhetsregisteret.get_enhet
.. autoclass:: brreg.enhetsregisteret.Client
:members:
:undoc-members:

.. autoclass:: brreg.enhetsregisteret.Enhet
:members:
Expand Down
5 changes: 3 additions & 2 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ Organization details by organization number

To get details about an organization ("enhet") given its organization number:

>>> from brreg import enhetsregisteret
>>> enhet = enhetsregisteret.get_enhet('915501680')
>>> from brreg.enhetsregisteret import Client
>>> client = Client()
>>> enhet = client.get_enhet('915501680')
>>> enhet.organisasjonsnummer
'915501680'
>>> enhet.navn
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ classifiers = [
[tool.poetry.dependencies]
python = ">= 3.8.0"
attrs = ">= 22.2"
requests = ">= 2.28.1"
httpx = ">= 0.24"

[tool.poetry.group.dev.dependencies]
nox = "^2023.4.22"
Expand All @@ -41,7 +41,7 @@ ruff = "^0.1.4"
coverage = { extras = ["toml"], version = "^7.3.2" }
pytest = "^7.4.2"
pytest-cov = "^4.1.0"
responses = "^0.24.0"
pytest-httpx = "0.22.0"

[tool.poetry.group.typing.dependencies]
types-requests = "^2.31.0.10"
Expand Down
4 changes: 2 additions & 2 deletions src/brreg/_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ def __init__(
*,
method: Optional[str],
url: Optional[str],
status: Optional[int],
status_code: Optional[int],
) -> None:
super().__init__(f"REST API exception: {msg}")
self.method = method
self.url = url
self.status = status
self.status_code = status_code
4 changes: 2 additions & 2 deletions src/brreg/enhetsregisteret/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
See https://data.brreg.no/enhetsregisteret/api/docs/index.html for API details.
"""

from brreg.enhetsregisteret._client import get_enhet
from brreg.enhetsregisteret._client import Client
from brreg.enhetsregisteret._types import (
Adresse,
Enhet,
Expand All @@ -14,7 +14,7 @@

__all__ = [
# From _client module:
"get_enhet",
"Client",
# From _types module:
"Adresse",
"Enhet",
Expand Down
79 changes: 53 additions & 26 deletions src/brreg/enhetsregisteret/_client.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,65 @@
from typing import Optional
from __future__ import annotations

import requests
from typing import TYPE_CHECKING, Optional

import httpx

from brreg import BrregError, BrregRestError
from brreg.enhetsregisteret._types import Enhet

if TYPE_CHECKING:
from types import TracebackType


from ._types import Enhet
class Client:
client: httpx.Client

BASE_URL = "https://data.brreg.no/enhetsregisteret/api"
def __new__(cls) -> Client:
self = super().__new__(cls)
self.open()
return self

def __enter__(self) -> Client:
return self

def get_enhet(organisasjonsnummer: str) -> Optional[Enhet]:
"""Get :class:`Enhet` given an organization number.
def __exit__(
self,
exc_type: type[BaseException] | None = None,
exc_value: BaseException | None = None,
traceback: TracebackType | None = None,
) -> None:
self.close()

Returns :class:`None` if Enhet is gone or not found
Returns :class:`Enhet` if Enhet is found
def open(self) -> None:
self.client = httpx.Client(
base_url="https://data.brreg.no/enhetsregisteret/api",
)

Raises :class:`BrregRestException` if a REST exception occures
Raises :class:`BrregException` if an unhandled exception occures
"""
try:
res = requests.get(f"{BASE_URL}/enheter/{organisasjonsnummer}")
def close(self) -> None:
self.client.close()

if res.status_code in (404, 410):
return None
def get_enhet(self, organisasjonsnummer: str) -> Optional[Enhet]:
"""Get :class:`Enhet` given an organization number.

res.raise_for_status()
Returns :class:`None` if Enhet is gone or not found.
Returns :class:`Enhet` if Enhet is found.

return Enhet.from_json(res.json())
except requests.RequestException as exc:
raise BrregRestError(
str(exc),
method=(exc.request.method if exc.request else None),
url=(exc.request.url if exc.request else None),
status=getattr(exc.response, "status_code", None),
) from exc
except Exception as exc:
raise BrregError(exc) from exc
Raises :class:`BrregRestError` if a REST error occurs.
Raises :class:`BrregError` if an unhandled exception occurs.
"""
res: Optional[httpx.Response] = None
try:
res = self.client.get(f"/enheter/{organisasjonsnummer}")
if res.status_code in (404, 410):
return None
res.raise_for_status()
return Enhet.from_json(res.json())
except httpx.HTTPError as exc:
raise BrregRestError(
str(exc),
method=(exc.request.method if exc.request else None),
url=(str(exc.request.url) if exc.request else None),
status_code=(res.status_code if res else None),
) from exc
except Exception as exc:
raise BrregError(exc) from exc
109 changes: 61 additions & 48 deletions tests/test_enhetsregisteret.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
from datetime import date

import httpx
import pytest
import responses
from pytest_httpx import HTTPXMock

from brreg import BrregRestError, enhetsregisteret


@responses.activate
def test_get_enhet(organization_details_response: bytes) -> None:
responses.add(
responses.GET,
"https://data.brreg.no/enhetsregisteret/api/enheter/818511752",
body=organization_details_response,
status=200,
content_type="application/json",
def test_get_enhet(
httpx_mock: HTTPXMock,
organization_details_response: bytes,
) -> None:
httpx_mock.add_response( # pyright: ignore[reportUnknownMemberType]
method="GET",
url="https://data.brreg.no/enhetsregisteret/api/enheter/818511752",
status_code=200,
headers={"content-type": "application/json"},
content=organization_details_response,
)

org = enhetsregisteret.get_enhet("818511752")
org = enhetsregisteret.Client().get_enhet("818511752")

assert org is not None
assert org.organisasjonsnummer == "818511752"
Expand Down Expand Up @@ -50,17 +53,19 @@ def test_get_enhet(organization_details_response: bytes) -> None:
assert org.slettedato is None


@responses.activate
def test_get_enhet_when_deleted(deleted_organization_details_response: bytes) -> None:
responses.add(
responses.GET,
"https://data.brreg.no/enhetsregisteret/api/enheter/815597222",
body=deleted_organization_details_response,
status=200,
content_type="application/json",
def test_get_enhet_when_deleted(
httpx_mock: HTTPXMock,
deleted_organization_details_response: bytes,
) -> None:
httpx_mock.add_response( # pyright: ignore[reportUnknownMemberType]
method="GET",
url="https://data.brreg.no/enhetsregisteret/api/enheter/815597222",
status_code=200,
headers={"content-type": "application/json"},
content=deleted_organization_details_response,
)

org = enhetsregisteret.get_enhet("815597222")
org = enhetsregisteret.Client().get_enhet("815597222")

assert org is not None
assert org.organisasjonsnummer == "815597222"
Expand All @@ -71,45 +76,48 @@ def test_get_enhet_when_deleted(deleted_organization_details_response: bytes) ->
assert org.slettedato == date(2017, 10, 20)


@responses.activate
def test_get_enhet_when_gone() -> None:
responses.add(
responses.GET,
"https://data.brreg.no/enhetsregisteret/api/enheter/818511752",
status=410,
content_type="application/json",
def test_get_enhet_when_gone(
httpx_mock: HTTPXMock,
) -> None:
httpx_mock.add_response( # pyright: ignore[reportUnknownMemberType]
method="GET",
url="https://data.brreg.no/enhetsregisteret/api/enheter/818511752",
status_code=410,
headers={"content-type": "application/json"},
)

org = enhetsregisteret.get_enhet("818511752")
org = enhetsregisteret.Client().get_enhet("818511752")

assert org is None


@responses.activate
def test_get_enhet_when_not_found() -> None:
responses.add(
responses.GET,
"https://data.brreg.no/enhetsregisteret/api/enheter/818511752",
status=404,
content_type="application/json",
def test_get_enhet_when_not_found(
httpx_mock: HTTPXMock,
) -> None:
httpx_mock.add_response( # pyright: ignore[reportUnknownMemberType]
method="GET",
url="https://data.brreg.no/enhetsregisteret/api/enheter/818511752",
status_code=404,
headers={"content-type": "application/json"},
)

org = enhetsregisteret.get_enhet("818511752")
org = enhetsregisteret.Client().get_enhet("818511752")

assert org is None


@responses.activate
def test_get_enhet_when_http_error() -> None:
responses.add(
responses.GET,
"https://data.brreg.no/enhetsregisteret/api/enheter/818511752",
status=400,
content_type="application/json",
def test_get_enhet_when_http_error(
httpx_mock: HTTPXMock,
) -> None:
httpx_mock.add_response( # pyright: ignore[reportUnknownMemberType]
method="GET",
url="https://data.brreg.no/enhetsregisteret/api/enheter/818511752",
status_code=400,
headers={"content-type": "application/json"},
)

with pytest.raises(BrregRestError) as exc_info:
enhetsregisteret.get_enhet("818511752")
enhetsregisteret.Client().get_enhet("818511752")

assert "REST API exception" in str(exc_info.value)
assert "Bad Request" in str(exc_info.value)
Expand All @@ -119,13 +127,18 @@ def test_get_enhet_when_http_error() -> None:
exc_info.value.url
== "https://data.brreg.no/enhetsregisteret/api/enheter/818511752"
)
assert exc_info.value.status == 400
assert exc_info.value.status_code == 400


@responses.activate
def test_get_organization_by_number_when_http_timeout() -> None:
def test_get_organization_by_number_when_http_timeout(
httpx_mock: HTTPXMock,
) -> None:
httpx_mock.add_exception( # pyright: ignore[reportUnknownMemberType]
httpx.ConnectTimeout("Connection refused"),
)

with pytest.raises(BrregRestError) as exc_info:
enhetsregisteret.get_enhet("818511752")
enhetsregisteret.Client().get_enhet("818511752")

assert "REST API exception" in str(exc_info.value)
assert "Connection refused" in str(exc_info.value)
Expand All @@ -135,4 +148,4 @@ def test_get_organization_by_number_when_http_timeout() -> None:
exc_info.value.url
== "https://data.brreg.no/enhetsregisteret/api/enheter/818511752"
)
assert exc_info.value.status is None
assert exc_info.value.status_code is None