Skip to content

Commit ac3e8be

Browse files
fix(workflows): refactor sample 'workflows_api_quickstart' to comply with the Samples Style Guide and fix tests (#13261)
* fix(workflows): refactor sample to comply with the Samples Style Guide - Take out unneccesary `os.getenv`s and the return from the sample - As the `__main__` entry point is requested to stay in the script, but not in the sample, it was adjusted accordingly * fix(workflows): adjust function definition for line length * fix(workflows): fix grammar in comments * fix(workflows): allow testing in Python 3.9+, and update to latest google-cloud-workflows library * test(workflows): move test from main to conftest, and add backoff to workflow creation * fix(workflows): fix header in conftest.py * fix(workflows): add api_core.retry as suggested in https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/AUTHORING_GUIDE.md#retry-rpcs * fix(workflows): add exponential backoff to main_test * fix(workflows): add missing period to comment * fix(workflows): apply Camie Kim's feedback update the copyright year line 31: change "The Google Cloud project id" to "The ID of the Google Cloud project" line 33: add a period line 50: add one more space before the line comment per https://google.github.io/styleguide/pyguide#385-block-and-inline-comments line 51: add a line comment (# For example: myFirstWorkflow) * fix(workflows): add two missing spaces before in-line comment
1 parent 314c158 commit ac3e8be

File tree

6 files changed

+143
-65
lines changed

6 files changed

+143
-65
lines changed

workflows/cloud-client/conftest.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import os
16+
import time
17+
import uuid
18+
19+
from google.cloud import workflows_v1
20+
21+
import pytest
22+
23+
24+
PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT")
25+
LOCATION = "us-central1"
26+
WORKFLOW_ID = "myFirstWorkflow_" + str(uuid.uuid4())
27+
28+
29+
def workflow_exists(client: workflows_v1.WorkflowsClient) -> bool:
30+
"""Returns True if the workflow exists in this project."""
31+
try:
32+
workflow_name = client.workflow_path(
33+
PROJECT_ID, LOCATION, WORKFLOW_ID
34+
)
35+
client.get_workflow(request={"name": workflow_name})
36+
return True
37+
except Exception as e:
38+
print(f"Workflow doesn't exist: {e}")
39+
return False
40+
41+
42+
@pytest.fixture(scope="module")
43+
def client() -> str:
44+
assert PROJECT_ID, "'GOOGLE_CLOUD_PROJECT' environment variable not set."
45+
workflows_client = workflows_v1.WorkflowsClient()
46+
return workflows_client
47+
48+
49+
@pytest.fixture(scope="module")
50+
def project_id() -> str:
51+
return PROJECT_ID
52+
53+
54+
@pytest.fixture(scope="module")
55+
def location() -> str:
56+
return LOCATION
57+
58+
59+
@pytest.fixture(scope="module")
60+
def workflow_id(client: workflows_v1.WorkflowsClient) -> str:
61+
creating_workflow = False
62+
backoff_delay = 1 # Start wait with delay of 1 second.
63+
64+
# Create the workflow if it doesn't exist.
65+
while not workflow_exists(client):
66+
if not creating_workflow:
67+
# Create the workflow.
68+
workflow_file = open("myFirstWorkflow.workflows.yaml").read()
69+
70+
parent = client.common_location_path(PROJECT_ID, LOCATION)
71+
72+
client.create_workflow(
73+
request={
74+
"parent": parent,
75+
"workflow_id": WORKFLOW_ID,
76+
"workflow": {
77+
"name": WORKFLOW_ID,
78+
"source_contents": workflow_file
79+
},
80+
}
81+
)
82+
83+
creating_workflow = True
84+
85+
# Wait until the workflow is created.
86+
print("- Waiting for the Workflow to be created...")
87+
time.sleep(backoff_delay)
88+
# Double the delay to provide exponential backoff.
89+
backoff_delay *= 2
90+
91+
yield WORKFLOW_ID
92+
93+
# Delete the workflow.
94+
workflow_full_name = client.workflow_path(
95+
PROJECT_ID, LOCATION, WORKFLOW_ID
96+
)
97+
98+
client.delete_workflow(
99+
request={
100+
"name": workflow_full_name,
101+
}
102+
)

workflows/cloud-client/main.py

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2021 Google LLC
1+
# Copyright 2025 Google LLC
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -12,60 +12,70 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
# [START workflows_api_quickstart]
1615
import os
17-
import time
1816

19-
from google.cloud import workflows_v1
20-
from google.cloud.workflows import executions_v1
2117
from google.cloud.workflows.executions_v1 import Execution
22-
from google.cloud.workflows.executions_v1.types import executions
23-
24-
PROJECT = os.getenv("GOOGLE_CLOUD_PROJECT")
25-
LOCATION = os.getenv("LOCATION", "us-central1")
26-
WORKFLOW_ID = os.getenv("WORKFLOW", "myFirstWorkflow")
2718

2819

29-
def execute_workflow(project: str, location: str, workflow: str) -> Execution:
20+
def execute_workflow(
21+
project_id: str,
22+
location: str,
23+
workflow_id: str
24+
) -> Execution:
3025
"""Execute a workflow and print the execution results.
3126
3227
A workflow consists of a series of steps described
3328
using the Workflows syntax, and can be written in either YAML or JSON.
3429
3530
Args:
36-
project: The Google Cloud project id
31+
project: The ID of the Google Cloud project
3732
which contains the workflow to execute.
38-
location: The location for the workflow
33+
location: The location for the workflow.
3934
workflow: The ID of the workflow to execute.
4035
4136
Returns:
4237
The execution response.
4338
"""
39+
40+
# [START workflows_api_quickstart]
41+
import time
42+
43+
from google.cloud import workflows_v1
44+
from google.cloud.workflows import executions_v1
45+
46+
from google.cloud.workflows.executions_v1.types import executions
47+
48+
# TODO(developer): Update and uncomment the following lines.
49+
# project_id = "MY_PROJECT_ID"
50+
# location = "MY_LOCATION" # For example: us-central1
51+
# workflow_id = "MY_WORKFLOW_ID" # For example: myFirstWorkflow
52+
4453
# [START workflows_api_quickstart_client_libraries]
45-
# Set up API clients.
54+
# Initialize API clients.
4655
execution_client = executions_v1.ExecutionsClient()
4756
workflows_client = workflows_v1.WorkflowsClient()
4857
# [END workflows_api_quickstart_client_libraries]
4958

5059
# [START workflows_api_quickstart_execution]
5160
# Construct the fully qualified location path.
52-
parent = workflows_client.workflow_path(project, location, workflow)
61+
parent = workflows_client.workflow_path(project_id, location, workflow_id)
5362

5463
# Execute the workflow.
5564
response = execution_client.create_execution(request={"parent": parent})
5665
print(f"Created execution: {response.name}")
5766

5867
# Wait for execution to finish, then print results.
5968
execution_finished = False
60-
backoff_delay = 1 # Start wait with delay of 1 second
69+
backoff_delay = 1 # Start wait with delay of 1 second.
6170
print("Poll for result...")
71+
6272
while not execution_finished:
6373
execution = execution_client.get_execution(
6474
request={"name": response.name}
6575
)
6676
execution_finished = execution.state != executions.Execution.State.ACTIVE
6777

68-
# If we haven't seen the result yet, wait a second.
78+
# If we haven't seen the result yet, keep waiting.
6979
if not execution_finished:
7080
print("- Waiting for results...")
7181
time.sleep(backoff_delay)
@@ -74,11 +84,13 @@ def execute_workflow(project: str, location: str, workflow: str) -> Execution:
7484
else:
7585
print(f"Execution finished with state: {execution.state.name}")
7686
print(f"Execution results: {execution.result}")
77-
return execution
7887
# [END workflows_api_quickstart_execution]
88+
# [END workflows_api_quickstart]
89+
return execution
7990

8091

8192
if __name__ == "__main__":
93+
PROJECT = os.getenv("GOOGLE_CLOUD_PROJECT")
8294
assert PROJECT, "'GOOGLE_CLOUD_PROJECT' environment variable not set."
83-
execute_workflow(PROJECT, LOCATION, WORKFLOW_ID)
84-
# [END workflows_api_quickstart]
95+
96+
execute_workflow(PROJECT, "us-central1", "myFirstWorkflow")

workflows/cloud-client/main_test.py

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,52 +12,15 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
import os
15+
import backoff
1616

17-
from google.cloud import workflows_v1
1817
from google.cloud.workflows.executions_v1.types import executions
1918

2019
import main
2120

22-
PROJECT = os.getenv("GOOGLE_CLOUD_PROJECT")
23-
LOCATION = "us-central1"
24-
WORKFLOW_ID = "myFirstWorkflow"
2521

26-
27-
def test_workflow_execution() -> None:
28-
assert PROJECT, "'GOOGLE_CLOUD_PROJECT' environment variable not set."
29-
30-
if not workflow_exists():
31-
workflow_file = open("myFirstWorkflow.workflows.yaml").read()
32-
33-
workflows_client = workflows_v1.WorkflowsClient()
34-
workflows_client.create_workflow(
35-
request={
36-
# Manually construct the location
37-
# https://github.com/googleapis/python-workflows/issues/21
38-
"parent": f"projects/{PROJECT}/locations/{LOCATION}",
39-
"workflow_id": WORKFLOW_ID,
40-
"workflow": {
41-
"name": WORKFLOW_ID,
42-
"source_contents": workflow_file
43-
},
44-
}
45-
)
46-
47-
result = main.execute_workflow(PROJECT, LOCATION, WORKFLOW_ID)
22+
@backoff.on_exception(backoff.expo, AssertionError, max_time=60)
23+
def test_workflow_execution(project_id: str, location: str, workflow_id: str) -> None:
24+
result = main.execute_workflow(project_id, location, workflow_id)
4825
assert result.state == executions.Execution.State.SUCCEEDED
49-
assert result.result # Result not empty
50-
51-
52-
def workflow_exists() -> bool:
53-
"""Returns True if the workflow exists in this project."""
54-
try:
55-
workflows_client = workflows_v1.WorkflowsClient()
56-
workflow_name = workflows_client.workflow_path(
57-
PROJECT, LOCATION, WORKFLOW_ID
58-
)
59-
workflows_client.get_workflow(request={"name": workflow_name})
60-
return True
61-
except Exception as e:
62-
print(f"Workflow doesn't exist: {e}")
63-
return False
26+
assert result.result

workflows/cloud-client/noxfile_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
TEST_CONFIG_OVERRIDE = {
2424
# You can opt out from the test for specific Python versions.
25-
"ignored_versions": ["2.7", "3.7", "3.8", "3.10", "3.11"],
25+
"ignored_versions": ["2.7", "3.7", "3.8"],
2626
# Old samples are opted out of enforcing Python type hints
2727
# All new samples should feature them
2828
"enforce_type_hints": True,
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
pytest==8.2.0
1+
pytest==8.3.5
2+
backoff==2.2.1
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
google-cloud-workflows==1.15.1
1+
google-cloud-workflows==1.18.0

0 commit comments

Comments
 (0)