diff --git a/greg/admin.py b/greg/admin.py index 90b80bbe495c3c7222a1c3f278b355dfba02fb93..fa833b233cf117ba14267a59299ea508668567e0 100644 --- a/greg/admin.py +++ b/greg/admin.py @@ -78,7 +78,7 @@ class ConsentAdmin(VersionAdmin): get_consent_type_name.short_description = "Consent name" # type: ignore -class ConsentTypeAdmin(admin.ModelAdmin): +class ConsentTypeAdmin(VersionAdmin): list_display = ( "id", "name_en", @@ -90,8 +90,9 @@ class ConsentTypeAdmin(admin.ModelAdmin): class OrganizationalUnitAdmin(VersionAdmin): - list_display = ("id", "name_en", "parent") + list_display = ("id", "orgreg_id", "name_en", "parent") readonly_fields = ("id", "created", "updated") + search_fields = ("name_en", "id", "orgreg_id") class OrganizationalUnitInline(admin.TabularInline): diff --git a/greg/management/commands/import_from_orgreg.py b/greg/management/commands/import_from_orgreg.py new file mode 100644 index 0000000000000000000000000000000000000000..438accf08ddd1c6fd76031171e90d3f4b5967d4f --- /dev/null +++ b/greg/management/commands/import_from_orgreg.py @@ -0,0 +1,121 @@ +""" +Fetch all OUs from OrgReg and add the complete tree to Greg. + +Assumes that the header used for authentication is of the type +'X-Gravitee-Api-Key': 'token'. + +If the path to the endpoint of the OUs is oregreg/v3/ou/ you want to give +orgreg/v3/ as the url argument (note the trailing slash). +""" +import datetime +import logging +from typing import Union, Mapping, Dict + +import orgreg_client +from django.conf import settings +from django.core.management.base import BaseCommand +from orgreg_client import OrgUnit + +from greg.models import OrganizationalUnit + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + help = __doc__ + processed: Dict[int, OrganizationalUnit] = {} + + def _get_or_create_and_set_values( + self, ou, values: Mapping[str, Union[str, int, bool]] + ): + """Upsert ou with latest values and store in processed dict.""" + + self.processed[ou.ou_id], created = OrganizationalUnit.objects.get_or_create( + orgreg_id=str(ou.ou_id) + ) + for k, v in values.items(): + setattr(self.processed[ou.ou_id], k, v) + self.processed[ou.ou_id].save() + logger.info( + "%s %s with %s", + "Created" if created else "Updated", + self.processed[ou.ou_id], + values, + ) + + def _upsert_ou(self, ou: OrgUnit, ous: Mapping[int, OrgUnit]): + """ + Update or create a OU with current values. + + There are three cases: + 1. OU has no parent -> Create it. + 2. OU has parent and parent exists -> Create it. + 3. OU has parent but parent has not been created -> Create parent, then child. + + Case 3 is solved by calling this method recursively so that the parent and its + parent are created before the child. + """ + + # skip if already processed (happens if a previous ou needed this one as a + # parent) + if ou.ou_id in self.processed: + return + + values = {"deleted": False} + # add names if present + if ou.name: + if ou.name.nob: + values["name_nb"] = ou.name.nob + if ou.name.eng: + values["name_en"] = ou.name.eng + + # Set inactive if ou is no longer valid + is_inactive = ou.valid_to and ou.valid_to < datetime.date.today() + values["active"] = not is_inactive + + # Create OUs without parent (this should only happen for the root node) + if not ou.parent: + self._get_or_create_and_set_values(ou, values) + return + + # If OU has parent and it is not already created, we create it and point the + # child to it. + if ou.parent not in self.processed: + self._upsert_ou(ous[ou.parent], ous) + values["parent"] = self.processed[ou.parent] + + # Finally create the OU we're currently looking at + self._get_or_create_and_set_values(ou, values) + + def handle(self, *args, **options): + """ + Handle import of OUs from OrgReg. + + - Updates already present OUs with new information + - Creates not present OUs + - Set not present OUs as inactive + """ + # Empty processed in case of class reuse by tests + self.processed = {} + client = orgreg_client.get_client(**settings.ORGREG_CLIENT) + + # Fetch already present OUs and those in OrgReg + current_ous = {int(i.orgreg_id): i for i in OrganizationalUnit.objects.all()} + + logger.info("Fetch OUs from Orgreg...") + orgreg_ous = {i.ou_id: i for i in client.get_ou()} + + # Set deleted if an OU from Greg no longer exists in OrgReg and inactive if + # valid_to is set to a date before today + logger.info("Set deleted tag on removed OUs...") + for ou_id, ou in current_ous.items(): + if ou_id not in orgreg_ous: + logger.info("%s marked as deleted", ou) + ou.deleted = True + ou.save() + continue + + # Create new OUs recursively to ensure parents exist before children + logger.info("Update OU values...") + for ou_id, ou in orgreg_ous.items(): + self._upsert_ou(ou, orgreg_ous) diff --git a/greg/migrations/0004_ou_deleted_active.py b/greg/migrations/0004_ou_deleted_active.py new file mode 100644 index 0000000000000000000000000000000000000000..3c1954fbe6d99ee3d194068a493ba27e7f2392f4 --- /dev/null +++ b/greg/migrations/0004_ou_deleted_active.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.7 on 2021-09-08 08:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('greg', '0003_consenttype_mandatory'), + ] + + operations = [ + migrations.AddField( + model_name='organizationalunit', + name='active', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='organizationalunit', + name='deleted', + field=models.BooleanField(default=False), + ), + ] diff --git a/greg/models.py b/greg/models.py index 701821a068eeab2cd4eeda92084e4e182fce7aed..6d76d19ab595843a0cab6c0a18a9e5feee7fcdf8 100644 --- a/greg/models.py +++ b/greg/models.py @@ -317,6 +317,8 @@ class OrganizationalUnit(BaseModel): name_nb = models.CharField(max_length=256) name_en = models.CharField(max_length=256) parent = models.ForeignKey("self", on_delete=models.PROTECT, null=True) + active = models.BooleanField(default=True) + deleted = models.BooleanField(default=False) def __repr__(self): return "{}(id={!r}, orgreg_id={!r}, name_en={!r}, parent={!r})".format( diff --git a/greg/tests/management/test_import_from_orgreg.py b/greg/tests/management/test_import_from_orgreg.py new file mode 100644 index 0000000000000000000000000000000000000000..72d867bffc5b32dec03fb4aaedb161f56c12b03e --- /dev/null +++ b/greg/tests/management/test_import_from_orgreg.py @@ -0,0 +1,73 @@ +import datetime + +import pytest +from django.core.management import call_command +from orgreg_client import OrgUnit, OrgUnitList + +from greg.models import OrganizationalUnit + + +@pytest.fixture +def old_unit(): + OrganizationalUnit.objects.create(orgreg_id="4", name_nb="a", name_en="b") + return OrganizationalUnit.objects.get(orgreg_id="4") + + +@pytest.fixture +def orgreg_response(): + orgreg_ous = OrgUnitList( + __root__=[ + OrgUnit( + ou_id=1, + valid_from=datetime.date(year=2020, month=2, day=4), + external_keys=[], + name={"nob": "foo"}, + ), + OrgUnit( + ou_id=3, + valid_from=datetime.date(year=2020, month=2, day=6), + valid_to=datetime.date(year=2020, month=2, day=7), + parent=2, + name={"nob": "bar"}, + external_keys=[], + ), + OrgUnit( + ou_id=2, + valid_from=datetime.date(year=2020, month=2, day=5), + parent=1, + name={"eng": "baz"}, + external_keys=[], + ), + ] + ) + return orgreg_ous + + +@pytest.mark.django_db +def test_command_ou_init(requests_mock, old_unit, orgreg_response): + requests_mock.get("https://example.com/fake/ou/", text=orgreg_response.json()) + assert OrganizationalUnit.objects.all().count() == 1 + call_command("import_from_orgreg") + + # Ensure all three new units are imported + assert OrganizationalUnit.objects.all().count() == 4 + + # Ensure tree is built correctly + assert OrganizationalUnit.objects.get(orgreg_id="3").parent.orgreg_id == "2" + assert OrganizationalUnit.objects.get(orgreg_id="3").parent.parent.orgreg_id == "1" + + # Ensure unknown org is marked deleted + assert OrganizationalUnit.objects.get(orgreg_id="4").deleted is True + + +@pytest.mark.django_db +def test_run_twice(requests_mock, orgreg_response): + requests_mock.get("https://example.com/fake/ou/", text=orgreg_response.json()) + + assert OrganizationalUnit.objects.all().count() == 0 + + call_command("import_from_orgreg") + assert OrganizationalUnit.objects.all().count() == 3 + + call_command("import_from_orgreg") + assert OrganizationalUnit.objects.all().count() == 3 diff --git a/gregsite/settings/dev.py b/gregsite/settings/dev.py index e6defc10ab35b5020e118ec57358cf349496d3f3..dfaaed2ec0e1602d92ca2e6b8987b1c7339f1e11 100644 --- a/gregsite/settings/dev.py +++ b/gregsite/settings/dev.py @@ -8,3 +8,8 @@ try: from .local import * except ImportError: pass + +ORGREG_CLIENT = { + "endpoints": {"base_url": "https://example.com/fake/"}, + "headers": {"X-Gravitee-Api-Key": "bar"}, +} diff --git a/mypy.ini b/mypy.ini index 96c6844ecdc814148146df6d3076321e964f599d..4b3066fc11e7e93a77557f2f7735c495474657ca 100644 --- a/mypy.ini +++ b/mypy.ini @@ -37,3 +37,6 @@ ignore_missing_imports = True [mypy-pika_context_manager.*] ignore_missing_imports = True + +[mypy-orgreg_client.*] +ignore_missing_imports = True diff --git a/poetry.lock b/poetry.lock index 69220a687a27e38b8e3c114dc55e9cc5002ef01c..ff64d048394a119373a4f00f5b6f4b5b5ac9875d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -98,7 +98,7 @@ python-versions = "*" name = "charset-normalizer" version = "2.0.4" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" +category = "main" optional = false python-versions = ">=3.5.0" @@ -335,7 +335,7 @@ text-unidecode = "1.3" name = "idna" version = "3.2" description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" +category = "main" optional = false python-versions = ">=3.5" @@ -523,6 +523,25 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "orgreg-client" +version = "0.2.3" +description = "Client for the OrgReg-" +category = "main" +optional = false +python-versions = "*" +develop = false + +[package.dependencies] +pydantic = "*" +requests = "*" + +[package.source] +type = "git" +url = "https://git.app.uib.no/it-bott-integrasjoner/orgreg-client.git" +reference = "v0.2.3" +resolved_reference = "bb60dbba6b48dd82a6a585f4568fd3199eced188" + [[package]] name = "packaging" version = "21.0" @@ -663,6 +682,21 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pydantic" +version = "1.8.2" +description = "Data validation and settings management using python 3.6 type hinting" +category = "main" +optional = false +python-versions = ">=3.6.1" + +[package.dependencies] +typing-extensions = ">=3.7.4.3" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + [[package]] name = "pygments" version = "2.10.0" @@ -828,7 +862,7 @@ python-versions = "*" name = "requests" version = "2.26.0" description = "Python HTTP for Humans." -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" @@ -842,6 +876,22 @@ urllib3 = ">=1.21.1,<1.27" socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] +[[package]] +name = "requests-mock" +version = "1.9.3" +description = "Mock out responses from the requests package" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +requests = ">=2.3,<3" +six = "*" + +[package.extras] +fixture = ["fixtures"] +test = ["fixtures", "mock", "purl", "pytest", "sphinx", "testrepository (>=0.0.18)", "testtools"] + [[package]] name = "rope" version = "0.19.0" @@ -953,7 +1003,7 @@ python-versions = "*" name = "typing-extensions" version = "3.10.0.2" description = "Backported and Experimental Type Hints for Python 3.5+" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -1008,7 +1058,7 @@ python-versions = "*" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "77e4055c61d7e5f8177787a95c79647067fba3bcb298c636a7fb3c3961227b9d" +content-hash = "75c30a6f1bcd72a8d9725dbe92509be0fab8bf9e498ceaaf0a730f2337f439b0" [metadata.files] appnope = [ @@ -1075,6 +1125,9 @@ coverage = [ {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, + {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, + {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, + {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, @@ -1307,6 +1360,7 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] +orgreg-client = [] packaging = [ {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, @@ -1383,6 +1437,30 @@ py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] +pydantic = [ + {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"}, + {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"}, + {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"}, + {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"}, + {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"}, + {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"}, + {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"}, + {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"}, + {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"}, + {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, +] pygments = [ {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, @@ -1457,18 +1535,26 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, @@ -1520,6 +1606,10 @@ requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, ] +requests-mock = [ + {file = "requests-mock-1.9.3.tar.gz", hash = "sha256:8d72abe54546c1fc9696fa1516672f1031d72a55a1d66c85184f972a24ba0eba"}, + {file = "requests_mock-1.9.3-py2.py3-none-any.whl", hash = "sha256:0a2d38a117c08bb78939ec163522976ad59a6b7fdd82b709e23bb98004a44970"}, +] rope = [ {file = "rope-0.19.0.tar.gz", hash = "sha256:64e6d747532e1f5c8009ec5aae3e5523a5bcedf516f39a750d57d8ed749d90da"}, ] diff --git a/pyproject.toml b/pyproject.toml index 9c4f2a23756dd65b3eca4b53a54e0e843a772412..9af43a411dab7960a20861cd13135f5b29e4624b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ djangorestframework = "*" drf-spectacular = "*" pika-context-manager = {git = "https://git.app.uib.no/it-bott-integrasjoner/pika-context-manager.git", rev = "v1.2.0"} psycopg2-binary = "*" +orgreg-client = {git = "https://git.app.uib.no/it-bott-integrasjoner/orgreg-client.git", rev = "v0.2.3" } python = "^3.9" python-daemon = "*" python-json-logger = "*" @@ -34,6 +35,7 @@ pylint-django = "*" pytest-django = "*" rope = "*" coverage = "*" +requests-mock = "^1.9.3" [tool.black] extend-exclude = '^/greg/migrations/.*\.py'