Skip to content

Commit dbf25dd

Browse files
authored
Merge pull request #68 from hashicorp/refactor/Iterator-pattern
Refactor/iterator-pattern
2 parents 06a7097 + 4acc402 commit dbf25dd

17 files changed

Lines changed: 351 additions & 337 deletions

examples/oauth_token.py

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
from pytfe import TFEClient, TFEConfig
3232
from pytfe.errors import NotFound
33-
from pytfe.models import OAuthTokenListOptions, OAuthTokenUpdateOptions
33+
from pytfe.models import OAuthTokenUpdateOptions
3434

3535

3636
def main():
@@ -55,34 +55,18 @@ def main():
5555
# =====================================================
5656
print("\n1. Testing list() function:")
5757
try:
58-
# Test basic list without options
59-
token_list = client.oauth_tokens.list(organization_name)
60-
print(f" ✓ Found {len(token_list.items)} OAuth tokens")
61-
62-
# Show token details
63-
for i, token in enumerate(token_list.items[:3], 1): # Show first 3
64-
print(f" {i}. Token ID: {token.id}")
65-
print(f" UID: {token.uid}")
66-
print(f" Service Provider User: {token.service_provider_user}")
67-
print(f" Has SSH Key: {token.has_ssh_key}")
68-
print(f" Created: {token.created_at}")
58+
for token in client.oauth_tokens.list(organization_name):
59+
print(f" Token ID: {token.id}")
60+
print(f" Service Provider User: {token.service_provider_user}")
61+
print(f" Has SSH Key: {token.has_ssh_key}")
62+
print(f" Created: {token.created_at}")
6963
if token.oauth_client:
7064
print(f" OAuth Client: {token.oauth_client.id}")
7165

72-
# Store first token for subsequent tests
73-
if token_list.items:
74-
test_token_id = token_list.items[0].id
75-
print(f"\n Using token {test_token_id} for subsequent tests")
76-
77-
# Test list with options
78-
print("\n Testing list() with pagination options:")
79-
options = OAuthTokenListOptions(page_size=10, page_number=1)
80-
token_list_with_options = client.oauth_tokens.list(organization_name, options)
81-
print(f" ✓ Found {len(token_list_with_options.items)} tokens with options")
82-
if token_list_with_options.current_page:
83-
print(f" Current page: {token_list_with_options.current_page}")
84-
if token_list_with_options.total_count:
85-
print(f" Total count: {token_list_with_options.total_count}")
66+
# Store first token for subsequent tests
67+
if token and not test_token_id:
68+
test_token_id = token.id
69+
print(f"\n Using token {test_token_id} for subsequent tests \n")
8670

8771
except NotFound:
8872
print(
@@ -99,7 +83,6 @@ def main():
9983
try:
10084
token = client.oauth_tokens.read(test_token_id)
10185
print(f" ✓ Read OAuth token: {token.id}")
102-
print(f" UID: {token.uid}")
10386
print(f" Service Provider User: {token.service_provider_user}")
10487
print(f" Has SSH Key: {token.has_ssh_key}")
10588
print(f" Created: {token.created_at}")

examples/policy_evaluation.py

Lines changed: 43 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ def main():
2626
required=True,
2727
help="Task stage ID to list policy evaluations for",
2828
)
29-
parser.add_argument("--page", type=int, default=1)
3029
parser.add_argument("--page-size", type=int, default=20)
3130
args = parser.parse_args()
3231

@@ -41,62 +40,55 @@ def main():
4140
_print_header(f"Listing policy evaluations for task stage: {args.task_stage_id}")
4241

4342
options = PolicyEvaluationListOptions(
44-
page_number=args.page,
4543
page_size=args.page_size,
4644
)
4745

