Repository: digitalfabrik/integreat-cms

Issue:
Saving region form not possible
### Describe the Bug
<!-- A clear and concise description of what the bug is. -->
It is not possible to submit the region form, because a `KeyError` shows.

### Steps to Reproduce

1. Go to Regions
2. Click on any region or create a new one
3. Click on save
4. See error

### Expected Behavior
<!-- A clear and concise description of what you expected to happen. -->
The region should be saved

### Actual Behavior
<!-- A clear and concise description of what actually happened. -->
The following key error appears: `KeyError at /regions/augsburg/edit: 0`

<details>
  <summary>Debug information</summary>

```
Environment:


Request Method: POST
Request URL: http://localhost:8000/regions/augsburg/edit

Django Version: 2.2.14
Python Version: 3.7.8
Installed Applications:
['cms.apps.CmsConfig',
 'gvz_api.apps.GvzApiConfig',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.messages',
 'django.contrib.sessions',
 'django.contrib.staticfiles',
 'compressor',
 'compressor_toolkit',
 'corsheaders',
 'widget_tweaks',
 'easy_thumbnails',
 'filer',
 'mptt',
 'rules.apps.AutodiscoverRulesConfig']
Installed Middleware:
['corsheaders.middleware.CorsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Traceback:


File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/views/generic/base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
  45.         return bound_method(*args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  21.                 return view_func(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
  45.         return bound_method(*args, **kwargs)

File "/home/timo/job/integreat/django/src/cms/decorators.py" in wrap
  31.             return function(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/contrib/auth/mixins.py" in dispatch
  85.         return super().dispatch(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/views/generic/base.py" in dispatch
  97.         return handler(request, *args, **kwargs)

File "/home/timo/job/integreat/django/src/cms/views/regions/region_view.py" in post
  39.         if not form.is_valid():

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in is_valid
  185.         return self.is_bound and not self.errors

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in errors
  180.             self.full_clean()

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in full_clean
  382.         self._clean_form()

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in _clean_form
  409.             cleaned_data = self.clean()

File "/home/timo/job/integreat/django/src/cms/forms/regions/region_form.py" in clean
  86.                 region_type=cleaned_data["administrative_division"],

File "/home/timo/job/integreat/django/src/gvz_api/utils.py" in __init__
  190.             best_match = api.best_match(region_name, region_type)

File "/home/timo/job/integreat/django/src/gvz_api/utils.py" in best_match
  135.             return results[0]

Exception Type: KeyError at /regions/augsburg/edit
Exception Value: 0

```

</details>

### Additional Information
<!-- Add any other context (e.g. logs, screenshots, etc.) about the problem here. -->

The error appears because the gvz api is used with double slashes (`https://gvz.integreat-app.de/api//search/Augsburg`) which it can handle on http requests, but returns the following error on https requests:  `{"error":"end point not found"}`
Saving region form not possible
### Describe the Bug
<!-- A clear and concise description of what the bug is. -->
It is not possible to submit the region form, because a `KeyError` shows.

### Steps to Reproduce

1. Go to Regions
2. Click on any region or create a new one
3. Click on save
4. See error

### Expected Behavior
<!-- A clear and concise description of what you expected to happen. -->
The region should be saved

### Actual Behavior
<!-- A clear and concise description of what actually happened. -->
The following key error appears: `KeyError at /regions/augsburg/edit: 0`

<details>
  <summary>Debug information</summary>

```
Environment:


Request Method: POST
Request URL: http://localhost:8000/regions/augsburg/edit

Django Version: 2.2.14
Python Version: 3.7.8
Installed Applications:
['cms.apps.CmsConfig',
 'gvz_api.apps.GvzApiConfig',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.messages',
 'django.contrib.sessions',
 'django.contrib.staticfiles',
 'compressor',
 'compressor_toolkit',
 'corsheaders',
 'widget_tweaks',
 'easy_thumbnails',
 'filer',
 'mptt',
 'rules.apps.AutodiscoverRulesConfig']
Installed Middleware:
['corsheaders.middleware.CorsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Traceback:


File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/views/generic/base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
  45.         return bound_method(*args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  21.                 return view_func(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
  45.         return bound_method(*args, **kwargs)

File "/home/timo/job/integreat/django/src/cms/decorators.py" in wrap
  31.             return function(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/contrib/auth/mixins.py" in dispatch
  85.         return super().dispatch(request, *args, **kwargs)

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/views/generic/base.py" in dispatch
  97.         return handler(request, *args, **kwargs)

File "/home/timo/job/integreat/django/src/cms/views/regions/region_view.py" in post
  39.         if not form.is_valid():

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in is_valid
  185.         return self.is_bound and not self.errors

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in errors
  180.             self.full_clean()

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in full_clean
  382.         self._clean_form()

File "/home/timo/job/integreat/django/.venv/lib/python3.7/site-packages/django/forms/forms.py" in _clean_form
  409.             cleaned_data = self.clean()

File "/home/timo/job/integreat/django/src/cms/forms/regions/region_form.py" in clean
  86.                 region_type=cleaned_data["administrative_division"],

File "/home/timo/job/integreat/django/src/gvz_api/utils.py" in __init__
  190.             best_match = api.best_match(region_name, region_type)

File "/home/timo/job/integreat/django/src/gvz_api/utils.py" in best_match
  135.             return results[0]

Exception Type: KeyError at /regions/augsburg/edit
Exception Value: 0

```

</details>

### Additional Information
<!-- Add any other context (e.g. logs, screenshots, etc.) about the problem here. -->

The error appears because the gvz api is used with double slashes (`https://gvz.integreat-app.de/api//search/Augsburg`) which it can handle on http requests, but returns the following error on https requests:  `{"error":"end point not found"}`


Discussion:

Resolving patch:
diff --git a/src/backend/settings.py b/src/backend/settings.py
--- a/src/backend/settings.py
+++ b/src/backend/settings.py
@@ -227,7 +227,7 @@
 COMPRESS_OFFLINE = True
 
 # GVZ (Gemeindeverzeichnis) API URL
-GVZ_API_URL = "https://gvz.integreat-app.de/api/"
+GVZ_API_URL = "https://gvz.integreat-app.de/api"
 GVZ_API_ENABLED = True
 
 # Allow access to all domains by setting the following variable to TRUE
diff --git a/src/gvz_api/apps.py b/src/gvz_api/apps.py
--- a/src/gvz_api/apps.py
+++ b/src/gvz_api/apps.py
@@ -30,11 +30,13 @@ def ready(self):
                     response = requests.get(
                         f"{settings.GVZ_API_URL}/search/expect_empty_json", timeout=3
                     )
-                    json.loads(response.text)
+                    # Require the response to be empty, otherwise it's probably an error
+                    assert not json.loads(response.text)
                 except (
                     json.decoder.JSONDecodeError,
                     requests.exceptions.RequestException,
                     requests.exceptions.Timeout,
+                    AssertionError,
                 ):
                     logger.info(
                         "GVZ API is not available. You won't be able to "


Test patch: