From 46a782319d6ef0e92333652fbe71e4c8a6f46498 Mon Sep 17 00:00:00 2001 From: ajaymahadeven Date: Wed, 8 Jul 2026 13:20:08 +0100 Subject: [PATCH 1/3] feat: add QUERY HTTP method support per RFC 10008 Adds HttpMethod.QUERY to the HttpMethod enum so Azure Functions HTTP triggers can declare and handle the new QUERY method (RFC 10008), which provides a safe, read-only alternative to POST for requests requiring a body. --- azure/functions/decorators/http.py | 1 + tests/decorators/test_http.py | 2 +- tests/test_http_asgi.py | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/azure/functions/decorators/http.py b/azure/functions/decorators/http.py index 3112efc6..6ceccfc4 100644 --- a/azure/functions/decorators/http.py +++ b/azure/functions/decorators/http.py @@ -16,6 +16,7 @@ class HttpMethod(StringifyEnum): PATCH = "PATCH" PUT = "PUT" OPTIONS = "OPTIONS" + QUERY = "QUERY" class HttpTrigger(Trigger): diff --git a/tests/decorators/test_http.py b/tests/decorators/test_http.py index c1edb443..329b42ba 100644 --- a/tests/decorators/test_http.py +++ b/tests/decorators/test_http.py @@ -14,7 +14,7 @@ def test_http_method_enum(self): self.assertEqual([e for e in HttpMethod], [HttpMethod.GET, HttpMethod.POST, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.PATCH, HttpMethod.PUT, - HttpMethod.OPTIONS]) + HttpMethod.OPTIONS, HttpMethod.QUERY]) def test_http_trigger_valid_creation_with_methods(self): http_trigger = HttpTrigger(name='req', diff --git a/tests/test_http_asgi.py b/tests/test_http_asgi.py index 3d95fd2f..eca93e21 100644 --- a/tests/test_http_asgi.py +++ b/tests/test_http_asgi.py @@ -69,7 +69,8 @@ async def __call__(self, scope, receive, send): assert scope['http_version'] in ['1.0', '1.1', '2'] assert isinstance(scope['http_version'], str) - assert scope['method'] in ['POST', 'GET', 'PUT', 'DELETE', 'PATCH'] + assert scope['method'] in ['POST', 'GET', 'PUT', 'DELETE', 'PATCH', + 'QUERY'] assert isinstance(scope['method'], str) assert scope['scheme'] in ['http', 'https'] From 01110739f91afc34deedecec86723464c60a360e Mon Sep 17 00:00:00 2001 From: ajaymahadeven Date: Wed, 8 Jul 2026 13:30:04 +0100 Subject: [PATCH 2/3] test: add QUERY method coverage for converter decode and HttpTrigger Adds two tests: one verifying HttpRequestConverter correctly passes through a QUERY request with a body, and one verifying HttpTrigger accepts HttpMethod.QUERY in its methods binding. --- tests/decorators/test_function_app.py | 30 +++++++++++++++++++++++++++ tests/test_http.py | 17 +++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/tests/decorators/test_function_app.py b/tests/decorators/test_function_app.py index 4617e691..b5b1a1ac 100644 --- a/tests/decorators/test_function_app.py +++ b/tests/decorators/test_function_app.py @@ -62,6 +62,36 @@ def test_add_trigger(self): f"is {trigger1} and New trigger " f"being added is {trigger2}") + def test_function_creation_with_query_method_trigger(self): + output = HttpOutput(name="out", data_type=DataType.UNDEFINED) + trigger = HttpTrigger(name="req", + methods=(HttpMethod.QUERY,), + data_type=DataType.UNDEFINED, + auth_level=AuthLevel.ANONYMOUS, route="dummy") + self.func.add_binding(output) + self.func.add_trigger(trigger) + + assert_json(self, self.func, {"scriptFile": "dummy.py", + "bindings": [ + { + "type": HTTP_OUTPUT, + "direction": + BindingDirection.OUT, + "name": "out", + "dataType": DataType.UNDEFINED + }, + { + "authLevel": AuthLevel.ANONYMOUS, + "type": HTTP_TRIGGER, + "direction": BindingDirection.IN, + "name": "req", + "dataType": DataType.UNDEFINED, + "route": "dummy", + "methods": [HttpMethod.QUERY] + } + ] + }) + def test_function_creation_with_binding_and_trigger(self): output = HttpOutput(name="out", data_type=DataType.UNDEFINED) trigger = HttpTrigger(name="req", diff --git a/tests/test_http.py b/tests/test_http.py index 1efa82bd..a923b358 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -211,6 +211,23 @@ def test_http_request_converter_decode(self): "dummy_params_key": "dummy_params_value"})) self.assertEqual(http_request.get_body(), b"test_body") + def test_http_request_converter_decode_query_method(self): + data = { + "method": Datum("QUERY", "string"), + "url": Datum("www.dummy.com/products", "string"), + "headers": {'Content-Type': Datum("application/json", "string")}, + "query": {}, + "params": {}, + "body": Datum('{"category": "laptops"}', "string") + } + datum = Datum(data, "http") + + http_request = http.HttpRequestConverter.decode( + data=datum, trigger_metadata={}) + + self.assertEqual(http_request.method, "QUERY") + self.assertEqual(http_request.get_body(), b'{"category": "laptops"}') + def test_http_with_bytes_data(self): data = ( b"--foo\r\n" From a81ecafaf6936301715531a5aa869798c895bbad Mon Sep 17 00:00:00 2001 From: ajaymahadeven Date: Thu, 16 Jul 2026 23:02:23 +0100 Subject: [PATCH 3/3] fix: update ASGI/WSGI test assertions to include QUERY method The _test_http_external_app helper hardcoded the methods list for ASGI/WSGI catch-all routes. Since these routes register all HttpMethod enum members, adding QUERY to the enum caused the assertions to fail. --- tests/decorators/test_function_app.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/decorators/test_function_app.py b/tests/decorators/test_function_app.py index b5b1a1ac..699cde1e 100644 --- a/tests/decorators/test_function_app.py +++ b/tests/decorators/test_function_app.py @@ -1012,11 +1012,12 @@ def _test_http_external_app(self, app, is_async, function_name): '{"direction": "IN", "type": "httpTrigger", ' '"authLevel": "FUNCTION", "route": "/{*route}", ' '"methods": ["GET", "POST", "DELETE", "HEAD", ' - '"PATCH", "PUT", "OPTIONS"], "name": "req"}')) + '"PATCH", "PUT", "OPTIONS", "QUERY"], ' + '"name": "req"}')) self.assertEqual(json.loads(raw_output_binding), json.loads( '{"direction": "IN", "type": "httpTrigger", "authLevel": ' '"FUNCTION", "methods": ["GET", "POST", "DELETE", "HEAD", ' - '"PATCH", "PUT", "OPTIONS"], "name": "req", "route": "/{' + '"PATCH", "PUT", "OPTIONS", "QUERY"], "name": "req", "route": "/{' '*route}"}')) self.assertEqual(func.get_bindings_dict(), { "bindings": [ @@ -1027,7 +1028,8 @@ def _test_http_external_app(self, app, is_async, function_name): HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.PATCH, - HttpMethod.PUT, HttpMethod.OPTIONS], + HttpMethod.PUT, HttpMethod.OPTIONS, + HttpMethod.QUERY], "name": "req", "route": "/{*route}", "type": HTTP_TRIGGER