From 4c8c7bbf403de68208feb7189deee33600c4aff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20Horvat?= Date: Mon, 30 Sep 2024 13:23:04 +0200 Subject: [PATCH 1/7] Add trailing slash to the graphql URLs --- pontoon/api/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pontoon/api/urls.py b/pontoon/api/urls.py index bf48f67689..86755dcfd5 100644 --- a/pontoon/api/urls.py +++ b/pontoon/api/urls.py @@ -9,7 +9,7 @@ urlpatterns = [ # GraphQL endpoint. In DEV mode it serves the GraphiQL IDE if accessed with Accept: text/html path( - "graphql", + "graphql/", GraphQLView.as_view(schema=schema, graphiql=DEV), ), ] From b897015897e83f46031e999ce0d17825944e11ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20Horvat?= Date: Mon, 30 Sep 2024 13:33:05 +0200 Subject: [PATCH 2/7] Fix tests --- pontoon/api/tests/test_schema.py | 16 ++++++++-------- pontoon/api/tests/test_urls.py | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pontoon/api/tests/test_schema.py b/pontoon/api/tests/test_schema.py index b664a38177..b8403b2937 100644 --- a/pontoon/api/tests/test_schema.py +++ b/pontoon/api/tests/test_schema.py @@ -34,7 +34,7 @@ def test_projects(client): }""" } - response = client.get("/graphql", body, HTTP_ACCEPT="application/json") + response = client.get("/graphql/", body, HTTP_ACCEPT="application/json") ProjectFactory.create(visibility=Project.Visibility.PRIVATE) assert response.status_code == 200 @@ -111,7 +111,7 @@ def test_project_filters( } if is_admin: client.force_login(admin) - response = client.get("/graphql", body, HTTP_ACCEPT="application/json") + response = client.get("/graphql/", body, HTTP_ACCEPT="application/json") assert response.status_code == 200 assert response.json() == { @@ -143,7 +143,7 @@ def test_project_localizations(client): }""" } - response = client.get("/graphql", body, HTTP_ACCEPT="application/json") + response = client.get("/graphql/", body, HTTP_ACCEPT="application/json") assert response.status_code == 200 assert response.json() == { @@ -205,7 +205,7 @@ def test_localization_filters( if is_admin: client.force_login(admin) - response = client.get("/graphql", body, HTTP_ACCEPT="application/json") + response = client.get("/graphql/", body, HTTP_ACCEPT="application/json") assert response.status_code == 200 assert response.json() == { @@ -246,7 +246,7 @@ def test_projects_localizations_cyclic(client): }""" } - response = client.get("/graphql", body, HTTP_ACCEPT="application/json") + response = client.get("/graphql/", body, HTTP_ACCEPT="application/json") assert response.status_code == 200 assert b"Cyclic queries are forbidden" in response.content @@ -268,7 +268,7 @@ def test_project_localizations_cyclic(client): }""" } - response = client.get("/graphql", body, HTTP_ACCEPT="application/json") + response = client.get("/graphql/", body, HTTP_ACCEPT="application/json") assert response.status_code == 200 assert b"Cyclic queries are forbidden" in response.content @@ -290,7 +290,7 @@ def test_locales_localizations_cyclic(client): }""" } - response = client.get("/graphql", body, HTTP_ACCEPT="application/json") + response = client.get("/graphql/", body, HTTP_ACCEPT="application/json") assert response.status_code == 200 assert b"Cyclic queries are forbidden" in response.content @@ -312,7 +312,7 @@ def test_locale_localizations_cyclic(client): }""" } - response = client.get("/graphql", body, HTTP_ACCEPT="application/json") + response = client.get("/graphql/", body, HTTP_ACCEPT="application/json") assert response.status_code == 200 assert b"Cyclic queries are forbidden" in response.content diff --git a/pontoon/api/tests/test_urls.py b/pontoon/api/tests/test_urls.py index d931fb3d62..db207e6071 100644 --- a/pontoon/api/tests/test_urls.py +++ b/pontoon/api/tests/test_urls.py @@ -21,7 +21,7 @@ def projects_query(): def test_graphql_dev_get(settings, projects_query, client): settings.DEV = True - response = client.get("/graphql", projects_query, HTTP_ACCEPT="application/json") + response = client.get("/graphql/", projects_query, HTTP_ACCEPT="application/json") assert response.status_code == 200 @@ -29,7 +29,7 @@ def test_graphql_dev_get(settings, projects_query, client): def test_graphql_dev_post(settings, projects_query, client): settings.DEV = True - response = client.post("/graphql", projects_query, HTTP_ACCEPT="application/json") + response = client.post("/graphql/", projects_query, HTTP_ACCEPT="application/json") assert response.status_code == 200 @@ -38,7 +38,7 @@ def test_graphql_dev_post(settings, projects_query, client): def test_graphql_prod_get(settings, projects_query, client): settings.DEV = True - response = client.get("/graphql", projects_query, HTTP_ACCEPT="application/json") + response = client.get("/graphql/", projects_query, HTTP_ACCEPT="application/json") assert response.status_code == 200 @@ -47,7 +47,7 @@ def test_graphql_prod_get(settings, projects_query, client): def test_graphql_prod_post(settings, projects_query, client): settings.DEV = False - response = client.post("/graphql", projects_query, HTTP_ACCEPT="appication/json") + response = client.post("/graphql/", projects_query, HTTP_ACCEPT="appication/json") assert response.status_code == 200 @@ -55,14 +55,14 @@ def test_graphql_prod_post(settings, projects_query, client): def test_graphiql_dev_get(settings, projects_query, client): settings.DEV = True - response = client.get("/graphql", projects_query, HTTP_ACCEPT="text/html") + response = client.get("/graphql/", projects_query, HTTP_ACCEPT="text/html") assert response.status_code == 200 @pytest.mark.django_db def test_graphiql_dev_post(settings, projects_query, client): settings.DEV = True - response = client.post("/graphql", projects_query, HTTP_ACCEPT="text/html") + response = client.post("/graphql/", projects_query, HTTP_ACCEPT="text/html") assert response.status_code == 200 @@ -71,7 +71,7 @@ def test_graphiql_dev_post(settings, projects_query, client): def test_graphiql_prod_get(settings, projects_query, client): settings.DEV = False reload_urls(settings) - response = client.get("/graphql", projects_query, HTTP_ACCEPT="text/html") + response = client.get("/graphql/", projects_query, HTTP_ACCEPT="text/html") assert response.status_code == 400 @@ -80,5 +80,5 @@ def test_graphiql_prod_get(settings, projects_query, client): def test_graphiql_prod_post(projects_query, client, settings): settings.DEV = False reload_urls(settings) - response = client.post("/graphql", projects_query, HTTP_ACCEPT="text/html") + response = client.post("/graphql/", projects_query, HTTP_ACCEPT="text/html") assert response.status_code == 400 From 36c710793ff179da3c3ea2b7470b2180f30351a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20Horvat?= Date: Mon, 30 Sep 2024 15:58:42 +0200 Subject: [PATCH 3/7] Reflect the URL change in the docs --- pontoon/api/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pontoon/api/README.md b/pontoon/api/README.md index a6ac443306..171f737e62 100644 --- a/pontoon/api/README.md +++ b/pontoon/api/README.md @@ -1,7 +1,7 @@ # GraphQL API Pontoon exposes some of its data via a public API endpoint. The API is -[GraphQL](http://graphql.org/)-based and available at `/graphql`. +[GraphQL](http://graphql.org/)-based and available at `/graphql/`. ## Production Deployments @@ -12,13 +12,13 @@ the query must be escaped. An example GET requests may look like this: ```bash -$ curl --globoff https://example.com/graphql?query={projects{name}} +$ curl --globoff https://example.com/graphql/?query={projects{name}} ``` An example POST requests may look like this: ```bash -$ curl -X POST -d "query={ projects { name } }" https://example.com/graphql +$ curl -X POST -d "query={ projects { name } }" https://example.com/graphql/ ``` ## Local Development @@ -33,23 +33,23 @@ a production one, returning JSON responses. The following query in the CLI will return a JSON response: ```bash -$ curl --globoff http://localhost:8000/graphql?query={projects{name}} +$ curl --globoff http://localhost:8000/graphql/?query={projects{name}} ``` If however a request is sent with `Accept: text/html` such as is the case when accessing the endpoint in a browser, a GUI query editor and explorer, [GraphiQL](https://github.com/graphql/graphiql), will be served:: - http://localhost:8000/graphql?query={projects{name}} + http://localhost:8000/graphql/?query={projects{name}} To preview the JSON response in the browser, pass in the `raw` query argument:: - http://localhost:8000/graphql?query={projects{name}}&raw + http://localhost:8000/graphql/?query={projects{name}}&raw ## Query IDE The [GraphiQL](https://github.com/graphql/graphiql) query IDE is available at -`http://localhost:8000/graphql` when running Pontoon locally and the URL is +`http://localhost:8000/graphql/` when running Pontoon locally and the URL is accessed with the `Accept: text/html` header, e.g. using a browser. It offers a query editor with: From 9f4cc6f9b626c20dfacdf0e78f1fcaa8e028261f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20Horvat?= Date: Mon, 30 Sep 2024 16:02:28 +0200 Subject: [PATCH 4/7] Explicitly support URLs with or without trailing slash in order to support curl requests --- pontoon/api/urls.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pontoon/api/urls.py b/pontoon/api/urls.py index 86755dcfd5..e4b85a57c0 100644 --- a/pontoon/api/urls.py +++ b/pontoon/api/urls.py @@ -1,15 +1,16 @@ from graphene_django.views import GraphQLView -from django.urls import path +from django.urls import re_path from pontoon.api.schema import schema from pontoon.settings import DEV urlpatterns = [ - # GraphQL endpoint. In DEV mode it serves the GraphiQL IDE if accessed with Accept: text/html - path( - "graphql/", + # GraphQL endpoint. In DEV mode it serves the GraphiQL IDE if accessed with Accept: text/html. + # Explicitly support URLs with or without trailing slash in order to support curl requests. + re_path( + r"^graphql/?$", GraphQLView.as_view(schema=schema, graphiql=DEV), ), ] From 6b982f9086ac2ae301268f7f7b4e423dbd4419ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20Horvat?= Date: Mon, 30 Sep 2024 16:03:34 +0200 Subject: [PATCH 5/7] Fix typo in the tests Co-authored-by: Francesco Lodolo --- pontoon/api/tests/test_urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pontoon/api/tests/test_urls.py b/pontoon/api/tests/test_urls.py index db207e6071..c4dcad2b8a 100644 --- a/pontoon/api/tests/test_urls.py +++ b/pontoon/api/tests/test_urls.py @@ -47,7 +47,7 @@ def test_graphql_prod_get(settings, projects_query, client): def test_graphql_prod_post(settings, projects_query, client): settings.DEV = False - response = client.post("/graphql/", projects_query, HTTP_ACCEPT="appication/json") + response = client.post("/graphql/", projects_query, HTTP_ACCEPT="application/json") assert response.status_code == 200 From c4e9d81c22db5e6ca29799dcf9f6ae4d07d069ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20Horvat?= Date: Mon, 30 Sep 2024 16:15:53 +0200 Subject: [PATCH 6/7] Add quotes Co-authored-by: Francesco Lodolo --- pontoon/api/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pontoon/api/README.md b/pontoon/api/README.md index 171f737e62..7c7f063130 100644 --- a/pontoon/api/README.md +++ b/pontoon/api/README.md @@ -12,7 +12,7 @@ the query must be escaped. An example GET requests may look like this: ```bash -$ curl --globoff https://example.com/graphql/?query={projects{name}} +$ curl --globoff "https://example.com/graphql/?query={projects{name}}" ``` An example POST requests may look like this: From 598df38b2ad3b3f04a13bc47dcc67d28f33b10eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20Horvat?= Date: Mon, 30 Sep 2024 16:16:00 +0200 Subject: [PATCH 7/7] Add quotes Co-authored-by: Francesco Lodolo --- pontoon/api/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pontoon/api/README.md b/pontoon/api/README.md index 7c7f063130..254236d708 100644 --- a/pontoon/api/README.md +++ b/pontoon/api/README.md @@ -33,7 +33,7 @@ a production one, returning JSON responses. The following query in the CLI will return a JSON response: ```bash -$ curl --globoff http://localhost:8000/graphql/?query={projects{name}} +$ curl --globoff "http://localhost:8000/graphql/?query={projects{name}}" ``` If however a request is sent with `Accept: text/html` such as is the case when