diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 7cc61b6..b668c04 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -1,17 +1,17 @@ -# Copyright 2021 Google LLC +# Copyright 2022 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 +# 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. - docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:39ad8c0570e4f5d2d3124a509de4fe975e799e2b97e0f58aed88f8880d5a8b60 + digest: sha256:ed1f9983d5a935a89fe8085e8bb97d94e41015252c5b6c9771257cf8624367e6 + diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index be888a9..e446644 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,9 +3,10 @@ # # For syntax help see: # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax +# Note: This file is autogenerated. To make changes to the codeowner team, please update .repo-metadata.json. -# The @googleapis/yoshi-python is the default owner for changes in this repo -* @googleapis/yoshi-python +# @googleapis/yoshi-python is the default owner for changes in this repo +* @googleapis/yoshi-python - -/samples/ @googleapis/python-samples-owners +# @googleapis/python-samples-reviewers is the default owner for samples changes +/samples/ @googleapis/python-samples-reviewers diff --git a/.github/release-please.yml b/.github/release-please.yml index 4507ad0..466597e 100644 --- a/.github/release-please.yml +++ b/.github/release-please.yml @@ -1 +1,2 @@ releaseType: python +handleGHRelease: true diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml new file mode 100644 index 0000000..d4ca941 --- /dev/null +++ b/.github/release-trigger.yml @@ -0,0 +1 @@ +enabled: true diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..f7b8344 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,38 @@ +on: + pull_request: + branches: + - main +name: docs +jobs: + docs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install nox + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install nox + - name: Run docs + run: | + nox -s docs + docfx: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install nox + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install nox + - name: Run docfx + run: | + nox -s docfx diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..1e8b05c --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,25 @@ +on: + pull_request: + branches: + - main +name: lint +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install nox + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install nox + - name: Run lint + run: | + nox -s lint + - name: Run lint_setup_py + run: | + nox -s lint_setup_py diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml new file mode 100644 index 0000000..074ee25 --- /dev/null +++ b/.github/workflows/unittest.yml @@ -0,0 +1,57 @@ +on: + pull_request: + branches: + - main +name: unittest +jobs: + unit: + runs-on: ubuntu-latest + strategy: + matrix: + python: ['3.6', '3.7', '3.8', '3.9', '3.10'] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Install nox + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install nox + - name: Run unit tests + env: + COVERAGE_FILE: .coverage-${{ matrix.python }} + run: | + nox -s unit-${{ matrix.python }} + - name: Upload coverage results + uses: actions/upload-artifact@v2 + with: + name: coverage-artifacts + path: .coverage-${{ matrix.python }} + + cover: + runs-on: ubuntu-latest + needs: + - unit + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install coverage + run: | + python -m pip install --upgrade setuptools pip wheel + python -m pip install coverage + - name: Download coverage results + uses: actions/download-artifact@v2 + with: + name: coverage-artifacts + path: .coverage-results/ + - name: Report coverage results + run: | + coverage combine .coverage-results/.coverage* + coverage report --show-missing --fail-under=100 diff --git a/.kokoro/release.sh b/.kokoro/release.sh index b1f5c78..0e691c3 100755 --- a/.kokoro/release.sh +++ b/.kokoro/release.sh @@ -26,7 +26,7 @@ python3 -m pip install --upgrade twine wheel setuptools export PYTHONUNBUFFERED=1 # Move into the package, build the distribution and upload. -TWINE_PASSWORD=$(cat "${KOKORO_GFILE_DIR}/secret_manager/google-cloud-pypi-token") +TWINE_PASSWORD=$(cat "${KOKORO_KEYSTORE_DIR}/73713_google-cloud-pypi-token-keystore-1") cd github/python-ids python3 setup.py sdist bdist_wheel twine upload --username __token__ --password "${TWINE_PASSWORD}" dist/* diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg index 6868408..285ee0d 100644 --- a/.kokoro/release/common.cfg +++ b/.kokoro/release/common.cfg @@ -23,8 +23,18 @@ env_vars: { value: "github/python-ids/.kokoro/release.sh" } +# Fetch PyPI password +before_action { + fetch_keystore { + keystore_resource { + keystore_config_id: 73713 + keyname: "google-cloud-pypi-token-keystore-1" + } + } +} + # Tokens needed to report release status back to GitHub env_vars: { key: "SECRET_MANAGER_KEYS" - value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem,google-cloud-pypi-token" + value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem" } diff --git a/.repo-metadata.json b/.repo-metadata.json index 65bc383..1f59c0a 100644 --- a/.repo-metadata.json +++ b/.repo-metadata.json @@ -2,14 +2,15 @@ "name": "ids", "name_pretty": "Cloud IDS", "product_documentation": "https://cloud.google.com/intrusion-detection-system/", - "client_documentation": "https://googleapis.dev/python/ids/latest", + "client_documentation": "https://cloud.google.com/python/docs/reference/ids/latest", "issue_tracker": "", - "release_level": "beta", + "release_level": "stable", "language": "python", "library_type": "GAPIC_AUTO", "repo": "googleapis/python-ids", "distribution_name": "google-cloud-ids", "api_id": "ids.googleapis.com", "default_version": "v1", - "codeowner_team": "" -} \ No newline at end of file + "codeowner_team": "", + "api_shortname": "ids" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index dfaaa84..b38deb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.0.0](https://github.com/googleapis/python-ids/compare/v0.1.0...v1.0.0) (2022-01-24) + + +### Features + +* bump release level to production/stable ([#5](https://github.com/googleapis/python-ids/issues/5)) ([ad90dd9](https://github.com/googleapis/python-ids/commit/ad90dd9e6064d2eb8504f38df2aa1f882b516459)) + ## 0.1.0 (2021-11-12) diff --git a/README.rst b/README.rst index 3dbd586..b6cfd53 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ Python Client for Cloud IDS =========================== -|beta| |pypi| |versions| +|stable| |pypi| |versions| `Cloud IDS`_: is an intrusion detection service that provides threat detection for intrusions, malware, spyware, and command-and-control attacks on your network. Cloud IDS works by creating @@ -12,14 +12,14 @@ threat detection. - `Client Library Documentation`_ - `Product Documentation`_ -.. |beta| image:: https://img.shields.io/badge/support-beta-gold.svg +.. |stable| image:: https://img.shields.io/badge/support-stable-gold.svg :target: https://github.com/googleapis/google-cloud-python/blob/main/README.rst#general-availability .. |pypi| image:: https://img.shields.io/pypi/v/google-cloud-ids.svg :target: https://pypi.org/project/google-cloud-ids/ .. |versions| image:: https://img.shields.io/pypi/pyversions/google-cloud-ids.svg :target: https://pypi.org/project/google-cloud-ids/ .. _Cloud IDS: https://cloud.google.com/intrusion-detection-system -.. _Client Library Documentation: https://googleapis.dev/python/ids/latest +.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/ids/latest .. _Product Documentation: https://cloud.google.com/intrusion-detection-system/docs Quick Start diff --git a/google/cloud/ids_v1/services/ids/transports/base.py b/google/cloud/ids_v1/services/ids/transports/base.py index b98283f..4384f24 100644 --- a/google/cloud/ids_v1/services/ids/transports/base.py +++ b/google/cloud/ids_v1/services/ids/transports/base.py @@ -101,7 +101,6 @@ def __init__( credentials, _ = google.auth.load_credentials_from_file( credentials_file, **scopes_kwargs, quota_project_id=quota_project_id ) - elif credentials is None: credentials, _ = google.auth.default( **scopes_kwargs, quota_project_id=quota_project_id diff --git a/setup.py b/setup.py index 5dec8b6..ca51b44 100644 --- a/setup.py +++ b/setup.py @@ -22,9 +22,9 @@ name = "google-cloud-ids" description = "Cloud IDS API client library" -version = "0.1.0" +version = "1.0.0" url = "https://github.com/googleapis/python-ids" -release_status = "Development Status :: 4 - Beta" +release_status = "Development Status :: 5 - Production/Stable" dependencies = [ # NOTE: Maintainers, please do not require google-api-core>=2.x.x # Until this issue is closed diff --git a/tests/unit/gapic/ids_v1/test_ids.py b/tests/unit/gapic/ids_v1/test_ids.py index ffd2b16..a56e8aa 100644 --- a/tests/unit/gapic/ids_v1/test_ids.py +++ b/tests/unit/gapic/ids_v1/test_ids.py @@ -224,20 +224,20 @@ def test_ids_client_client_options(client_class, transport_class, transport_name # unsupported value. with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): with pytest.raises(MutualTLSChannelError): - client = client_class() + client = client_class(transport=transport_name) # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. with mock.patch.dict( os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} ): with pytest.raises(ValueError): - client = client_class() + client = client_class(transport=transport_name) # Check the case quota_project_id is provided options = client_options.ClientOptions(quota_project_id="octopus") with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None - client = client_class(transport=transport_name, client_options=options) + client = client_class(client_options=options, transport=transport_name) patched.assert_called_once_with( credentials=None, credentials_file=None, @@ -280,7 +280,7 @@ def test_ids_client_mtls_env_auto( ) with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None - client = client_class(transport=transport_name, client_options=options) + client = client_class(client_options=options, transport=transport_name) if use_client_cert_env == "false": expected_client_cert_source = None @@ -371,7 +371,7 @@ def test_ids_client_client_options_scopes( options = client_options.ClientOptions(scopes=["1", "2"],) with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None - client = client_class(transport=transport_name, client_options=options) + client = client_class(client_options=options, transport=transport_name) patched.assert_called_once_with( credentials=None, credentials_file=None, @@ -398,7 +398,7 @@ def test_ids_client_client_options_credentials_file( options = client_options.ClientOptions(credentials_file="credentials.json") with mock.patch.object(transport_class, "__init__") as patched: patched.return_value = None - client = client_class(transport=transport_name, client_options=options) + client = client_class(client_options=options, transport=transport_name) patched.assert_called_once_with( credentials=None, credentials_file="credentials.json", @@ -429,7 +429,8 @@ def test_ids_client_client_options_from_dict(): ) -def test_list_endpoints(transport: str = "grpc", request_type=ids.ListEndpointsRequest): +@pytest.mark.parametrize("request_type", [ids.ListEndpointsRequest, dict,]) +def test_list_endpoints(request_type, transport: str = "grpc"): client = IDSClient( credentials=ga_credentials.AnonymousCredentials(), transport=transport, ) @@ -457,10 +458,6 @@ def test_list_endpoints(transport: str = "grpc", request_type=ids.ListEndpointsR assert response.unreachable == ["unreachable_value"] -def test_list_endpoints_from_dict(): - test_list_endpoints(request_type=dict) - - def test_list_endpoints_empty_call(): # This test is a coverage failsafe to make sure that totally empty calls, # i.e. request == None and no flattened fields passed, work. @@ -634,8 +631,10 @@ async def test_list_endpoints_flattened_error_async(): ) -def test_list_endpoints_pager(): - client = IDSClient(credentials=ga_credentials.AnonymousCredentials,) +def test_list_endpoints_pager(transport_name: str = "grpc"): + client = IDSClient( + credentials=ga_credentials.AnonymousCredentials, transport=transport_name, + ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.list_endpoints), "__call__") as call: @@ -666,8 +665,10 @@ def test_list_endpoints_pager(): assert all(isinstance(i, ids.Endpoint) for i in results) -def test_list_endpoints_pages(): - client = IDSClient(credentials=ga_credentials.AnonymousCredentials,) +def test_list_endpoints_pages(transport_name: str = "grpc"): + client = IDSClient( + credentials=ga_credentials.AnonymousCredentials, transport=transport_name, + ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.list_endpoints), "__call__") as call: @@ -748,7 +749,8 @@ async def test_list_endpoints_async_pages(): assert page_.raw_page.next_page_token == token -def test_get_endpoint(transport: str = "grpc", request_type=ids.GetEndpointRequest): +@pytest.mark.parametrize("request_type", [ids.GetEndpointRequest, dict,]) +def test_get_endpoint(request_type, transport: str = "grpc"): client = IDSClient( credentials=ga_credentials.AnonymousCredentials(), transport=transport, ) @@ -789,10 +791,6 @@ def test_get_endpoint(transport: str = "grpc", request_type=ids.GetEndpointReque assert response.traffic_logs is True -def test_get_endpoint_from_dict(): - test_get_endpoint(request_type=dict) - - def test_get_endpoint_empty_call(): # This test is a coverage failsafe to make sure that totally empty calls, # i.e. request == None and no flattened fields passed, work. @@ -974,9 +972,8 @@ async def test_get_endpoint_flattened_error_async(): ) -def test_create_endpoint( - transport: str = "grpc", request_type=ids.CreateEndpointRequest -): +@pytest.mark.parametrize("request_type", [ids.CreateEndpointRequest, dict,]) +def test_create_endpoint(request_type, transport: str = "grpc"): client = IDSClient( credentials=ga_credentials.AnonymousCredentials(), transport=transport, ) @@ -1000,10 +997,6 @@ def test_create_endpoint( assert isinstance(response, future.Future) -def test_create_endpoint_from_dict(): - test_create_endpoint(request_type=dict) - - def test_create_endpoint_empty_call(): # This test is a coverage failsafe to make sure that totally empty calls, # i.e. request == None and no flattened fields passed, work. @@ -1198,9 +1191,8 @@ async def test_create_endpoint_flattened_error_async(): ) -def test_delete_endpoint( - transport: str = "grpc", request_type=ids.DeleteEndpointRequest -): +@pytest.mark.parametrize("request_type", [ids.DeleteEndpointRequest, dict,]) +def test_delete_endpoint(request_type, transport: str = "grpc"): client = IDSClient( credentials=ga_credentials.AnonymousCredentials(), transport=transport, ) @@ -1224,10 +1216,6 @@ def test_delete_endpoint( assert isinstance(response, future.Future) -def test_delete_endpoint_from_dict(): - test_delete_endpoint(request_type=dict) - - def test_delete_endpoint_empty_call(): # This test is a coverage failsafe to make sure that totally empty calls, # i.e. request == None and no flattened fields passed, work. @@ -1918,7 +1906,7 @@ def test_parse_common_location_path(): assert expected == actual -def test_client_withDEFAULT_CLIENT_INFO(): +def test_client_with_default_client_info(): client_info = gapic_v1.client_info.ClientInfo() with mock.patch.object(transports.IDSTransport, "_prep_wrapped_messages") as prep: