diff --git a/docs/hub/account.md b/docs/hub/account.md index e23811c..7efd41a 100644 --- a/docs/hub/account.md +++ b/docs/hub/account.md @@ -4,7 +4,7 @@ Access with `norbix.hub.account`. Every method here needs only a key or a bearer token (scope `project`): the gateway takes the account from the signed-in session, or from the project id -in the path, and never reads the `X-CM-AccountId` header. Four routes are +in the path, and never reads the `norbix-account-id` header. Four routes are public on the gateway and need no token and no `account_id` at all (scope `unauthenticated`, no `Authorization` header is sent): sign-up (`create_account`), accepting an invitation diff --git a/src/norbix_python/transport.py b/src/norbix_python/transport.py index b9b3b7c..31af8e1 100644 --- a/src/norbix_python/transport.py +++ b/src/norbix_python/transport.py @@ -109,9 +109,9 @@ def send( if token: headers["Authorization"] = f"Bearer {token}" - headers["X-CM-ProjectId"] = self._cfg.project_id + headers["norbix-project-id"] = self._cfg.project_id if self._cfg.account_id: - headers["X-CM-AccountId"] = self._cfg.account_id + headers["norbix-account-id"] = self._cfg.account_id # Environment selector: per-call override wins over the client default. # "PROD" is the backend default, so the header is omitted for it. resolved_env = env if env is not None else self._cfg.env @@ -275,9 +275,9 @@ async def send( if token: headers["Authorization"] = f"Bearer {token}" - headers["X-CM-ProjectId"] = self._cfg.project_id + headers["norbix-project-id"] = self._cfg.project_id if self._cfg.account_id: - headers["X-CM-AccountId"] = self._cfg.account_id + headers["norbix-account-id"] = self._cfg.account_id # Environment selector: per-call override wins over the client default. # "PROD" is the backend default, so the header is omitted for it. resolved_env = env if env is not None else self._cfg.env diff --git a/tests/api/test_ai.py b/tests/api/test_ai.py index 4f03c27..f8d4548 100644 --- a/tests/api/test_ai.py +++ b/tests/api/test_ai.py @@ -29,7 +29,7 @@ def test_api_ai_get_end_user_chat_availability_request_shape() -> None: assert transport.last_request['method'] == 'GET' assert transport.last_request['url'].endswith('/v3/ai/chat/availability') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_list_end_user_chat_sessions_request_shape() -> None: @@ -39,7 +39,7 @@ def test_api_ai_list_end_user_chat_sessions_request_shape() -> None: assert transport.last_request['method'] == 'GET' assert transport.last_request['url'].endswith('/v3/ai/chat/sessions') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_create_end_user_chat_session_request_shape() -> None: @@ -49,7 +49,7 @@ def test_api_ai_create_end_user_chat_session_request_shape() -> None: assert transport.last_request['method'] == 'POST' assert transport.last_request['url'].endswith('/v3/ai/chat/sessions') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_get_end_user_chat_session_request_shape() -> None: @@ -59,7 +59,7 @@ def test_api_ai_get_end_user_chat_session_request_shape() -> None: assert transport.last_request['method'] == 'GET' assert transport.last_request['url'].endswith('/v3/ai/chat/sessions/stub-SessionId') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_rename_end_user_chat_session_request_shape() -> None: @@ -69,7 +69,7 @@ def test_api_ai_rename_end_user_chat_session_request_shape() -> None: assert transport.last_request['method'] == 'PATCH' assert transport.last_request['url'].endswith('/v3/ai/chat/sessions/stub-SessionId') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_delete_end_user_chat_session_request_shape() -> None: @@ -79,7 +79,7 @@ def test_api_ai_delete_end_user_chat_session_request_shape() -> None: assert transport.last_request['method'] == 'DELETE' assert transport.last_request['url'].endswith('/v3/ai/chat/sessions/stub-SessionId') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_pin_end_user_chat_session_request_shape() -> None: @@ -89,7 +89,7 @@ def test_api_ai_pin_end_user_chat_session_request_shape() -> None: assert transport.last_request['method'] == 'PUT' assert transport.last_request['url'].endswith('/v3/ai/chat/sessions/stub-SessionId/pin') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_archive_end_user_chat_session_request_shape() -> None: @@ -99,7 +99,7 @@ def test_api_ai_archive_end_user_chat_session_request_shape() -> None: assert transport.last_request['method'] == 'PUT' assert transport.last_request['url'].endswith('/v3/ai/chat/sessions/stub-SessionId/archive') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_get_end_user_chat_entries_request_shape() -> None: @@ -109,7 +109,7 @@ def test_api_ai_get_end_user_chat_entries_request_shape() -> None: assert transport.last_request['method'] == 'GET' assert transport.last_request['url'].endswith('/v3/ai/chat/sessions/stub-SessionId/entries') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_set_end_user_chat_entry_feedback_request_shape() -> None: @@ -119,7 +119,7 @@ def test_api_ai_set_end_user_chat_entry_feedback_request_shape() -> None: assert transport.last_request['method'] == 'PUT' assert transport.last_request['url'].endswith('/v3/ai/chat/sessions/stub-SessionId/entries/stub-EntryId/feedback') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_list_end_user_chat_attachments_request_shape() -> None: @@ -129,7 +129,7 @@ def test_api_ai_list_end_user_chat_attachments_request_shape() -> None: assert transport.last_request['method'] == 'GET' assert transport.last_request['url'].endswith('/v3/ai/chat/sessions/stub-SessionId/attachments') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_upload_end_user_chat_attachment_request_shape() -> None: @@ -139,7 +139,7 @@ def test_api_ai_upload_end_user_chat_attachment_request_shape() -> None: assert transport.last_request['method'] == 'POST' assert transport.last_request['url'].endswith('/v3/ai/chat/sessions/stub-SessionId/attachments') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_delete_end_user_chat_attachment_request_shape() -> None: @@ -149,7 +149,7 @@ def test_api_ai_delete_end_user_chat_attachment_request_shape() -> None: assert transport.last_request['method'] == 'DELETE' assert transport.last_request['url'].endswith('/v3/ai/chat/attachments/stub-AttachmentId') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_list_end_user_chat_memory_request_shape() -> None: @@ -159,7 +159,7 @@ def test_api_ai_list_end_user_chat_memory_request_shape() -> None: assert transport.last_request['method'] == 'GET' assert transport.last_request['url'].endswith('/v3/ai/chat/memory') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_forget_end_user_chat_memory_request_shape() -> None: @@ -169,7 +169,7 @@ def test_api_ai_forget_end_user_chat_memory_request_shape() -> None: assert transport.last_request['method'] == 'DELETE' assert transport.last_request['url'].endswith('/v3/ai/chat/memory/stub-NoteId') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_api_ai_start_end_user_chat_turn_request_shape() -> None: @@ -179,5 +179,5 @@ def test_api_ai_start_end_user_chat_turn_request_shape() -> None: assert transport.last_request['method'] == 'POST' assert transport.last_request['url'].endswith('/v3/ai/chat/turn') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' diff --git a/tests/hub/test_account.py b/tests/hub/test_account.py index a35adc7..3552aff 100644 --- a/tests/hub/test_account.py +++ b/tests/hub/test_account.py @@ -292,7 +292,7 @@ def test_hub_account_get_account_collaborators_needs_only_a_token() -> None: client.hub.account.get_account_collaborators() assert transport.last_request is not None assert transport.last_request['method'] == 'GET' - assert 'x-cm-accountid' not in transport.last_request['headers'] + assert 'norbix-account-id' not in transport.last_request['headers'] def test_hub_account_send_invite_to_team_member_request_shape() -> None: client, transport = make_client(account_id='acc-1') @@ -327,7 +327,7 @@ def test_hub_account_get_project_ai_settings_request_shape() -> None: assert transport.last_request['method'] == 'GET' assert transport.last_request['url'].endswith('/v3/account/projects/stub-projectId/ai/settings') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_account_update_project_ai_settings_request_shape() -> None: @@ -337,7 +337,7 @@ def test_hub_account_update_project_ai_settings_request_shape() -> None: assert transport.last_request['method'] == 'PUT' assert transport.last_request['url'].endswith('/v3/account/projects/stub-projectId/ai/settings') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_account_create_project_ai_assistant_request_shape() -> None: @@ -347,7 +347,7 @@ def test_hub_account_create_project_ai_assistant_request_shape() -> None: assert transport.last_request['method'] == 'POST' assert transport.last_request['url'].endswith('/v3/account/projects/stub-projectId/ai/assistants') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_account_update_project_ai_assistant_request_shape() -> None: @@ -357,7 +357,7 @@ def test_hub_account_update_project_ai_assistant_request_shape() -> None: assert transport.last_request['method'] == 'PUT' assert transport.last_request['url'].endswith('/v3/account/projects/stub-projectId/ai/assistants/stub-assistantId') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_account_delete_project_ai_assistant_request_shape() -> None: @@ -367,7 +367,7 @@ def test_hub_account_delete_project_ai_assistant_request_shape() -> None: assert transport.last_request['method'] == 'DELETE' assert transport.last_request['url'].endswith('/v3/account/projects/stub-projectId/ai/assistants/stub-assistantId') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_account_get_project_ai_usage_request_shape() -> None: @@ -377,7 +377,7 @@ def test_hub_account_get_project_ai_usage_request_shape() -> None: assert transport.last_request['method'] == 'GET' assert transport.last_request['url'].endswith('/v3/account/projects/stub-projectId/ai/usage') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_account_set_admin_portal_enabled_request_shape() -> None: @@ -387,7 +387,7 @@ def test_hub_account_set_admin_portal_enabled_request_shape() -> None: assert transport.last_request['method'] == 'PUT' assert transport.last_request['url'].endswith('/v3/account/projects/stub-projectId/admin-portal/enabled') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_account_check_project_languages_request_shape() -> None: @@ -402,7 +402,7 @@ def test_hub_account_check_project_languages_request_shape() -> None: assert transport.last_request['method'] == 'POST' assert urlparse(transport.last_request['url']).path == '/v3/account/projects/proj-1/settings/languages/check' assert json.loads(transport.last_request['body']) == {"defaultLanguage": "en", "languages": ["en", "de"]} - assert transport.last_request['headers']['x-cm-accountid'] == 'acc-1' + assert transport.last_request['headers']['norbix-account-id'] == 'acc-1' def test_async_hub_account_check_project_languages_hits_the_route() -> None: import asyncio diff --git a/tests/hub/test_account_me.py b/tests/hub/test_account_me.py index 275d8d3..37ce4e4 100644 --- a/tests/hub/test_account_me.py +++ b/tests/hub/test_account_me.py @@ -48,7 +48,7 @@ def test_get_my_account_user_profile_reads_the_profile_and_phone() -> None: profile = _sync_client(seen, PROFILE).hub.account.get_my_account_user_profile() assert [(r.method, urlparse(str(r.url)).path) for r in seen] == [("GET", "/v3/account/me")] - assert "x-cm-accountid" not in seen[0].headers + assert "norbix-account-id" not in seen[0].headers assert profile["item"]["email"] == "ada@example.test" assert profile["item"]["generalInfo"]["phone"] == "+37060000000" @@ -72,7 +72,7 @@ def test_get_account_collaborators_sends_flat_paging_and_the_project_filter() -> url = urlparse(str(seen[0].url)) assert (seen[0].method, url.path) == ("GET", "/v3/account/collaborators") assert parse_qs(url.query) == {"projectId": ["proj_2"], "pageSize": ["50"], "startingAfter": ["member_20"]} - assert "x-cm-accountid" not in seen[0].headers + assert "norbix-account-id" not in seen[0].headers def test_async_account_me_and_team_list_need_only_a_token() -> None: diff --git a/tests/hub/test_account_project.py b/tests/hub/test_account_project.py index 26a778c..757a3f8 100644 --- a/tests/hub/test_account_project.py +++ b/tests/hub/test_account_project.py @@ -54,7 +54,7 @@ def _check(request: httpx.Request, verb: str, path: str, body: dict[str, Any] | assert request.method == verb assert request.url.path == "/v3" + path assert request.headers["authorization"] == "Bearer test-token" - assert request.headers["x-cm-projectid"] == "test-project" + assert request.headers["norbix-project-id"] == "test-project" if body is None: assert request.content == b"" else: diff --git a/tests/hub/test_account_token_only.py b/tests/hub/test_account_token_only.py index 30e0bb4..3c74d12 100644 --- a/tests/hub/test_account_token_only.py +++ b/tests/hub/test_account_token_only.py @@ -2,7 +2,7 @@ The gateway takes the account from the signed-in session on each route below (``UserSession.UserAuth.AccountId``), or from the project id in the path, or -needs no account at all — and it never reads the ``X-CM-AccountId`` header. +needs no account at all — and it never reads the ``norbix-account-id`` header. So each method must work on a client WITHOUT ``account_id``, like the TypeScript, .NET, Go, Kotlin and Swift SDKs. One test per method, for the sync and the async client: verb, resolved path, Bearer header and no account @@ -83,7 +83,7 @@ def handler(request: httpx.Request) -> httpx.Response: def _assert_token_only(seen: list[httpx.Request], verb: str, path: str) -> None: assert [(r.method, urlparse(str(r.url)).path) for r in seen] == [(verb, path)] assert seen[0].headers["authorization"] == "Bearer test-token" - assert "x-cm-accountid" not in seen[0].headers + assert "norbix-account-id" not in seen[0].headers @pytest.mark.parametrize(("module", "method", "verb", "path", "ids"), CASES, ids=IDS) @@ -139,7 +139,7 @@ async def run() -> None: def _assert_public(seen: list[httpx.Request], verb: str, path: str, fields: dict[str, str]) -> None: assert [(r.method, urlparse(str(r.url)).path) for r in seen] == [(verb, path)] assert "authorization" not in seen[0].headers - assert "x-cm-accountid" not in seen[0].headers + assert "norbix-account-id" not in seen[0].headers if verb == "GET": assert dict(parse_qsl(urlparse(str(seen[0].url)).query)) == fields else: diff --git a/tests/hub/test_ai.py b/tests/hub/test_ai.py index e332b2e..d561659 100644 --- a/tests/hub/test_ai.py +++ b/tests/hub/test_ai.py @@ -139,7 +139,7 @@ def test_hub_ai_get_embedding_integrations_request_shape() -> None: assert transport.last_request['method'] == 'GET' assert transport.last_request['url'].endswith('/v3/ai/integrations/embeddings') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_ai_save_embedding_integration_request_shape() -> None: @@ -149,7 +149,7 @@ def test_hub_ai_save_embedding_integration_request_shape() -> None: assert transport.last_request['method'] == 'POST' assert transport.last_request['url'].endswith('/v3/ai/integrations/embeddings') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_ai_get_embedding_integration_request_shape() -> None: @@ -159,7 +159,7 @@ def test_hub_ai_get_embedding_integration_request_shape() -> None: assert transport.last_request['method'] == 'GET' assert transport.last_request['url'].endswith('/v3/ai/integrations/embeddings/stub-Id') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_ai_delete_embedding_integration_request_shape() -> None: @@ -169,7 +169,7 @@ def test_hub_ai_delete_embedding_integration_request_shape() -> None: assert transport.last_request['method'] == 'DELETE' assert transport.last_request['url'].endswith('/v3/ai/integrations/embeddings/stub-Id') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_ai_test_embedding_integration_request_shape() -> None: @@ -179,7 +179,7 @@ def test_hub_ai_test_embedding_integration_request_shape() -> None: assert transport.last_request['method'] == 'POST' assert transport.last_request['url'].endswith('/v3/ai/integrations/embeddings/stub-Id/test') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' def test_hub_ai_set_llm_integration_as_default_request_shape() -> None: @@ -189,5 +189,5 @@ def test_hub_ai_set_llm_integration_as_default_request_shape() -> None: assert transport.last_request['method'] == 'PUT' assert transport.last_request['url'].endswith('/v3/ai/integrations/llms/stub-Id/default') assert transport.last_request['headers']['authorization'] == 'Bearer test-token' - assert transport.last_request['headers']['x-cm-projectid'] == 'test-project' + assert transport.last_request['headers']['norbix-project-id'] == 'test-project' diff --git a/tests/hub/test_database.py b/tests/hub/test_database.py index 833fb59..0f33619 100644 --- a/tests/hub/test_database.py +++ b/tests/hub/test_database.py @@ -493,7 +493,7 @@ def test_hub_database_record_call_sends_auth_and_project_headers() -> None: assert transport.last_request is not None headers = transport.last_request["headers"] assert headers["authorization"] == "Bearer test-token" - assert headers["x-cm-projectid"] == "test-project" + assert headers["norbix-project-id"] == "test-project" def test_hub_database_audit_methods_exist_on_the_async_module() -> None: diff --git a/tests/hub/test_email_routes.py b/tests/hub/test_email_routes.py index a3a6d08..e0bf322 100644 --- a/tests/hub/test_email_routes.py +++ b/tests/hub/test_email_routes.py @@ -366,7 +366,7 @@ def test_email_call_sends_auth_and_project_headers() -> None: assert transport.last_request is not None headers = transport.last_request["headers"] assert headers["authorization"] == "Bearer test-token" - assert headers["x-cm-projectid"] == "test-project" + assert headers["norbix-project-id"] == "test-project" @pytest.fixture diff --git a/tests/hub/test_push.py b/tests/hub/test_push.py index 691665e..58fce6f 100644 --- a/tests/hub/test_push.py +++ b/tests/hub/test_push.py @@ -232,7 +232,7 @@ def test_push_call_sends_auth_and_project_headers() -> None: assert transport.last_request is not None headers = transport.last_request["headers"] assert headers["authorization"] == "Bearer test-token" - assert headers["x-cm-projectid"] == "test-project" + assert headers["norbix-project-id"] == "test-project" # The five campaign targets, with the fields the gateway reads for each one. diff --git a/tests/hub/test_sms.py b/tests/hub/test_sms.py index f868eb3..db11152 100644 --- a/tests/hub/test_sms.py +++ b/tests/hub/test_sms.py @@ -229,7 +229,7 @@ def test_sms_call_sends_auth_and_project_headers() -> None: assert transport.last_request is not None headers = transport.last_request["headers"] assert headers["authorization"] == "Bearer test-token" - assert headers["x-cm-projectid"] == "test-project" + assert headers["norbix-project-id"] == "test-project" # The four campaign audiences the gateway accepts, each with the settings diff --git a/tests/hub/test_triggers.py b/tests/hub/test_triggers.py index beb05e0..a61e736 100644 --- a/tests/hub/test_triggers.py +++ b/tests/hub/test_triggers.py @@ -42,7 +42,7 @@ def test_get_triggers_needing_attention_hits_the_route_with_query_values() -> No assert url.path == "/v3/triggers/attention" assert parse_qs(url.query) == {"triggerType": ["Schema"]} assert transport.last_request["body"] == "" - assert transport.last_request["headers"]["x-cm-projectid"] == "test-project" + assert transport.last_request["headers"]["norbix-project-id"] == "test-project" def test_async_get_triggers_needing_attention_hits_the_route() -> None: diff --git a/tests/test_files_routes.py b/tests/test_files_routes.py index ba59af1..43983d1 100644 --- a/tests/test_files_routes.py +++ b/tests/test_files_routes.py @@ -285,7 +285,7 @@ def test_api_test_files_integration_sends_project_scope_headers() -> None: client.api.files.test_files_integration(INTEGRATION_ID) headers = transport.last_request["headers"] assert headers["authorization"] == "Bearer test-token" - assert headers["x-cm-projectid"] == "test-project" + assert headers["norbix-project-id"] == "test-project" def test_api_test_files_integration_gives_back_the_parsed_items() -> None: @@ -346,5 +346,5 @@ async def run() -> Any: assert request is not None assert request["method"] == "POST" assert urlparse(request["url"]).path == f"/v3/files/{INTEGRATION_ID}/test" - assert request["headers"]["x-cm-projectid"] == "test-project" + assert request["headers"]["norbix-project-id"] == "test-project" assert result["items"][2]["operation"] == "GetAllFiles" diff --git a/tests/test_public_files.py b/tests/test_public_files.py index db8df8b..c6d73c6 100644 --- a/tests/test_public_files.py +++ b/tests/test_public_files.py @@ -54,7 +54,7 @@ def test_hub_method_hits_its_own_route(method_name: str, expected_path: str) -> assert (verb, path) == ("POST", expected_path) assert body == {"filesIntegrationId": INTEGRATION_ID, "path": "invoices/invoice.pdf"} assert headers["authorization"] == "Bearer test-token" - assert headers["x-cm-projectid"] == "test-project" + assert headers["norbix-project-id"] == "test-project" def test_make_file_public_gives_back_the_minted_id() -> None: diff --git a/tests/test_transport_optional_scope.py b/tests/test_transport_optional_scope.py index 3ad48d2..fe0e240 100644 --- a/tests/test_transport_optional_scope.py +++ b/tests/test_transport_optional_scope.py @@ -58,7 +58,7 @@ def test_optional_scope_sends_auth_only_when_present( assert len(seen) == 1 assert seen[0].headers.get("Authorization") == want_auth assert seen[0].url.params.get("hash") == "h1" - assert seen[0].headers.get("X-CM-ProjectId") == "proj_1" + assert seen[0].headers.get("norbix-project-id") == "proj_1" @pytest.mark.parametrize( diff --git a/tests/test_transport_scope_headers.py b/tests/test_transport_scope_headers.py new file mode 100644 index 0000000..f625d39 --- /dev/null +++ b/tests/test_transport_scope_headers.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +import asyncio +from collections.abc import Callable + +import httpx +import pytest + +from norbix_python.transport import AsyncTransport, Scope, Target, Transport, TransportConfig + +"""The project and account ids go out under the header names the gateway +reads (AuthStatics): norbix-project-id and norbix-account-id. The legacy +X-CM-* names are not sent — the gateway never read them, so with an account +bearer token every Api project call failed with "ProjectId is required".""" + +CASES = [ + ("api", "/{version}/db/records/find", "project"), + ("hub", "/{version}/account/projects", "account"), +] +IDS = ["api_project_call", "hub_account_call"] + + +def _cfg() -> TransportConfig: + return TransportConfig( + api_key=None, + bearer_token="account_token", + project_id="proj_1", + account_id="acc_1", + base_url_api="https://api.test", + base_url_hub="https://hub.test", + api_version="v2", + hub_version="v2", + timeout=5.0, + ) + + +def _capture(seen: list[httpx.Request]) -> Callable[[httpx.Request], httpx.Response]: + def handler(request: httpx.Request) -> httpx.Response: + seen.append(request) + return httpx.Response(200, json={}) + + return handler + + +def _assert_scope_headers(request: httpx.Request) -> None: + assert request.headers.get("Authorization") == "Bearer account_token" + assert request.headers.get("norbix-project-id") == "proj_1" + assert request.headers.get("norbix-account-id") == "acc_1" + assert "X-CM-ProjectId" not in request.headers + assert "X-CM-AccountId" not in request.headers + + +@pytest.mark.parametrize("target, path, scope", CASES, ids=IDS) +def test_sends_norbix_scope_headers(target: Target, path: str, scope: Scope) -> None: + seen: list[httpx.Request] = [] + transport = Transport(_cfg(), httpx.Client(transport=httpx.MockTransport(_capture(seen)))) + + transport.send(target=target, path=path, method="POST", request={}, scope=scope) + + assert len(seen) == 1 + _assert_scope_headers(seen[0]) + + +@pytest.mark.parametrize("target, path, scope", CASES, ids=IDS) +def test_async_sends_norbix_scope_headers(target: Target, path: str, scope: Scope) -> None: + seen: list[httpx.Request] = [] + + async def run() -> None: + async with httpx.AsyncClient(transport=httpx.MockTransport(_capture(seen))) as http: + await AsyncTransport(_cfg(), http).send( + target=target, path=path, method="POST", request={}, scope=scope + ) + + asyncio.run(run()) + + assert len(seen) == 1 + _assert_scope_headers(seen[0])