diff --git a/packages/google-cloud-alloydb-connectors/docs/connectors_v1alpha/services_.rst b/packages/google-cloud-alloydb-connectors/docs/connectors_v1alpha/services_.rst new file mode 100644 index 000000000000..9840db516688 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/docs/connectors_v1alpha/services_.rst @@ -0,0 +1,4 @@ +Services for Google Cloud Alloydb Connectors v1alpha API +======================================================== +.. toctree:: + :maxdepth: 2 diff --git a/packages/google-cloud-alloydb-connectors/docs/connectors_v1alpha/types_.rst b/packages/google-cloud-alloydb-connectors/docs/connectors_v1alpha/types_.rst new file mode 100644 index 000000000000..3fa7032f39f0 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/docs/connectors_v1alpha/types_.rst @@ -0,0 +1,6 @@ +Types for Google Cloud Alloydb Connectors v1alpha API +===================================================== + +.. automodule:: google.cloud.alloydb.connectors_v1alpha.types + :members: + :show-inheritance: diff --git a/packages/google-cloud-alloydb-connectors/docs/connectors_v1beta/services_.rst b/packages/google-cloud-alloydb-connectors/docs/connectors_v1beta/services_.rst new file mode 100644 index 000000000000..52e65386fcfc --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/docs/connectors_v1beta/services_.rst @@ -0,0 +1,4 @@ +Services for Google Cloud Alloydb Connectors v1beta API +======================================================= +.. toctree:: + :maxdepth: 2 diff --git a/packages/google-cloud-alloydb-connectors/docs/connectors_v1beta/types_.rst b/packages/google-cloud-alloydb-connectors/docs/connectors_v1beta/types_.rst new file mode 100644 index 000000000000..0297e1757546 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/docs/connectors_v1beta/types_.rst @@ -0,0 +1,6 @@ +Types for Google Cloud Alloydb Connectors v1beta API +==================================================== + +.. automodule:: google.cloud.alloydb.connectors_v1beta.types + :members: + :show-inheritance: diff --git a/packages/google-cloud-alloydb-connectors/docs/index.rst b/packages/google-cloud-alloydb-connectors/docs/index.rst index 7dd37a46a9f1..eae284aca688 100644 --- a/packages/google-cloud-alloydb-connectors/docs/index.rst +++ b/packages/google-cloud-alloydb-connectors/docs/index.rst @@ -2,6 +2,9 @@ .. include:: multiprocessing.rst +This package includes clients for multiple versions of AlloyDB connectors. +By default, you will get version ``connectors_v1``. + API Reference ------------- @@ -11,6 +14,22 @@ API Reference connectors_v1/services_ connectors_v1/types_ +API Reference +------------- +.. toctree:: + :maxdepth: 2 + + connectors_v1alpha/services_ + connectors_v1alpha/types_ + +API Reference +------------- +.. toctree:: + :maxdepth: 2 + + connectors_v1beta/services_ + connectors_v1beta/types_ + Changelog --------- diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/__init__.py b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/__init__.py new file mode 100644 index 000000000000..fc62e3365a0d --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from google.cloud.alloydb.connectors_v1alpha import gapic_version as package_version + +__version__ = package_version.__version__ + + +from .types.resources import MetadataExchangeRequest, MetadataExchangeResponse + +__all__ = ( + "MetadataExchangeRequest", + "MetadataExchangeResponse", +) diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/gapic_metadata.json b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/gapic_metadata.json new file mode 100644 index 000000000000..a64ae5c9ab8f --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/gapic_metadata.json @@ -0,0 +1,7 @@ + { + "comment": "This file maps proto services/RPCs to the corresponding library clients/methods", + "language": "python", + "libraryPackage": "google.cloud.alloydb.connectors_v1alpha", + "protoPackage": "google.cloud.alloydb.connectors.v1alpha", + "schema": "1.0" +} diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/gapic_version.py b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/gapic_version.py new file mode 100644 index 000000000000..20a9cd975b02 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/gapic_version.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +__version__ = "0.0.0" # {x-release-please-version} diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/py.typed b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/py.typed new file mode 100644 index 000000000000..d5fe144934dc --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-cloud-alloydb-connectors package uses inline types. diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/services/__init__.py b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/services/__init__.py new file mode 100644 index 000000000000..cbf94b283c70 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/services/__init__.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/types/__init__.py b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/types/__init__.py new file mode 100644 index 000000000000..d4c190d538f6 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/types/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from .resources import MetadataExchangeRequest, MetadataExchangeResponse + +__all__ = ( + "MetadataExchangeRequest", + "MetadataExchangeResponse", +) diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/types/resources.py b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/types/resources.py new file mode 100644 index 000000000000..95ac129fbd0e --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1alpha/types/resources.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from __future__ import annotations + +from typing import MutableMapping, MutableSequence + +import proto # type: ignore + +__protobuf__ = proto.module( + package="google.cloud.alloydb.connectors.v1alpha", + manifest={ + "MetadataExchangeRequest", + "MetadataExchangeResponse", + }, +) + + +class MetadataExchangeRequest(proto.Message): + r"""Message used by AlloyDB connectors to exchange client and + connection metadata with the server after a successful TLS + handshake. This metadata includes an IAM token, which is used to + authenticate users based on their IAM identity. The sole purpose + of this message is for the use of AlloyDB connectors. Clients + should not rely on this message directly as there can be + breaking changes in the future. + + Attributes: + user_agent (str): + Optional. Connector information. + auth_type (google.cloud.alloydb.connectors_v1alpha.types.MetadataExchangeRequest.AuthType): + Authentication type. + oauth2_token (str): + IAM token used for both IAM user authentiation and + ``alloydb.instances.connect`` permission check. + """ + + class AuthType(proto.Enum): + r"""AuthType contains all supported authentication types. + + Values: + AUTH_TYPE_UNSPECIFIED (0): + Authentication type is unspecified and DB_NATIVE is used by + default + DB_NATIVE (1): + Database native authentication + (user/password) + AUTO_IAM (2): + Automatic IAM authentication + """ + AUTH_TYPE_UNSPECIFIED = 0 + DB_NATIVE = 1 + AUTO_IAM = 2 + + user_agent: str = proto.Field( + proto.STRING, + number=1, + ) + auth_type: AuthType = proto.Field( + proto.ENUM, + number=2, + enum=AuthType, + ) + oauth2_token: str = proto.Field( + proto.STRING, + number=3, + ) + + +class MetadataExchangeResponse(proto.Message): + r"""Message for response to metadata exchange request. The sole + purpose of this message is for the use of AlloyDB connectors. + Clients should not rely on this message directly as there can be + breaking changes in the future. + + Attributes: + response_code (google.cloud.alloydb.connectors_v1alpha.types.MetadataExchangeResponse.ResponseCode): + Response code. + error (str): + Optional. Error message. + """ + + class ResponseCode(proto.Enum): + r"""Response code. + + Values: + RESPONSE_CODE_UNSPECIFIED (0): + Unknown response code + OK (1): + Success + ERROR (2): + Failure + """ + RESPONSE_CODE_UNSPECIFIED = 0 + OK = 1 + ERROR = 2 + + response_code: ResponseCode = proto.Field( + proto.ENUM, + number=1, + enum=ResponseCode, + ) + error: str = proto.Field( + proto.STRING, + number=2, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/__init__.py b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/__init__.py new file mode 100644 index 000000000000..04e61de241c6 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from google.cloud.alloydb.connectors_v1beta import gapic_version as package_version + +__version__ = package_version.__version__ + + +from .types.resources import MetadataExchangeRequest, MetadataExchangeResponse + +__all__ = ( + "MetadataExchangeRequest", + "MetadataExchangeResponse", +) diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/gapic_metadata.json b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/gapic_metadata.json new file mode 100644 index 000000000000..af310801bfd4 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/gapic_metadata.json @@ -0,0 +1,7 @@ + { + "comment": "This file maps proto services/RPCs to the corresponding library clients/methods", + "language": "python", + "libraryPackage": "google.cloud.alloydb.connectors_v1beta", + "protoPackage": "google.cloud.alloydb.connectors.v1beta", + "schema": "1.0" +} diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/gapic_version.py b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/gapic_version.py new file mode 100644 index 000000000000..20a9cd975b02 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/gapic_version.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +__version__ = "0.0.0" # {x-release-please-version} diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/py.typed b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/py.typed new file mode 100644 index 000000000000..d5fe144934dc --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-cloud-alloydb-connectors package uses inline types. diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/services/__init__.py b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/services/__init__.py new file mode 100644 index 000000000000..cbf94b283c70 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/services/__init__.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/types/__init__.py b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/types/__init__.py new file mode 100644 index 000000000000..d4c190d538f6 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/types/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from .resources import MetadataExchangeRequest, MetadataExchangeResponse + +__all__ = ( + "MetadataExchangeRequest", + "MetadataExchangeResponse", +) diff --git a/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/types/resources.py b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/types/resources.py new file mode 100644 index 000000000000..0d991f382b62 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/google/cloud/alloydb/connectors_v1beta/types/resources.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from __future__ import annotations + +from typing import MutableMapping, MutableSequence + +import proto # type: ignore + +__protobuf__ = proto.module( + package="google.cloud.alloydb.connectors.v1beta", + manifest={ + "MetadataExchangeRequest", + "MetadataExchangeResponse", + }, +) + + +class MetadataExchangeRequest(proto.Message): + r"""Message used by AlloyDB connectors to exchange client and + connection metadata with the server after a successful TLS + handshake. This metadata includes an IAM token, which is used to + authenticate users based on their IAM identity. The sole purpose + of this message is for the use of AlloyDB connectors. Clients + should not rely on this message directly as there can be + breaking changes in the future. + + Attributes: + user_agent (str): + Optional. Connector information. + auth_type (google.cloud.alloydb.connectors_v1beta.types.MetadataExchangeRequest.AuthType): + Authentication type. + oauth2_token (str): + IAM token used for both IAM user authentiation and + ``alloydb.instances.connect`` permission check. + """ + + class AuthType(proto.Enum): + r"""AuthType contains all supported authentication types. + + Values: + AUTH_TYPE_UNSPECIFIED (0): + Authentication type is unspecified and DB_NATIVE is used by + default + DB_NATIVE (1): + Database native authentication + (user/password) + AUTO_IAM (2): + Automatic IAM authentication + """ + AUTH_TYPE_UNSPECIFIED = 0 + DB_NATIVE = 1 + AUTO_IAM = 2 + + user_agent: str = proto.Field( + proto.STRING, + number=1, + ) + auth_type: AuthType = proto.Field( + proto.ENUM, + number=2, + enum=AuthType, + ) + oauth2_token: str = proto.Field( + proto.STRING, + number=3, + ) + + +class MetadataExchangeResponse(proto.Message): + r"""Message for response to metadata exchange request. The sole + purpose of this message is for the use of AlloyDB connectors. + Clients should not rely on this message directly as there can be + breaking changes in the future. + + Attributes: + response_code (google.cloud.alloydb.connectors_v1beta.types.MetadataExchangeResponse.ResponseCode): + Response code. + error (str): + Optional. Error message. + """ + + class ResponseCode(proto.Enum): + r"""Response code. + + Values: + RESPONSE_CODE_UNSPECIFIED (0): + Unknown response code + OK (1): + Success + ERROR (2): + Failure + """ + RESPONSE_CODE_UNSPECIFIED = 0 + OK = 1 + ERROR = 2 + + response_code: ResponseCode = proto.Field( + proto.ENUM, + number=1, + enum=ResponseCode, + ) + error: str = proto.Field( + proto.STRING, + number=2, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/packages/google-cloud-alloydb-connectors/scripts/fixup_connectors_v1alpha_keywords.py b/packages/google-cloud-alloydb-connectors/scripts/fixup_connectors_v1alpha_keywords.py new file mode 100644 index 000000000000..e79a739118c4 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/scripts/fixup_connectors_v1alpha_keywords.py @@ -0,0 +1,175 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import argparse +import os +import libcst as cst +import pathlib +import sys +from typing import (Any, Callable, Dict, List, Sequence, Tuple) + + +def partition( + predicate: Callable[[Any], bool], + iterator: Sequence[Any] +) -> Tuple[List[Any], List[Any]]: + """A stable, out-of-place partition.""" + results = ([], []) + + for i in iterator: + results[int(predicate(i))].append(i) + + # Returns trueList, falseList + return results[1], results[0] + + +class connectorsCallTransformer(cst.CSTTransformer): + CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') + METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { + } + + def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: + try: + key = original.func.attr.value + kword_params = self.METHOD_TO_PARAMS[key] + except (AttributeError, KeyError): + # Either not a method from the API or too convoluted to be sure. + return updated + + # If the existing code is valid, keyword args come after positional args. + # Therefore, all positional args must map to the first parameters. + args, kwargs = partition(lambda a: not bool(a.keyword), updated.args) + if any(k.keyword.value == "request" for k in kwargs): + # We've already fixed this file, don't fix it again. + return updated + + kwargs, ctrl_kwargs = partition( + lambda a: a.keyword.value not in self.CTRL_PARAMS, + kwargs + ) + + args, ctrl_args = args[:len(kword_params)], args[len(kword_params):] + ctrl_kwargs.extend(cst.Arg(value=a.value, keyword=cst.Name(value=ctrl)) + for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS)) + + request_arg = cst.Arg( + value=cst.Dict([ + cst.DictElement( + cst.SimpleString("'{}'".format(name)), +cst.Element(value=arg.value) + ) + # Note: the args + kwargs looks silly, but keep in mind that + # the control parameters had to be stripped out, and that + # those could have been passed positionally or by keyword. + for name, arg in zip(kword_params, args + kwargs)]), + keyword=cst.Name("request") + ) + + return updated.with_changes( + args=[request_arg] + ctrl_kwargs + ) + + +def fix_files( + in_dir: pathlib.Path, + out_dir: pathlib.Path, + *, + transformer=connectorsCallTransformer(), +): + """Duplicate the input dir to the output dir, fixing file method calls. + + Preconditions: + * in_dir is a real directory + * out_dir is a real, empty directory + """ + pyfile_gen = ( + pathlib.Path(os.path.join(root, f)) + for root, _, files in os.walk(in_dir) + for f in files if os.path.splitext(f)[1] == ".py" + ) + + for fpath in pyfile_gen: + with open(fpath, 'r') as f: + src = f.read() + + # Parse the code and insert method call fixes. + tree = cst.parse_module(src) + updated = tree.visit(transformer) + + # Create the path and directory structure for the new file. + updated_path = out_dir.joinpath(fpath.relative_to(in_dir)) + updated_path.parent.mkdir(parents=True, exist_ok=True) + + # Generate the updated source file at the corresponding path. + with open(updated_path, 'w') as f: + f.write(updated.code) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description="""Fix up source that uses the connectors client library. + +The existing sources are NOT overwritten but are copied to output_dir with changes made. + +Note: This tool operates at a best-effort level at converting positional + parameters in client method calls to keyword based parameters. + Cases where it WILL FAIL include + A) * or ** expansion in a method call. + B) Calls via function or method alias (includes free function calls) + C) Indirect or dispatched calls (e.g. the method is looked up dynamically) + + These all constitute false negatives. The tool will also detect false + positives when an API method shares a name with another method. +""") + parser.add_argument( + '-d', + '--input-directory', + required=True, + dest='input_dir', + help='the input directory to walk for python files to fix up', + ) + parser.add_argument( + '-o', + '--output-directory', + required=True, + dest='output_dir', + help='the directory to output files fixed via un-flattening', + ) + args = parser.parse_args() + input_dir = pathlib.Path(args.input_dir) + output_dir = pathlib.Path(args.output_dir) + if not input_dir.is_dir(): + print( + f"input directory '{input_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if not output_dir.is_dir(): + print( + f"output directory '{output_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if os.listdir(output_dir): + print( + f"output directory '{output_dir}' is not empty", + file=sys.stderr, + ) + sys.exit(-1) + + fix_files(input_dir, output_dir) diff --git a/packages/google-cloud-alloydb-connectors/scripts/fixup_connectors_v1beta_keywords.py b/packages/google-cloud-alloydb-connectors/scripts/fixup_connectors_v1beta_keywords.py new file mode 100644 index 000000000000..e79a739118c4 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/scripts/fixup_connectors_v1beta_keywords.py @@ -0,0 +1,175 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import argparse +import os +import libcst as cst +import pathlib +import sys +from typing import (Any, Callable, Dict, List, Sequence, Tuple) + + +def partition( + predicate: Callable[[Any], bool], + iterator: Sequence[Any] +) -> Tuple[List[Any], List[Any]]: + """A stable, out-of-place partition.""" + results = ([], []) + + for i in iterator: + results[int(predicate(i))].append(i) + + # Returns trueList, falseList + return results[1], results[0] + + +class connectorsCallTransformer(cst.CSTTransformer): + CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') + METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { + } + + def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: + try: + key = original.func.attr.value + kword_params = self.METHOD_TO_PARAMS[key] + except (AttributeError, KeyError): + # Either not a method from the API or too convoluted to be sure. + return updated + + # If the existing code is valid, keyword args come after positional args. + # Therefore, all positional args must map to the first parameters. + args, kwargs = partition(lambda a: not bool(a.keyword), updated.args) + if any(k.keyword.value == "request" for k in kwargs): + # We've already fixed this file, don't fix it again. + return updated + + kwargs, ctrl_kwargs = partition( + lambda a: a.keyword.value not in self.CTRL_PARAMS, + kwargs + ) + + args, ctrl_args = args[:len(kword_params)], args[len(kword_params):] + ctrl_kwargs.extend(cst.Arg(value=a.value, keyword=cst.Name(value=ctrl)) + for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS)) + + request_arg = cst.Arg( + value=cst.Dict([ + cst.DictElement( + cst.SimpleString("'{}'".format(name)), +cst.Element(value=arg.value) + ) + # Note: the args + kwargs looks silly, but keep in mind that + # the control parameters had to be stripped out, and that + # those could have been passed positionally or by keyword. + for name, arg in zip(kword_params, args + kwargs)]), + keyword=cst.Name("request") + ) + + return updated.with_changes( + args=[request_arg] + ctrl_kwargs + ) + + +def fix_files( + in_dir: pathlib.Path, + out_dir: pathlib.Path, + *, + transformer=connectorsCallTransformer(), +): + """Duplicate the input dir to the output dir, fixing file method calls. + + Preconditions: + * in_dir is a real directory + * out_dir is a real, empty directory + """ + pyfile_gen = ( + pathlib.Path(os.path.join(root, f)) + for root, _, files in os.walk(in_dir) + for f in files if os.path.splitext(f)[1] == ".py" + ) + + for fpath in pyfile_gen: + with open(fpath, 'r') as f: + src = f.read() + + # Parse the code and insert method call fixes. + tree = cst.parse_module(src) + updated = tree.visit(transformer) + + # Create the path and directory structure for the new file. + updated_path = out_dir.joinpath(fpath.relative_to(in_dir)) + updated_path.parent.mkdir(parents=True, exist_ok=True) + + # Generate the updated source file at the corresponding path. + with open(updated_path, 'w') as f: + f.write(updated.code) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description="""Fix up source that uses the connectors client library. + +The existing sources are NOT overwritten but are copied to output_dir with changes made. + +Note: This tool operates at a best-effort level at converting positional + parameters in client method calls to keyword based parameters. + Cases where it WILL FAIL include + A) * or ** expansion in a method call. + B) Calls via function or method alias (includes free function calls) + C) Indirect or dispatched calls (e.g. the method is looked up dynamically) + + These all constitute false negatives. The tool will also detect false + positives when an API method shares a name with another method. +""") + parser.add_argument( + '-d', + '--input-directory', + required=True, + dest='input_dir', + help='the input directory to walk for python files to fix up', + ) + parser.add_argument( + '-o', + '--output-directory', + required=True, + dest='output_dir', + help='the directory to output files fixed via un-flattening', + ) + args = parser.parse_args() + input_dir = pathlib.Path(args.input_dir) + output_dir = pathlib.Path(args.output_dir) + if not input_dir.is_dir(): + print( + f"input directory '{input_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if not output_dir.is_dir(): + print( + f"output directory '{output_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if os.listdir(output_dir): + print( + f"output directory '{output_dir}' is not empty", + file=sys.stderr, + ) + sys.exit(-1) + + fix_files(input_dir, output_dir) diff --git a/packages/google-cloud-alloydb-connectors/tests/unit/gapic/connectors_v1alpha/__init__.py b/packages/google-cloud-alloydb-connectors/tests/unit/gapic/connectors_v1alpha/__init__.py new file mode 100644 index 000000000000..cbf94b283c70 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/tests/unit/gapic/connectors_v1alpha/__init__.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/packages/google-cloud-alloydb-connectors/tests/unit/gapic/connectors_v1beta/__init__.py b/packages/google-cloud-alloydb-connectors/tests/unit/gapic/connectors_v1beta/__init__.py new file mode 100644 index 000000000000..cbf94b283c70 --- /dev/null +++ b/packages/google-cloud-alloydb-connectors/tests/unit/gapic/connectors_v1beta/__init__.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#