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
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,22 @@ def stage_variables(self) -> dict[str, str]:

@property
def path(self) -> str:
"""The request path, without the stage when `rawPath` starts with it.

A named stage is part of `rawPath` when the request comes through the execute-api endpoint,
but not through a custom domain API mapping or SAM local.
"""
raw_path = self.raw_path
stage = self.request_context.stage
if stage != "$default":
return self.raw_path[len("/" + stage) :]
return self.raw_path
if stage == "$default":
return raw_path

stage_prefix = f"/{stage}"
if raw_path == stage_prefix:
return "/"
if raw_path.startswith(f"{stage_prefix}/"):
return raw_path[len(stage_prefix) :]
return raw_path

@property
def http_method(self) -> str:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1378,6 +1378,41 @@ def foo():
assert result["headers"]["Content-Type"] == content_types.APPLICATION_JSON


@pytest.mark.parametrize(
"stage, raw_path, route, expected_path",
[
pytest.param("dev", "/dev/my/path", "/my/path", "/my/path", id="named stage in raw path"),
pytest.param("dev", "/my/path", "/my/path", "/my/path", id="named stage not in raw path"),
pytest.param("$default", "/my/path", "/my/path", "/my/path", id="default stage"),
pytest.param("dev", "/dev", "/", "/", id="raw path equals the stage"),
pytest.param("dev", "/dev/", "/", "/", id="raw path equals the stage with trailing slash"),
pytest.param("dev", "/devices", "/devices", "/devices", id="raw path starts with the stage name"),
pytest.param("dev", "/dev/devices", "/devices", "/devices", id="named stage and path starting with its name"),
],
)
def test_api_gateway_v2_stage_prefix_in_raw_path(stage, raw_path, route, expected_path):
# GIVEN an HTTP API resolver with a single route
app = APIGatewayHttpResolver()

@app.get(route)
def handler():
return {"path": app.current_event.path}

# AND a request for that stage. The execute-api endpoint puts a named stage in rawPath,
# while custom domain API mappings and SAM local send rawPath without it
event = load_event("apiGatewayProxyV2Event.json")
event["requestContext"]["stage"] = stage
event["requestContext"]["http"]["method"] = "GET"
event["rawPath"] = event["requestContext"]["http"]["path"] = raw_path

# WHEN calling the event handler
result = app(event, {})

# THEN the stage is stripped only when rawPath starts with it, and the route is found
assert result["statusCode"] == 200
assert json.loads(result["body"]) == {"path": expected_path}


def test_api_gateway_request_path_equals_strip_prefix():
# GIVEN a strip_prefix matches the request path
app = ApiGatewayResolver(strip_prefixes=["/foo"])
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import pytest

from aws_lambda_powertools.utilities.data_classes import (
APIGatewayProxyEvent,
APIGatewayProxyEventV2,
Expand Down Expand Up @@ -200,6 +202,29 @@ def test_api_gateway_proxy_v2_event():
assert parsed_event.stage_variables == raw_event["stageVariables"]


@pytest.mark.parametrize(
"stage, raw_path, expected_path",
[
pytest.param("$default", "/my/path", "/my/path", id="default stage"),
pytest.param("dev", "/dev/my/path", "/my/path", id="named stage in raw path"),
pytest.param("dev", "/my/path", "/my/path", id="named stage not in raw path"),
pytest.param("dev", "/dev", "/", id="raw path equals the stage"),
pytest.param("dev", "/dev/", "/", id="raw path equals the stage with trailing slash"),
pytest.param("dev", "/devices", "/devices", id="raw path starts with the stage name"),
pytest.param("dev", "/dev/dev", "/dev", id="stage is stripped once"),
],
)
def test_api_gateway_proxy_v2_event_path_strips_stage_only_when_present(stage, raw_path, expected_path):
raw_event = load_event("apiGatewayProxyV2Event.json")
raw_event["requestContext"]["stage"] = stage
raw_event["rawPath"] = raw_path

parsed_event = APIGatewayProxyEventV2(raw_event)

assert parsed_event.path == expected_path
assert parsed_event.raw_path == raw_path


def test_api_gateway_proxy_v2_lambda_authorizer_event():
raw_event = load_event("apiGatewayProxyV2LambdaAuthorizerEvent.json")
parsed_event = APIGatewayProxyEventV2(raw_event)
Expand Down
Loading