4846
try:
49-
pe_list = client.policy_evaluations.list(args.task_stage_id, options)
50-
51-
print(f"Total policy evaluations: {pe_list.total_count}")
52-
print(f"Page {pe_list.current_page} of {pe_list.total_pages}")
53-
print()
54-
55-
if not pe_list.items:
47+
pe_count = 0
48+
for pe in client.policy_evaluations.list(args.task_stage_id, options):
49+
pe_count += 1
50+
print(f"- ID: {pe.id}")
51+
print(f" Status: {pe.status}")
52+
print(f" Policy Kind: {pe.policy_kind}")
53+
54+
if pe.result_count:
55+
print(" Result Count:")
56+
if pe.result_count.passed is not None:
57+
print(f" - Passed: {pe.result_count.passed}")
58+
if pe.result_count.advisory_failed is not None:
59+
print(f" - Advisory Failed: {pe.result_count.advisory_failed}")
60+
if pe.result_count.mandatory_failed is not None:
61+
print(f" - Mandatory Failed: {pe.result_count.mandatory_failed}")
62+
if pe.result_count.errored is not None:
63+
print(f" - Errored: {pe.result_count.errored}")
64+
65+
if pe.status_timestamp:
66+
print(" Status Timestamps:")
67+
if pe.status_timestamp.passed_at:
68+
print(f" - Passed At: {pe.status_timestamp.passed_at}")
69+
if pe.status_timestamp.failed_at:
70+
print(f" - Failed At: {pe.status_timestamp.failed_at}")
71+
if pe.status_timestamp.running_at:
72+
print(f" - Running At: {pe.status_timestamp.running_at}")
73+
if pe.status_timestamp.canceled_at:
74+
print(f" - Canceled At: {pe.status_timestamp.canceled_at}")
75+
if pe.status_timestamp.errored_at:
76+
print(f" - Errored At: {pe.status_timestamp.errored_at}")
77+
78+
if pe.policy_attachable:
79+
print(f" Task Stage: {pe.task_stage.id} ({pe.task_stage.type})")
80+
81+
if pe.created_at:
82+
print(f" Created At: {pe.created_at}")
83+
if pe.updated_at:
84+
print(f" Updated At: {pe.updated_at}")
85+
86+
print()
87+
88+
if pe_count == 0:
5689
print("No policy evaluations found for this task stage.")
5790
else:
58-
for pe in pe_list.items:
59-
print(f"- ID: {pe.id}")
60-
print(f" Status: {pe.status}")
61-
print(f" Policy Kind: {pe.policy_kind}")
62-
63-
if pe.result_count:
64-
print(" Result Count:")
65-
if pe.result_count.passed is not None:
66-
print(f" - Passed: {pe.result_count.passed}")
67-
if pe.result_count.advisory_failed is not None:
68-
print(
69-
f" - Advisory Failed: {pe.result_count.advisory_failed}"
70-
)
71-
if pe.result_count.mandatory_failed is not None:
72-
print(
73-
f" - Mandatory Failed: {pe.result_count.mandatory_failed}"
74-
)
75-
if pe.result_count.errored is not None:
76-
print(f" - Errored: {pe.result_count.errored}")
77-
78-
if pe.status_timestamp:
79-
print(" Status Timestamps:")
80-
if pe.status_timestamp.passed_at:
81-
print(f" - Passed At: {pe.status_timestamp.passed_at}")
82-
if pe.status_timestamp.failed_at:
83-
print(f" - Failed At: {pe.status_timestamp.failed_at}")
84-
if pe.status_timestamp.running_at:
85-
print(f" - Running At: {pe.status_timestamp.running_at}")
86-
if pe.status_timestamp.canceled_at:
87-
print(f" - Canceled At: {pe.status_timestamp.canceled_at}")
88-
if pe.status_timestamp.errored_at:
89-
print(f" - Errored At: {pe.status_timestamp.errored_at}")
90-
91-
if pe.task_stage:
92-
print(f" Task Stage: {pe.task_stage.id} ({pe.task_stage.type})")
93-
94-
if pe.created_at:
95-
print(f" Created At: {pe.created_at}")
96-
if pe.updated_at:
97-
print(f" Updated At: {pe.updated_at}")
98-
99-
print()
91+
print(f"\nTotal: {pe_count} policy evaluations")
10092

10193
except Exception as e:
10294
print(f"Error listing policy evaluations: {e}")

examples/reserved_tag_key.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ def main():
5353
try:
5454
# 1. List existing reserved tag keys
5555
print("\n1. Listing reserved tag keys...")
56-
reserved_tag_keys = client.reserved_tag_key.list(TFE_ORG)
57-
print(f"✅ Found {len(reserved_tag_keys.items)} reserved tag keys:")
58-
for rtk in reserved_tag_keys.items:
56+
for rtk in client.reserved_tag_key.list(TFE_ORG):
5957
print(
6058
f" - ID: {rtk.id}, Key: {rtk.key}, Disable Overrides: {rtk.disable_overrides}"
6159
)
@@ -87,18 +85,16 @@ def main():
8785

