diff --git a/.chronus/changes/auto-microsoft-python-auth-flow-2025-2-31-14-16-3.md b/.chronus/changes/auto-microsoft-python-auth-flow-2025-2-31-14-16-3.md new file mode 100644 index 00000000000..5fb9fab0f82 --- /dev/null +++ b/.chronus/changes/auto-microsoft-python-auth-flow-2025-2-31-14-16-3.md @@ -0,0 +1,8 @@ +--- +changeKind: feature +packages: + - "@autorest/python" + - "@azure-tools/typespec-python" +--- + +Pass authentication flows info into credential policy for unbranded \ No newline at end of file diff --git a/packages/autorest.python/package.json b/packages/autorest.python/package.json index 5ef004392e2..ddd137461cb 100644 --- a/packages/autorest.python/package.json +++ b/packages/autorest.python/package.json @@ -29,7 +29,7 @@ }, "homepage": "https://github.com/Azure/autorest.python/blob/main/README.md", "dependencies": { - "@typespec/http-client-python": "~0.8.3-dev.1", + "@typespec/http-client-python": "~0.8.3-dev.2", "@autorest/system-requirements": "~1.0.2", "fs-extra": "~11.2.0", "tsx": "~4.19.1" diff --git a/packages/typespec-python/package.json b/packages/typespec-python/package.json index 32bcb27f30a..aa9c8e3cb68 100644 --- a/packages/typespec-python/package.json +++ b/packages/typespec-python/package.json @@ -50,6 +50,9 @@ "@typespec/rest": ">=0.67.0 <1.0.0", "@typespec/versioning": ">=0.67.0 <1.0.0", "@typespec/openapi": ">=0.67.0 <1.0.0", + "@typespec/events": ">=0.67.0 <1.0.0", + "@typespec/sse": ">=0.67.0 <1.0.0", + "@typespec/streams": ">=0.67.0 <1.0.0", "@azure-tools/typespec-azure-core": ">=0.53.0 <1.0.0", "@azure-tools/typespec-azure-resource-manager": ">=0.53.0 <1.0.0", "@azure-tools/typespec-autorest": ">=0.53.0 <1.0.0", @@ -60,7 +63,7 @@ "js-yaml": "~4.1.0", "semver": "~7.6.2", "tsx": "~4.19.1", - "@typespec/http-client-python": "~0.8.3-dev.1", + "@typespec/http-client-python": "~0.8.3-dev.2", "fs-extra": "~11.2.0" }, "devDependencies": { @@ -69,6 +72,9 @@ "@typespec/rest": "~0.67.0", "@typespec/versioning": "~0.67.0", "@typespec/openapi": "~0.67.0", + "@typespec/events": "~0.67.0", + "@typespec/sse": "~0.67.0", + "@typespec/streams": "~0.67.0", "@azure-tools/typespec-azure-resource-manager": "~0.53.0", "@azure-tools/typespec-azure-core": "~0.53.0", "@azure-tools/typespec-azure-rulesets": "~0.53.0", diff --git a/packages/typespec-python/test/azure/requirements.txt b/packages/typespec-python/test/azure/requirements.txt index 435ab8357ce..b39d6a53763 100644 --- a/packages/typespec-python/test/azure/requirements.txt +++ b/packages/typespec-python/test/azure/requirements.txt @@ -46,6 +46,7 @@ azure-mgmt-core==1.3.2 -e ./generated/server-versions-versioned -e ./generated/server-versions-not-versioned -e ./generated/special-words +-e ./generated/streaming-jsonl -e ./generated/typetest-array -e ./generated/typetest-dictionary -e ./generated/typetest-enum-extensible diff --git a/packages/typespec-python/test/generic_mock_api_tests/asynctests/test_authentication_async.py b/packages/typespec-python/test/generic_mock_api_tests/asynctests/test_authentication_async.py index 122df638356..bb92d42bc4a 100644 --- a/packages/typespec-python/test/generic_mock_api_tests/asynctests/test_authentication_async.py +++ b/packages/typespec-python/test/generic_mock_api_tests/asynctests/test_authentication_async.py @@ -33,6 +33,10 @@ class FakeCredential: async def get_token(*scopes): return core_library.credentials.AccessToken(token="".join(scopes), expires_on=1800) + @staticmethod + async def get_token_info(*scopes, **kwargs): + return core_library.credentials.AccessTokenInfo(token="".join(scopes), expires_on=1800) + return FakeCredential() diff --git a/packages/typespec-python/test/generic_mock_api_tests/asynctests/test_streaming_jsonl_async.py b/packages/typespec-python/test/generic_mock_api_tests/asynctests/test_streaming_jsonl_async.py new file mode 100644 index 00000000000..803215abd99 --- /dev/null +++ b/packages/typespec-python/test/generic_mock_api_tests/asynctests/test_streaming_jsonl_async.py @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import pytest + +from streaming.jsonl.aio import JsonlClient + + +@pytest.fixture +async def client(): + async with JsonlClient(endpoint="http://localhost:3000") as client: + yield client + + +JSONL = b'{"desc": "one"}\n{"desc": "two"}\n{"desc": "three"}' + + +@pytest.mark.asyncio +async def test_basic_send(client: JsonlClient): + await client.basic.send(JSONL) + + +@pytest.mark.asyncio +async def test_basic_recv(client: JsonlClient): + assert b"".join([d async for d in (await client.basic.receive())]) == JSONL diff --git a/packages/typespec-python/test/generic_mock_api_tests/test_authentication.py b/packages/typespec-python/test/generic_mock_api_tests/test_authentication.py index 13351ac74e6..5c1dc39a913 100644 --- a/packages/typespec-python/test/generic_mock_api_tests/test_authentication.py +++ b/packages/typespec-python/test/generic_mock_api_tests/test_authentication.py @@ -33,6 +33,10 @@ class FakeCredential: def get_token(*scopes): return core_library.credentials.AccessToken(token="".join(scopes), expires_on=1800) + @staticmethod + def get_token_info(*scopes, **kwargs): + return core_library.credentials.AccessTokenInfo(token="".join(scopes), expires_on=1800) + return FakeCredential() diff --git a/packages/typespec-python/test/generic_mock_api_tests/test_streaming_jsonl.py b/packages/typespec-python/test/generic_mock_api_tests/test_streaming_jsonl.py new file mode 100644 index 00000000000..494c17a3493 --- /dev/null +++ b/packages/typespec-python/test/generic_mock_api_tests/test_streaming_jsonl.py @@ -0,0 +1,25 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import pytest + +from streaming.jsonl import JsonlClient + + +@pytest.fixture +def client(): + with JsonlClient(endpoint="http://localhost:3000") as client: + yield client + + +JSONL = b'{"desc": "one"}\n{"desc": "two"}\n{"desc": "three"}' + + +def test_basic_send(client: JsonlClient): + client.basic.send(JSONL) + + +def test_basic_recv(client: JsonlClient): + assert b"".join(client.basic.receive()) == JSONL diff --git a/packages/typespec-python/test/unbranded/generated/authentication-oauth2/authentication/oauth2/_configuration.py b/packages/typespec-python/test/unbranded/generated/authentication-oauth2/authentication/oauth2/_configuration.py index f34b38796fe..23d1550888f 100644 --- a/packages/typespec-python/test/unbranded/generated/authentication-oauth2/authentication/oauth2/_configuration.py +++ b/packages/typespec-python/test/unbranded/generated/authentication-oauth2/authentication/oauth2/_configuration.py @@ -42,5 +42,14 @@ def _configure(self, **kwargs: Any) -> None: self.authentication_policy = kwargs.get("authentication_policy") if self.credential and not self.authentication_policy: self.authentication_policy = policies.BearerTokenCredentialPolicy( - self.credential, *self.credential_scopes, **kwargs + self.credential, + *self.credential_scopes, + auth_flows=[ + { + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": [{"value": "https://security.microsoft.com/.default"}], + "type": "implicit", + } + ], + **kwargs ) diff --git a/packages/typespec-python/test/unbranded/generated/authentication-oauth2/authentication/oauth2/aio/_configuration.py b/packages/typespec-python/test/unbranded/generated/authentication-oauth2/authentication/oauth2/aio/_configuration.py index 858820cf4a2..f386cafb473 100644 --- a/packages/typespec-python/test/unbranded/generated/authentication-oauth2/authentication/oauth2/aio/_configuration.py +++ b/packages/typespec-python/test/unbranded/generated/authentication-oauth2/authentication/oauth2/aio/_configuration.py @@ -44,5 +44,14 @@ def _configure(self, **kwargs: Any) -> None: self.authentication_policy = kwargs.get("authentication_policy") if self.credential and not self.authentication_policy: self.authentication_policy = policies.AsyncBearerTokenCredentialPolicy( - self.credential, *self.credential_scopes, **kwargs + self.credential, + *self.credential_scopes, + auth_flows=[ + { + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": [{"value": "https://security.microsoft.com/.default"}], + "type": "implicit", + } + ], + **kwargs ) diff --git a/packages/typespec-python/test/unbranded/generated/authentication-union/authentication/union/_configuration.py b/packages/typespec-python/test/unbranded/generated/authentication-union/authentication/union/_configuration.py index f1451eda58e..91799250df1 100644 --- a/packages/typespec-python/test/unbranded/generated/authentication-union/authentication/union/_configuration.py +++ b/packages/typespec-python/test/unbranded/generated/authentication-union/authentication/union/_configuration.py @@ -45,7 +45,18 @@ def _infer_policy(self, **kwargs): if isinstance(self.credential, ServiceKeyCredential): return policies.ServiceKeyCredentialPolicy(self.credential, "x-ms-api-key", **kwargs) if hasattr(self.credential, "get_token"): - return policies.BearerTokenCredentialPolicy(self.credential, *self.credential_scopes, **kwargs) + return policies.BearerTokenCredentialPolicy( + self.credential, + *self.credential_scopes, + auth_flows=[ + { + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": [{"value": "https://security.microsoft.com/.default"}], + "type": "implicit", + } + ], + **kwargs, + ) raise TypeError(f"Unsupported credential: {self.credential}") def _configure(self, **kwargs: Any) -> None: diff --git a/packages/typespec-python/test/unbranded/generated/authentication-union/authentication/union/aio/_configuration.py b/packages/typespec-python/test/unbranded/generated/authentication-union/authentication/union/aio/_configuration.py index 701795a1196..8d6587ad78c 100644 --- a/packages/typespec-python/test/unbranded/generated/authentication-union/authentication/union/aio/_configuration.py +++ b/packages/typespec-python/test/unbranded/generated/authentication-union/authentication/union/aio/_configuration.py @@ -45,7 +45,18 @@ def _infer_policy(self, **kwargs): if isinstance(self.credential, ServiceKeyCredential): return policies.ServiceKeyCredentialPolicy(self.credential, "x-ms-api-key", **kwargs) if hasattr(self.credential, "get_token"): - return policies.AsyncBearerTokenCredentialPolicy(self.credential, *self.credential_scopes, **kwargs) + return policies.AsyncBearerTokenCredentialPolicy( + self.credential, + *self.credential_scopes, + auth_flows=[ + { + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": [{"value": "https://security.microsoft.com/.default"}], + "type": "implicit", + } + ], + **kwargs, + ) raise TypeError(f"Unsupported credential: {self.credential}") def _configure(self, **kwargs: Any) -> None: diff --git a/packages/typespec-python/test/unbranded/mock_api_tests/asynctests/test_auth_flow_async.py b/packages/typespec-python/test/unbranded/mock_api_tests/asynctests/test_auth_flow_async.py new file mode 100644 index 00000000000..5ad8263c4b6 --- /dev/null +++ b/packages/typespec-python/test/unbranded/mock_api_tests/asynctests/test_auth_flow_async.py @@ -0,0 +1,19 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import pytest +from authentication.oauth2.aio import OAuth2Client + + +@pytest.mark.asyncio +async def test_oauth2_auth_flows(): + oauth2_client = OAuth2Client("fake_credential") + assert oauth2_client._config.authentication_policy._auth_flows == [ + { + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": [{"value": "https://security.microsoft.com/.default"}], + "type": "implicit", + } + ] diff --git a/packages/typespec-python/test/unbranded/mock_api_tests/test_auth_flow.py b/packages/typespec-python/test/unbranded/mock_api_tests/test_auth_flow.py new file mode 100644 index 00000000000..4aa857b020c --- /dev/null +++ b/packages/typespec-python/test/unbranded/mock_api_tests/test_auth_flow.py @@ -0,0 +1,17 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from authentication.oauth2 import OAuth2Client + + +def test_oauth2_auth_flows(): + oauth2_client = OAuth2Client("fake_credential") + assert oauth2_client._config.authentication_policy._auth_flows == [ + { + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": [{"value": "https://security.microsoft.com/.default"}], + "type": "implicit", + } + ] diff --git a/packages/typespec-python/test/unbranded/requirements.txt b/packages/typespec-python/test/unbranded/requirements.txt index 8d2fcf35041..223e748ed5e 100644 --- a/packages/typespec-python/test/unbranded/requirements.txt +++ b/packages/typespec-python/test/unbranded/requirements.txt @@ -2,7 +2,6 @@ aiohttp;python_full_version>="3.5.2" requests==2.32.2 pytest pytest-asyncio==0.14.0;python_full_version>="3.5.2" -corehttp==1.0.0b3 # common test case -e ./generated/authentication-api-key @@ -21,6 +20,7 @@ corehttp==1.0.0b3 -e ./generated/server-versions-versioned -e ./generated/server-versions-not-versioned -e ./generated/special-words +-e ./generated/streaming-jsonl -e ./generated/typetest-array -e ./generated/typetest-dictionary -e ./generated/typetest-enum-extensible diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8f12ca09b4b..62462f6d25b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -57,8 +57,8 @@ importers: specifier: ~1.0.2 version: 1.0.2 '@typespec/http-client-python': - specifier: ~0.8.3-dev.1 - version: 0.8.3-dev.1(c25h2yeksuorzcqz3v4sac5zy4) + specifier: ~0.8.3-dev.2 + version: 0.8.3-dev.2(c25h2yeksuorzcqz3v4sac5zy4) fs-extra: specifier: ~11.2.0 version: 11.2.0 @@ -79,8 +79,8 @@ importers: packages/typespec-python: dependencies: '@typespec/http-client-python': - specifier: ~0.8.3-dev.1 - version: 0.8.3-dev.1(kvm224o5qzo556uchxhfoioziu) + specifier: ~0.8.3-dev.2 + version: 0.8.3-dev.2(kvm224o5qzo556uchxhfoioziu) fs-extra: specifier: ~11.2.0 version: 11.2.0 @@ -130,6 +130,9 @@ importers: '@typespec/compiler': specifier: ~0.67.0 version: 0.67.1(@types/node@22.5.5) + '@typespec/events': + specifier: ~0.67.0 + version: 0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5)) '@typespec/http': specifier: ~0.67.0 version: 0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))(@typespec/streams@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))) @@ -142,6 +145,12 @@ importers: '@typespec/rest': specifier: ~0.67.0 version: 0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))(@typespec/http@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))(@typespec/streams@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5)))) + '@typespec/sse': + specifier: ~0.67.0 + version: 0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))(@typespec/events@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5)))(@typespec/http@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))(@typespec/streams@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))))(@typespec/streams@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))) + '@typespec/streams': + specifier: ~0.67.0 + version: 0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5)) '@typespec/versioning': specifier: ~0.67.0 version: 0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5)) @@ -1536,8 +1545,8 @@ packages: peerDependencies: '@typespec/compiler': ^0.67.1 - '@typespec/http-client-python@0.8.3-dev.1': - resolution: {integrity: sha512-9uEb32rR/YeSUepnG2NjIeqKnLlSoZxh47h2+RZNuoas1ZgvioYios4osyJuKpGgavAhNJaedtdXfMhF8niz2w==} + '@typespec/http-client-python@0.8.3-dev.2': + resolution: {integrity: sha512-XN5H5N4qZzsUHoM0cH97tHZ6DBYROWDD/L5YFjRykLy/6UwcE/PRNID8KmQocFlTXjCLExs1qtw4fcxizfgEsQ==} engines: {node: '>=20.0.0'} peerDependencies: '@azure-tools/typespec-autorest': '>=0.53.0 <1.0.0' @@ -6147,7 +6156,7 @@ snapshots: dependencies: '@typespec/compiler': 0.67.1(@types/node@22.5.5) - '@typespec/http-client-python@0.8.3-dev.1(c25h2yeksuorzcqz3v4sac5zy4)': + '@typespec/http-client-python@0.8.3-dev.2(c25h2yeksuorzcqz3v4sac5zy4)': dependencies: '@azure-tools/typespec-autorest': 0.53.0(wjegnqrrlroq36cedwihs7fwdy) '@azure-tools/typespec-azure-core': 0.53.0(@typespec/compiler@0.67.1(@types/node@22.13.10))(@typespec/http@0.67.1(@typespec/compiler@0.67.1(@types/node@22.13.10))(@typespec/streams@0.67.1(@typespec/compiler@0.67.1(@types/node@22.13.10))))(@typespec/rest@0.67.1(@typespec/compiler@0.67.1(@types/node@22.13.10))(@typespec/http@0.67.1(@typespec/compiler@0.67.1(@types/node@22.13.10))(@typespec/streams@0.67.1(@typespec/compiler@0.67.1(@types/node@22.13.10))))) @@ -6171,7 +6180,7 @@ snapshots: - bufferutil - utf-8-validate - '@typespec/http-client-python@0.8.3-dev.1(kvm224o5qzo556uchxhfoioziu)': + '@typespec/http-client-python@0.8.3-dev.2(kvm224o5qzo556uchxhfoioziu)': dependencies: '@azure-tools/typespec-autorest': 0.53.0(e3cfoxkazmlklhsfsyiqwpleoq) '@azure-tools/typespec-azure-core': 0.53.0(@typespec/compiler@0.67.1(@types/node@22.5.5))(@typespec/http@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))(@typespec/streams@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))))(@typespec/rest@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))(@typespec/http@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5))(@typespec/streams@0.67.1(@typespec/compiler@0.67.1(@types/node@22.5.5)))))