Skip to content

Commit 9e672c5

Browse files
committed
Merge remote-tracking branch 'origin/develop' into feat-324/stac-view-of-prip-products_julien
2 parents d6d02ca + a177b71 commit 9e672c5

File tree

12 files changed

+5858
-4955
lines changed

12 files changed

+5858
-4955
lines changed

.github/workflows/check-code-quality.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ jobs:
266266
sed -i 's|<source>[^<]*</source>|<source>.</source>|g' ./cov-report.xml
267267
268268
- name: Run SonarCloud Scanner
269-
uses: SonarSource/sonarqube-scan-action@v5
269+
uses: SonarSource/sonarqube-scan-action@v6
270270
with:
271271
# See doc:
272272
# https://docs.sonarsource.com/sonarqube/9.9/analyzing-source-code/languages/python/

poetry.lock

Lines changed: 1073 additions & 960 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

services/adgs/poetry.lock

Lines changed: 909 additions & 789 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

services/cadip/poetry.lock

Lines changed: 909 additions & 789 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

services/catalog/poetry.lock

Lines changed: 936 additions & 816 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

services/catalog/rs_server_catalog/app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from rs_server_common.middlewares import (
3636
AuthenticationMiddleware,
3737
HandleExceptionsMiddleware,
38+
PaginationLinksMiddleware,
3839
apply_middlewares,
3940
insert_middleware_after,
4041
)
@@ -126,6 +127,7 @@ async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -
126127
if common_settings.CLUSTER_MODE:
127128
app = apply_middlewares(app)
128129

130+
app.add_middleware(PaginationLinksMiddleware)
129131

130132
logger.debug(f"Middlewares: {app.user_middleware}")
131133

services/common/poetry.lock

Lines changed: 843 additions & 736 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

services/common/rs_server_common/fastapi_app.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from rs_server_common.authentication.oauth2 import AUTH_PREFIX
3131
from rs_server_common.middlewares import (
3232
HandleExceptionsMiddleware,
33+
PaginationLinksMiddleware,
3334
StacLinksTitleMiddleware,
3435
)
3536
from rs_server_common.schemas.health_schema import HealthSchema
@@ -224,6 +225,10 @@ async def patched_landing_page(self, request, **kwargs):
224225

225226
# This middleware allows to have consistant http/https protocol in stac links
226227
app.add_middleware(ProxyHeaderMiddleware)
228+
229+
# Middleware for implementing first and last buttons in STAC Browser
230+
app.add_middleware(PaginationLinksMiddleware)
231+
227232
app.add_middleware(StacLinksTitleMiddleware, title="My STAC Title")
228233
# Add CORS requests from the STAC browser
229234
if settings.CORS_ORIGINS:

services/common/rs_server_common/middlewares.py

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
import os
1818
import traceback
1919
from collections.abc import Callable
20-
from typing import ParamSpec, TypedDict
20+
from typing import Any, ParamSpec, TypedDict
2121
from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse
2222

23+
import brotli
2324
from fastapi import FastAPI, Request, Response, status
2425
from fastapi.responses import JSONResponse
2526
from rs_server_common import settings as common_settings
@@ -131,6 +132,111 @@ def is_bad_request(self, request: Request, e: Exception) -> bool:
131132
)
132133

133134

135+
class PaginationLinksMiddleware(BaseHTTPMiddleware):
136+
"""
137+
Middleware to implement 'first' button's functionality in STAC Browser
138+
"""
139+
140+
async def dispatch(
141+
self,
142+
request: Request,
143+
call_next: Callable,
144+
): # pylint: disable=too-many-branches,too-many-statements
145+
146+
# Only for /search in auxip, prip, cadip
147+
if request.url.path in ["/auxip/search", "/cadip/search", "/prip/search", "/catalog/search"]:
148+
149+
first_link: dict[str, Any] = {
150+
"rel": "first",
151+
"type": "application/geo+json",
152+
"method": request.method,
153+
"href": f"{str(request.base_url).rstrip('/')}{request.url.path}",
154+
"title": "First link",
155+
}
156+
157+
if common_settings.CLUSTER_MODE:
158+
first_link["href"] = f"https://{str(request.base_url.hostname).rstrip('/')}{request.url.path}"
159+
160+
if request.method == "GET":
161+
# parse query params to remove any 'prev' or 'next'
162+
query_dict = dict(request.query_params)
163+
164+
query_dict.pop("token", None)
165+
if "page" in query_dict:
166+
query_dict["page"] = "1"
167+
new_query_string = urlencode(query_dict, doseq=True)
168+
first_link["href"] += f"?{new_query_string}"
169+
170+
elif request.method == "POST":
171+
try:
172+
query = await request.json()
173+
body = {}
174+
175+
for key in ["datetime", "limit"]:
176+
if key in query and query[key] is not None:
177+
body[key] = query[key]
178+
179+
if "token" in query and request.url.path != "/catalog/search":
180+
body["token"] = "page=1" # nosec
181+
182+
first_link["body"] = body
183+
except Exception: # pylint: disable = broad-exception-caught
184+
logger.error(traceback.format_exc())
185+
186+
response = await call_next(request)
187+
188+
encoding = response.headers.get("content-encoding", "")
189+
if encoding == "br":
190+
body_bytes = b"".join([section async for section in response.body_iterator])
191+
response_body = brotli.decompress(body_bytes)
192+
193+
if request.url.path == "/catalog/search":
194+
first_link["auth:refs"] = ["apikey", "openid", "oauth2"]
195+
else:
196+
response_body = b""
197+
async for chunk in response.body_iterator:
198+
response_body += chunk
199+
200+
try:
201+
data = json.loads(response_body)
202+
203+
links = data.get("links", [])
204+
has_prev = any(link.get("rel") == "previous" for link in links)
205+
206+
if has_prev is True:
207+
links.append(first_link)
208+
data["links"] = links
209+
210+
headers = dict(response.headers)
211+
headers.pop("content-length", None)
212+
213+
if encoding == "br":
214+
new_body = brotli.compress(json.dumps(data).encode("utf-8"))
215+
else:
216+
new_body = json.dumps(data).encode("utf-8")
217+
218+
response = Response(
219+
content=new_body,
220+
status_code=response.status_code,
221+
headers=headers,
222+
media_type="application/json",
223+
)
224+
except Exception: # pylint: disable = broad-exception-caught
225+
headers = dict(response.headers)
226+
headers.pop("content-length", None)
227+
228+
response = Response(
229+
content=response_body,
230+
status_code=response.status_code,
231+
headers=headers,
232+
media_type=response.headers.get("content-type"),
233+
)
234+
else:
235+
return await call_next(request)
236+
237+
return response
238+
239+
134240
def get_link_title(link: dict, entity: dict) -> str:
135241
"""
136242
Determine a human-readable STAC link title based on the link relation and context.

0 commit comments

Comments
 (0)