From b72acb3febdebf85a0b295313cab992c4f214f02 Mon Sep 17 00:00:00 2001 From: Navin Ayer Date: Wed, 29 Oct 2025 16:12:13 +0545 Subject: [PATCH 1/2] docs(backend): basic api example --- examples/mapswipe-backend-api/.gitignore | 1 + examples/mapswipe-backend-api/README.md | 13 ++ examples/mapswipe-backend-api/run.py | 276 +++++++++++++++++++++++ examples/mapswipe-backend-api/sample.env | 17 ++ 4 files changed, 307 insertions(+) create mode 100644 examples/mapswipe-backend-api/.gitignore create mode 100644 examples/mapswipe-backend-api/README.md create mode 100644 examples/mapswipe-backend-api/run.py create mode 100644 examples/mapswipe-backend-api/sample.env diff --git a/examples/mapswipe-backend-api/.gitignore b/examples/mapswipe-backend-api/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/examples/mapswipe-backend-api/.gitignore @@ -0,0 +1 @@ +.env diff --git a/examples/mapswipe-backend-api/README.md b/examples/mapswipe-backend-api/README.md new file mode 100644 index 0000000..0f89672 --- /dev/null +++ b/examples/mapswipe-backend-api/README.md @@ -0,0 +1,13 @@ + +> [!CAUTION] +> This script provides a simple way to interact with the MapSwipe backend endpoint.\ +> Future updates may introduce breaking changes. + + +Copy `sample.env` as `.env` and change the required variables + +Run the example script using uv +``` +uv run run.py +``` +> NOTE: To install uv https://docs.astral.sh/uv/getting-started/installation/ diff --git a/examples/mapswipe-backend-api/run.py b/examples/mapswipe-backend-api/run.py new file mode 100644 index 0000000..f0f98a7 --- /dev/null +++ b/examples/mapswipe-backend-api/run.py @@ -0,0 +1,276 @@ +# /// script +# dependencies = [ +# "httpx", +# "python-dotenv", +# ] +# /// + +# NOTE: Please read ./README.md + +import httpx +import logging +from dotenv import dotenv_values + +logger = logging.getLogger(__name__) +config = dotenv_values(".env") + + +# Define the GraphQL query +class Query: + + ME = """ + query MyQuery { + me { + id + displayName + } + } + """ + + PUBLIC_PROJECTS = """ + query MyQuery($filters: ProjectFilter = {}) { + publicProjects(filters: $filters) { + totalCount + results { + id + firebaseId + name + + exportAggregatedResults { + id + file { + url + } + } + exportUsers { + id + file { + url + } + } + exportTasks { + id + file { + url + } + } + exportResults { + id + file { + url + } + } + exportModerateToHighAgreementYesMaybeGeometries { + id + file { + url + } + } + exportHotTaskingManagerGeometries { + id + file { + url + } + } + exportHistory { + id + file { + url + } + } + exportGroups { + id + file { + url + } + } + exportAreaOfInterest { + id + file { + url + } + } + exportAggregatedResultsWithGeometry { + id + file { + url + } + } + + } + } + } + """ + + PROJECTS = """ + query MyQuery { + projects { + totalCount + results { + id + firebaseId + name + + exportAggregatedResults { + id + file { + url + } + } + exportUsers { + id + file { + url + } + } + exportTasks { + id + file { + url + } + } + exportResults { + id + file { + url + } + } + exportModerateToHighAgreementYesMaybeGeometries { + id + file { + url + } + } + exportHotTaskingManagerGeometries { + id + file { + url + } + } + exportHistory { + id + file { + url + } + } + exportGroups { + id + file { + url + } + } + exportAreaOfInterest { + id + file { + url + } + } + exportAggregatedResultsWithGeometry { + id + file { + url + } + } + + } + } + } + """ + +class MapswipeApi: + # Set the base URL + BASE_URL = config["BACKEND_URL"] + CSRFTOKEN_KEY = config["CSRFTOKEN_KEY"] + MANAGER_URL = config["MANAGER_URL"] + + ENABLE_AUTHENTICATION = config.get("ENABLE_AUTHENTICATION", "false").lower() == "true" + FB_AUTH_URL = config.get("FB_AUTH_URL") + + # Your web-app login credential + FB_USERNAME = config.get("FB_USERNAME") + FB_PASSWORD = config.get("FB_PASSWORD") + + def __enter__(self): + self.client = httpx.Client(base_url=self.BASE_URL, timeout=10.0) + + # For CSRF + health_resp = self.client.get("/health-check/") + health_resp.raise_for_status() + + if self.ENABLE_AUTHENTICATION: + self.login_with_firebaes() + + csrf_token = self.client.cookies.get(self.CSRFTOKEN_KEY) + self.headers = { + "content-type": "application/json", + # Required for CSRF verification + "x-csrftoken": csrf_token, + "origin": self.MANAGER_URL, + } + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.client.close() + return False # If True, suppresses exceptions + + def login_with_firebaes(self): + logger.info("Logging using firebase auth") + resp = httpx.post( + self.FB_AUTH_URL, + headers={ + "origin": self.MANAGER_URL, + }, + json={ + "returnSecureToken": True, + "email": self.FB_USERNAME, + "password": self.FB_PASSWORD, + "clientType": "CLIENT_TYPE_WEB", + }, + ) + resp.raise_for_status() + + idToken = resp.json()["idToken"] + + resp = self.client.post( + "/firebase-auth/", + json={ + "token": idToken, + }, + ) + resp.raise_for_status() + + def graphql_request(self, query, variables = None): + graphql_resp = self.client.post( + "/graphql/", + headers=self.headers, + json={ + "query": query, + "variables": variables, + }, + ) + + graphql_resp.raise_for_status() + + return graphql_resp.json() + + +with MapswipeApi() as api: + print('Public endpoints') + + print( + api.graphql_request( + Query.PUBLIC_PROJECTS, + variables={ + "filters": { + "status": { + "exact": "FINISHED", + } + } + }, + ) + ) + + print('Private endpoints') + print(api.graphql_request(Query.ME)) + + print(api.graphql_request(Query.PROJECTS)) diff --git a/examples/mapswipe-backend-api/sample.env b/examples/mapswipe-backend-api/sample.env new file mode 100644 index 0000000..3e728fe --- /dev/null +++ b/examples/mapswipe-backend-api/sample.env @@ -0,0 +1,17 @@ +BACKEND_URL=https://backend-2.mapswipe.dev.togglecorp.com +CSRFTOKEN_KEY=MAPSWIPE-ALPHA-2-CSRFTOKEN +MANAGER_URL=https://manager-2.mapswipe.dev.togglecorp.com + +ENABLE_AUTHENTICATION=false + +# XXX: For the key, go to the managers dashboard login page, open th network tab, after login you can see the key in the network tab +FB_AUTH_URL=https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +# Your web-app login credential +FB_USERNAME=me@example.com +FB_PASSWORD=my-very-good-password + +## Production +# BACKEND_URL=https://backend.mapswipe.org +# CSRFTOKEN_KEY=MAPSWIPE-PROD-CSRFTOKEN +# MANAGER_URL=https://managers.mapswipe.org From 73e096beb75fb41d2dd02254a21062af273831fc Mon Sep 17 00:00:00 2001 From: thenav56 Date: Wed, 5 Nov 2025 11:52:26 +0545 Subject: [PATCH 2/2] docs(backend): include draft project creation and file upload examples --- examples/mapswipe-backend-api/run.py | 275 ++++++++++++++++-- .../mapswipe-backend-api/sample_image.png | Bin 0 -> 21136 bytes 2 files changed, 255 insertions(+), 20 deletions(-) create mode 100644 examples/mapswipe-backend-api/sample_image.png diff --git a/examples/mapswipe-backend-api/run.py b/examples/mapswipe-backend-api/run.py index f0f98a7..2e8c1d3 100644 --- a/examples/mapswipe-backend-api/run.py +++ b/examples/mapswipe-backend-api/run.py @@ -2,24 +2,54 @@ # dependencies = [ # "httpx", # "python-dotenv", +# "python-ulid>=3.0.0", +# "typing-extensions", +# "colorlog", # ] # /// # NOTE: Please read ./README.md +import json +import typing import httpx import logging +import colorlog from dotenv import dotenv_values +from ulid import ULID -logger = logging.getLogger(__name__) config = dotenv_values(".env") +def logging_init(): + handler = colorlog.StreamHandler() + handler.setFormatter( + colorlog.ColoredFormatter( + "%(log_color)s[%(levelname)s]%(reset)s %(message)s", + log_colors={ + "DEBUG": "cyan", + "INFO": "green", + "WARNING": "yellow", + "ERROR": "red", + "CRITICAL": "bold_red", + }, + ) + ) + + logger = colorlog.getLogger() + logger.addHandler(handler) + logger.setLevel(logging.INFO) + return logger + + +logger = logging_init() + + # Define the GraphQL query class Query: - + ME_OP_NAME = "Me" ME = """ - query MyQuery { + query Me { me { id displayName @@ -27,8 +57,9 @@ class Query: } """ + PUBLIC_PROJECTS_OP_NAME = "PublicProjectsList" PUBLIC_PROJECTS = """ - query MyQuery($filters: ProjectFilter = {}) { + query PublicProjectsList($filters: ProjectFilter = {}) { publicProjects(filters: $filters) { totalCount results { @@ -102,8 +133,9 @@ class Query: } """ + PROJECTS_OP_NAME = "ProjectsList" PROJECTS = """ - query MyQuery { + query ProjectsList { projects { totalCount results { @@ -177,13 +209,73 @@ class Query: } """ -class MapswipeApi: + ORGANIZATIONS_OP_NAME = "Organizations" + ORGANIZATIONS = """ + query Organizations { + organizations { + totalCount + results { + id + name + } + } + } + """ + + CREATE_DRAFT_PROJECTS_OP_NAME = "NewDraftProject" + CREATE_DRAFT_PROJECTS = """ + mutation NewDraftProject($data: ProjectCreateInput!) { + createProject(data: $data) { + ... on OperationInfo { + __typename + messages { + code + field + kind + message + } + } + ... on ProjectTypeMutationResponseType { + errors + ok + result { + id + firebaseId + } + } + } + } + """ + + CREATE_PROJECT_ASSET_OP_NAME = "CreateProjectAsset" + CREATE_PROJECT_ASSET = """ + mutation CreateProjectAsset($data: ProjectAssetCreateInput!) { + createProjectAsset(data: $data) { + ... on ProjectAssetTypeMutationResponseType { + errors + ok + result { + id + file { + name + url + } + } + } + } + } + """ + + +class MapSwipeApiClient: # Set the base URL BASE_URL = config["BACKEND_URL"] CSRFTOKEN_KEY = config["CSRFTOKEN_KEY"] MANAGER_URL = config["MANAGER_URL"] - ENABLE_AUTHENTICATION = config.get("ENABLE_AUTHENTICATION", "false").lower() == "true" + ENABLE_AUTHENTICATION = ( + config.get("ENABLE_AUTHENTICATION", "false").lower() == "true" + ) FB_AUTH_URL = config.get("FB_AUTH_URL") # Your web-app login credential @@ -202,7 +294,6 @@ def __enter__(self): csrf_token = self.client.cookies.get(self.CSRFTOKEN_KEY) self.headers = { - "content-type": "application/json", # Required for CSRF verification "x-csrftoken": csrf_token, "origin": self.MANAGER_URL, @@ -239,26 +330,123 @@ def login_with_firebaes(self): ) resp.raise_for_status() - def graphql_request(self, query, variables = None): + def graphql_request_with_files( + self, + operation_name: str, + query: str, + *, + files: dict[typing.Any, typing.Any], + map: dict[typing.Any, typing.Any], + variables: dict[typing.Any, typing.Any] | None = None, + ): + # Request type: form data graphql_resp = self.client.post( "/graphql/", headers=self.headers, - json={ - "query": query, - "variables": variables, + files=files, + data={ + "operations": json.dumps( + { + "query": query, + "variables": variables, + }, + ), + "map": json.dumps(map), }, ) + if not (200 <= graphql_resp.status_code < 300): + logger.error("Error: %s", graphql_resp.text) graphql_resp.raise_for_status() return graphql_resp.json() + def graphql_request( + self, + operation_name: str, + query: str, + variables: dict[typing.Any, typing.Any] | None = None, + ): + payload = { + "operationName": operation_name, + "query": query, + "variables": variables, + } + + graphql_resp = self.client.post( + "/graphql/", + headers=self.headers, + json=payload, + ) + + if not (200 <= graphql_resp.status_code < 300): + logger.error("Error: %s", graphql_resp.text) + graphql_resp.raise_for_status() + + return graphql_resp.json() -with MapswipeApi() as api: - print('Public endpoints') + def create_draft_project(self, params): + resp = self.graphql_request( + Query.CREATE_DRAFT_PROJECTS_OP_NAME, + Query.CREATE_DRAFT_PROJECTS, + {"data": params}, + ) + + if errors := resp.get("errors"): + logger.error("Failed to create new project: %s", errors) + return None + + if errors := resp["data"]["createProject"].get("messages"): + logger.error("Failed to create new project: %s", errors) + return None + + if errors := resp["data"]["createProject"].get("errors"): + logger.error("Failed to create new project: %s", errors) + return None + + return resp["data"]["createProject"]["result"]["id"] + + def create_project_asset( + self, + *, + project_file, + params, + ): + resp = self.graphql_request_with_files( + Query.CREATE_PROJECT_ASSET_OP_NAME, + Query.CREATE_PROJECT_ASSET, + files={ + "projectFile": project_file, + }, + map={ + "projectFile": ["variables.data.file"], + }, + variables={"data": params}, + ) - print( - api.graphql_request( + if errors := resp.get("errors"): + logger.error("Failed to create project asset: %s", errors) + return None + + if errors := resp["data"]["createProjectAsset"].get("messages"): + logger.error("Failed to create project asset: %s", errors) + return None + + if errors := resp["data"]["createProjectAsset"].get("errors"): + logger.error("Failed to create project asset: %s", errors) + return None + + return resp["data"]["createProjectAsset"]["result"]["id"] + + +with MapSwipeApiClient() as api_client: + logger.info("Public endpoints") + + logger.info( + "%s: %s", + Query.PUBLIC_PROJECTS_OP_NAME, + api_client.graphql_request( + Query.PUBLIC_PROJECTS_OP_NAME, Query.PUBLIC_PROJECTS, variables={ "filters": { @@ -267,10 +455,57 @@ def graphql_request(self, query, variables = None): } } }, - ) + ), + ) + + logger.info("Private endpoints") + me_info = api_client.graphql_request(Query.ME_OP_NAME, Query.ME)["data"]["me"] + if not me_info: + raise Exception("Not logged in.... :(") + logger.info("%s: %s", Query.ME_OP_NAME, me_info) + + organization_id = api_client.graphql_request( + Query.ORGANIZATIONS_OP_NAME, + Query.ORGANIZATIONS, + )["data"]["organizations"]["results"][0]["id"] + + logger.info( + "%s: %s", + Query.PUBLIC_PROJECTS_OP_NAME, + api_client.graphql_request(Query.PROJECTS_OP_NAME, Query.PROJECTS), ) - print('Private endpoints') - print(api.graphql_request(Query.ME)) + new_project_client_id = str(ULID()) + new_project_topic_name = "Test - Building Validation - 8" + + new_project_id = api_client.create_draft_project( + { + "clientId": new_project_client_id, + "projectType": "VALIDATE", + "region": "Nepal", + "topic": new_project_topic_name, + "description": "Validate building footprints", + "projectInstruction": "Validate building footprints", + "lookFor": "buildings", + "projectNumber": 1000, + "requestingOrganization": organization_id, + "additionalInfoUrl": "fair-dev.hotosm.org", + "team": None, + } + ) + assert new_project_id is not None + + logger.info("%s: %s", "Create Draft Project", new_project_id) + + with open("./sample_image.png", "rb") as image_file: + new_project_asset_client_id = str(ULID()) + new_project_asset = api_client.create_project_asset( + project_file=image_file, + params={ + "inputType": "COVER_IMAGE", + "clientId": new_project_asset_client_id, + "project": new_project_id, + }, + ) - print(api.graphql_request(Query.PROJECTS)) + logger.info("%s: %s", "Create Project Asset", new_project_asset) diff --git a/examples/mapswipe-backend-api/sample_image.png b/examples/mapswipe-backend-api/sample_image.png new file mode 100644 index 0000000000000000000000000000000000000000..b80767cc8775e054e5bfbdc2e7e27dea9a175669 GIT binary patch literal 21136 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 literal 0 HcmV?d00001