8886
# 5. Verify deletion by listing again
8987
print("\n5. Verifying deletion...")
90-
reserved_tag_keys_after = client.reserved_tag_key.list(TFE_ORG)
91-
print(
92-
f"✅ Reserved tag keys after deletion: {len(reserved_tag_keys_after.items)}"
93-
)
88+
reserved_tag_keys_after = list(client.reserved_tag_key.list(TFE_ORG))
89+
print(f"Reserved tag keys after deletion: {len(reserved_tag_keys_after)}")
9490

9591
# 6. Demonstrate pagination with options
9692
print("\n6. Demonstrating pagination options...")
97-
list_options = ReservedTagKeyListOptions(page_size=5, page_number=1)
98-
paginated_rtks = client.reserved_tag_key.list(TFE_ORG, list_options)
99-
print(f"✅ Page 1 with page size 5: {len(paginated_rtks.items)} keys")
100-
print(f" Total pages: {paginated_rtks.total_pages}")
101-
print(f" Total count: {paginated_rtks.total_count}")
93+
list_options = ReservedTagKeyListOptions(page_size=5)
94+
for rtk in client.reserved_tag_key.list(TFE_ORG, list_options):
95+
print(
96+
f" - ID: {rtk.id}, Key: {rtk.key}, Disable Overrides: {rtk.disable_overrides}"
97+
)
10298

10399
print("\n🎉 Reserved Tag Keys API example completed successfully!")
104100

src/pytfe/client.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
from .resources.policy_check import PolicyChecks
1616
from .resources.policy_evaluation import PolicyEvaluations
1717
from .resources.policy_set import PolicySets
18-
from .resources.policy_set_outcome import PolicySets as PolicySetOutcomes
18+
from .resources.policy_set_outcome import PolicySetOutcomes
1919
from .resources.policy_set_version import PolicySetVersions
2020
from .resources.projects import Projects
2121
from .resources.query_run import QueryRuns
2222
from .resources.registry_module import RegistryModules
2323
from .resources.registry_provider import RegistryProviders
2424
from .resources.registry_provider_version import RegistryProviderVersions
25-
from .resources.reserved_tag_key import ReservedTagKey
25+
from .resources.reserved_tag_key import ReservedTagKeys
2626
from .resources.run import Runs
2727
from .resources.run_event import RunEvents
2828
from .resources.run_task import RunTasks
@@ -93,7 +93,7 @@ def __init__(self, config: TFEConfig | None = None):
9393
self.ssh_keys = SSHKeys(self._transport)
9494

9595
# Reserved Tag Key
96-
self.reserved_tag_key = ReservedTagKey(self._transport)
96+
self.reserved_tag_key = ReservedTagKeys(self._transport)
9797

9898
def close(self) -> None:
9999
try:

src/pytfe/errors.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,3 +482,11 @@ class InvalidKeyIDError(InvalidValues):
482482

483483
def __init__(self, message: str = "invalid value for key-id"):
484484
super().__init__(message)
485+
486+
487+
# Policy Set Outcome errors
488+
class InvalidPolicySetOutcomeIDError(InvalidValues):
489+
"""Raised when an invalid policy set outcome ID is provided."""
490+
491+
def __init__(self, message: str = "invalid value for policy set outcome ID"):
492+
super().__init__(message)

