Repository: frappe/frappe

Issue:
feat: allow setting a custom http status code for redirects
Requires user to set `redirect_http_status` in the hook or redirect doc

Resolves #23762



Discussion:
## [Codecov](https://app.codecov.io/gh/frappe/frappe/pull/23765?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=frappe) Report
Attention: `2 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`91dce0b`)](https://app.codecov.io/gh/frappe/frappe/commit/91dce0b00799f48b421fc4a15a9e93bc7cf94b1a?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=frappe) 61.85% compared to head [(`2224d86`)](https://app.codecov.io/gh/frappe/frappe/pull/23765?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=frappe) 61.85%.
> Report is 65 commits behind head on develop.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           develop   #23765      +/-   ##
===========================================
- Coverage    61.85%   61.85%   -0.01%     
===========================================
  Files          774      774              
  Lines        75760    75764       +4     
  Branches      6346     6346              
===========================================
- Hits         46864    46863       -1     
- Misses       25278    25283       +5     
  Partials      3618     3618              
```

| [Flag](https://app.codecov.io/gh/frappe/frappe/pull/23765/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=frappe) | Coverage Δ | |
|---|---|---|
| [server](https://app.codecov.io/gh/frappe/frappe/pull/23765/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=frappe) | `71.14% <83.33%> (-0.03%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=frappe#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>
:heavy_check_mark: Looks good to me!

Docs might need an update at https://frappeframework.com/docs/user/en/guides/portal-development/redirects
~~Still might be an issue when `Website Settings` doc is updated, I don't think its clearing the correct cache entries~~

~~Will add in some additional tests to confirm and fix it.~~

Nvm, misread some code. All good.

Resolving patch:
diff --git a/frappe/website/doctype/website_route_redirect/website_route_redirect.py b/frappe/website/doctype/website_route_redirect/website_route_redirect.py
--- a/frappe/website/doctype/website_route_redirect/website_route_redirect.py
+++ b/frappe/website/doctype/website_route_redirect/website_route_redirect.py
@@ -17,7 +17,7 @@ class WebsiteRouteRedirect(Document):
 		parent: DF.Data
 		parentfield: DF.Data
 		parenttype: DF.Data
-		redirect_http_status: DF.Int
+		redirect_http_status: DF.Literal["301", "302", "307", "308"]
 		source: DF.SmallText
 		target: DF.SmallText
 	# end: auto-generated types
diff --git a/frappe/website/path_resolver.py b/frappe/website/path_resolver.py
--- a/frappe/website/path_resolver.py
+++ b/frappe/website/path_resolver.py
@@ -135,7 +135,7 @@ def resolve_redirect(path, query_string=None):
 		if match:
 			redirect_to = re.sub(pattern, rule["target"], path_to_match)
 			frappe.flags.redirect_location = redirect_to
-			status_code = rule.get("redirect_http_status", 301)
+			status_code = rule.get("redirect_http_status") or 301
 			frappe.cache.hset(
 				"website_redirects", path_to_match, {"path": redirect_to, "status_code": status_code}
 			)


Test patch:
diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py
--- a/frappe/tests/test_website.py
+++ b/frappe/tests/test_website.py
@@ -180,7 +180,11 @@ def test_redirect(self):
 		website_settings = frappe.get_doc("Website Settings")
 		website_settings.append(
 			"route_redirects",
-			{"source": "/testsource", "target": "/testtarget", "redirect_http_status": 301},
+			{"source": "/testsource", "target": "/testtarget"},
+		)
+		website_settings.append(
+			"route_redirects",
+			{"source": "/testdoc307", "target": "/testtarget", "redirect_http_status": 307},
 		)
 		website_settings.save()
 
@@ -208,6 +212,11 @@ def test_redirect(self):
 		self.assertEqual(response.status_code, 301)
 		self.assertEqual(response.headers.get("Location"), "/testtarget")
 
+		set_request(method="GET", path="/testdoc307")
+		response = get_response()
+		self.assertEqual(response.status_code, 307)
+		self.assertEqual(response.headers.get("Location"), "/testtarget")
+
 		set_request(method="GET", path="/courses/course?course=data")
 		response = get_response()
 		self.assertEqual(response.status_code, 301)