Skip to content

Commit db2cd58

Browse files
committed
Add url shortener
1 parent 684e07a commit db2cd58

10 files changed

Lines changed: 115 additions & 8 deletions

File tree

.env.ci

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ QSL_DATA_ROOT=/io/data
1111

1212
GEORAMA_DATA_INTEGRATION_ROOT=/io/data
1313

14+
GEORAMA_WEBGISURL=https://localhost:9309
15+
1416
GEORAMA_DB_PW=test
1517
GEORAMA_DB_USER=postgres
1618
GEORAMA_DB_PORT=54321

geogirafe/config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
"url": "https://empty"
3939
},
4040
"share": {
41-
"service": "",
42-
"createUrl": "https://empty"
41+
"service": "gmf",
42+
"createUrl": "http://localhost:4242/webgis/short/create"
4343
},
4444
"projections": {
4545
"EPSG:3857": "W-M",

src/georama/core/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class Base(Configuration):
3737
# SECURITY WARNING: don't run with debug turned on in production!
3838
DEBUG = values.BooleanValue(False, environ_prefix="GEORAMA")
3939

40-
WEBGISURL = values.Value(environ_prefix="GEORAMA")
40+
WEBGISURL = values.Value("https://localhost:9309", environ_prefix="GEORAMA")
4141
SITE_TITLE = values.Value("demo.georama.io", environ_prefix="GEORAMA")
4242

4343
LOGGING = {

src/georama/data_integration/models.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@
66
from django.db import models
77
from django.utils.translation import gettext as _
88
from qgis_server_light.interface.common import BBox
9-
from qgis_server_light.interface.exporter.extract import Crs, Custom, DataSource
9+
from qgis_server_light.interface.exporter.extract import (
10+
Crs,
11+
Custom,
12+
DataSource,
13+
Raster,
14+
Style,
15+
Vector,
16+
)
1017
from qgis_server_light.interface.exporter.extract import Field as QslField
11-
from qgis_server_light.interface.exporter.extract import Raster, Style, Vector
1218
from qgis_server_light.interface.job.common.input import QslJobLayer
1319
from xsdata.formats.dataclass.parsers import DictDecoder
1420
from xsdata.formats.dataclass.parsers.config import ParserConfig
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 5.2.10 on 2026-03-16 15:00
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("webgis", "0023_publishedastheme_location_publishedastheme_zoom"),
10+
]
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name="UrlShortener",
15+
fields=[
16+
("id", models.CharField(primary_key=True, serialize=False)),
17+
("url", models.CharField()),
18+
],
19+
),
20+
]

src/georama/webgis/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from georama.webgis.models.short import UrlShortener
12
from georama.webgis.models.themes import (
23
Interface,
34
Layer,
@@ -16,4 +17,5 @@
1617
"PublishedAsLayerWms",
1718
"PublishedAsLayerWmts",
1819
"PublishedAsTheme",
20+
"UrlShortener",
1921
]

src/georama/webgis/models/short.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.db import models
2+
3+
4+
class UrlShortener(models.Model):
5+
id = models.CharField(primary_key=True)
6+
url = models.CharField()

src/georama/webgis/urls.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,8 @@
7979
views.translation_json,
8080
name="translation_json",
8181
),
82+
path(
83+
"/short/get/<str:id>", views.UrlShortenerRetrieve.as_view(), name="get_shortened_url"
84+
),
85+
path("/short/create", views.UrlShortenerCreate.as_view(), name="shorten_url"),
8286
]

src/georama/webgis/views.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
from django.conf import settings
55
from django.contrib.auth.mixins import PermissionRequiredMixin
66
from django.contrib.auth.models import User
7-
from django.db import transaction
8-
from django.http import HttpRequest, HttpResponse, HttpResponseNotFound
7+
from django.db import IntegrityError, transaction
8+
from django.http import HttpRequest, HttpResponse, HttpResponseNotFound, JsonResponse
99
from django.shortcuts import redirect, render
10+
from django.utils.crypto import get_random_string
1011
from django.utils.http import url_has_allowed_host_and_scheme
1112
from django.views import View
1213
from qgis_server_light.interface.common import BBox
@@ -46,7 +47,12 @@
4647
Theme,
4748
ThemesJson,
4849
)
49-
from georama.webgis.models import LayerGroupMp, PublishedAsLayerWms, PublishedAsTheme
50+
from georama.webgis.models import (
51+
LayerGroupMp,
52+
PublishedAsLayerWms,
53+
PublishedAsTheme,
54+
UrlShortener,
55+
)
5056
from georama.webgis.models import OgcServer as WebGisOgcServer
5157

5258

@@ -616,3 +622,33 @@ def translation_json(request: HttpRequest):
616622
return HttpResponse(
617623
json.dumps(translation, indent=2), status=200, content_type="application/json"
618624
)
625+
626+
627+
class UrlShortenerCreate(View):
628+
def post(self, request: HttpRequest):
629+
url = request.POST.get("url", "").strip()
630+
if not url:
631+
return JsonResponse({"error": "Missing URL."}, status=400)
632+
633+
if not url.startswith(settings.WEBGISURL):
634+
return JsonResponse({"error": "Invalid URL."}, status=400)
635+
636+
while True:
637+
try:
638+
short = UrlShortener.objects.create(id=get_random_string(length=6), url=url)
639+
break
640+
except IntegrityError:
641+
continue
642+
643+
short_url = request.build_absolute_uri(f"/webgis/short/get/{short.id}")
644+
return JsonResponse({"short_url": short_url}, status=201)
645+
646+
647+
class UrlShortenerRetrieve(View):
648+
def get(self, _request: HttpRequest, id: str):
649+
try:
650+
short = UrlShortener.objects.get(id=id)
651+
except UrlShortener.DoesNotExist:
652+
return JsonResponse({"error": "Not found."}, status=404)
653+
654+
return JsonResponse({"long_url": short.url}, status=200)

tests/unit/webgis/test_views.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest
44

55
from georama.webgis.models import PublishedAsLayerWms
6+
from django.conf import settings
67

78
pytestmark = pytest.mark.django_db
89

@@ -37,3 +38,33 @@ def test_publish_as_wms_view(
3738
assert response.status_code == 200
3839
assert b"TestPointLayer" in response.content
3940
assert PublishedAsLayerWms.objects.filter(title="TestPointLayer").exists()
41+
42+
43+
class TestShortUrlViews:
44+
def test_shorten_url_create_and_get(self, client):
45+
url = f"{settings.WEBGISURL}/1234"
46+
response_create = client.post("/webgis/short/create", data={"url": url})
47+
48+
assert response_create.status_code == 201, response_create.json()
49+
payload_create = response_create.json()
50+
assert "short_url" in payload_create
51+
52+
short_id = payload_create["short_url"].rstrip("/").split("/")[-1]
53+
response_get = client.get(f"/webgis/short/get/{short_id}")
54+
55+
assert response_get.status_code == 200, response_create.json()
56+
57+
payload_get = response_get.json()
58+
assert payload_get["long_url"] == url
59+
60+
def test_shorten_url_requires_webgis_url(self, client):
61+
response = client.post("/webgis/short/create", data={"url": "https://example.org/foobar"})
62+
63+
assert response.status_code == 400
64+
assert response.json()["error"] == "Invalid URL."
65+
66+
def test_get_long_url_not_found(self, client):
67+
response = client.get("/webgis/short/get/does-not-exist")
68+
69+
assert response.status_code == 404
70+
assert response.json()["error"] == "Not found."

0 commit comments

Comments
 (0)