src/pytfe/models/__init__.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@
7171
)
7272
from .oauth_token import (
7373
OAuthToken,
74-
OAuthTokenList,
7574
OAuthTokenListOptions,
7675
OAuthTokenUpdateOptions,
7776
)
@@ -110,7 +109,6 @@
110109
from .policy_evaluation import (
111110
PolicyAttachable,
112111
PolicyEvaluation,
113-
PolicyEvaluationList,
114112
PolicyEvaluationListOptions,
115113
PolicyEvaluationStatus,
116114
PolicyEvaluationStatusTimestamps,
@@ -214,7 +212,6 @@
214212
from .reserved_tag_key import (
215213
ReservedTagKey,
216214
ReservedTagKeyCreateOptions,
217-
ReservedTagKeyList,
218215
ReservedTagKeyListOptions,
219216
ReservedTagKeyUpdateOptions,
220217
)
@@ -355,7 +352,6 @@
355352
"ServiceProviderType",
356353
# OAuth token
357354
"OAuthToken",
358-
"OAuthTokenList",
359355
"OAuthTokenListOptions",
360356
"OAuthTokenUpdateOptions",
361357
# SSH keys
@@ -367,7 +363,6 @@
367363
# Reserved tag keys
368364
"ReservedTagKey",
369365
"ReservedTagKeyCreateOptions",
370-
"ReservedTagKeyList",
371366
"ReservedTagKeyListOptions",
372367
"ReservedTagKeyUpdateOptions",
373368
# Agent & pools
@@ -570,7 +565,6 @@
570565
# Policy Evaluation
571566
"PolicyAttachable",
572567
"PolicyEvaluation",
573-
"PolicyEvaluationList",
574568
"PolicyEvaluationListOptions",
575569
"PolicyEvaluationStatus",
576570
"PolicyEvaluationStatusTimestamps",

src/pytfe/models/oauth_token.py

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ class OAuthToken(BaseModel):
1515
model_config = ConfigDict(extra="forbid")
1616

1717
id: str = Field(..., description="OAuth token ID")
18-
uid: str = Field(..., description="OAuth token UID")
1918
created_at: datetime = Field(..., description="Creation timestamp")
2019
has_ssh_key: bool = Field(..., description="Whether the token has an SSH key")
2120
service_provider_user: str = Field(..., description="Service provider user")
@@ -26,26 +25,12 @@ class OAuthToken(BaseModel):
2625
)
2726

2827

29-
class OAuthTokenList(BaseModel):
30-
"""List of OAuth tokens with pagination information."""
31-
32-
model_config = ConfigDict(extra="forbid")
33-
34-
items: list[OAuthToken] = Field(default_factory=list, description="OAuth tokens")
35-
current_page: int | None = Field(None, description="Current page number")
36-
prev_page: int | None = Field(None, description="Previous page number")
37-
next_page: int | None = Field(None, description="Next page number")
38-
total_pages: int | None = Field(None, description="Total number of pages")
39-
total_count: int | None = Field(None, description="Total count of items")
40-
41-
4228
class OAuthTokenListOptions(BaseModel):
4329
"""Options for listing OAuth tokens."""
4430

45-
model_config = ConfigDict(extra="forbid")
31+
model_config = ConfigDict(populate_by_name=True, validate_by_name=True)
4632

47-
page_number: int | None = Field(None, description="Page number")
48-
page_size: int | None = Field(None, description="Page size")
33+
page_size: int | None = Field(None, alias="page[size]", description="Page size")
4934

5035

5136
class OAuthTokenUpdateOptions(BaseModel):
@@ -63,7 +48,6 @@ class OAuthTokenUpdateOptions(BaseModel):
6348
from .oauth_client import OAuthClient # noqa: F401
6449

6550
OAuthToken.model_rebuild()
66-
OAuthTokenList.model_rebuild()
6751
except ImportError:
6852
# If OAuthClient is not available, create a dummy class
6953
pass

src/pytfe/models/policy_evaluation.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class PolicyEvaluation(BaseModel):
3737
updated_at: datetime | None = Field(None, alias="updated-at")
3838

3939
# The task stage the policy evaluation belongs to
40-
task_stage: PolicyAttachable | None = Field(None, alias="policy-attachable")
40+
policy_attachable: PolicyAttachable | None = Field(None, alias="policy-attachable")
4141

4242

4343
class PolicyEvaluationStatusTimestamps(BaseModel):
@@ -72,23 +72,9 @@ class PolicyResultCount(BaseModel):
7272
errored: int | None = Field(None, alias="errored")
7373

7474

75-
class PolicyEvaluationList(BaseModel):
76-
"""PolicyEvaluationList represents a list of policy evaluations"""
77-
78-
model_config = ConfigDict(populate_by_name=True, validate_by_name=True)
79-
80-
items: list[PolicyEvaluation] | None = Field(default_factory=list)
81-
current_page: int | None = None
82-
next_page: str | None = None
83-
prev_page: str | None = None
84-
total_count: int | None = None
85-
total_pages: int | None = None
86-
87-
8875
class PolicyEvaluationListOptions(BaseModel):
8976
"""PolicyEvaluationListOptions represents the options for listing policy evaluations"""
9077

9178
model_config = ConfigDict(populate_by_name=True, validate_by_name=True)
9279

93-
page_number: int | None = Field(None, alias="page[number]")
9480
page_size: int | None = Field(None, alias="page[size]")

0 commit comments

Comments
 (0)