Skip to content

Commit 01656ed

Browse files
oauth token all functions update
1 parent 0b2afd2 commit 01656ed

7 files changed

Lines changed: 766 additions & 0 deletions

File tree

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Complete OAuth Token Testing Suite
4+
5+
This file contains individual tests for all 4 OAuth token functions implemented in src/tfe/resources/oauth_token.py:
6+
7+
FUNCTIONS AVAILABLE FOR TESTING:
8+
1. list() - List OAuth tokens for an organization
9+
2. read() - Read an OAuth token by its ID
10+
3. update() - Update an existing OAuth token
11+
4. delete() - Delete an OAuth token by its ID
12+
13+
USAGE:
14+
- Uncomment specific test sections to test individual functions
15+
- Modify test data (token IDs, SSH keys, etc.) as needed for your environment
16+
- Ensure you have proper TFE credentials and organization access
17+
- Note: OAuth tokens are automatically created when OAuth clients are created
18+
19+
PREREQUISITES:
20+
- You need existing OAuth clients/tokens to test with
21+
- Set TFE_TOKEN and TFE_ADDRESS environment variables
22+
- Organization 'aayush-test' should exist with OAuth clients
23+
"""
24+
25+
import os
26+
import sys
27+
28+
# Add the src directory to the path
29+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
30+
31+
from tfe import TFEClient, TFEConfig
32+
from tfe.errors import NotFound
33+
from tfe.models.oauth_token import OAuthTokenListOptions, OAuthTokenUpdateOptions
34+
35+
36+
def main():
37+
"""Test all OAuth token functions individually."""
38+
39+
print("=" * 80)
40+
print("OAUTH TOKEN COMPLETE TESTING SUITE")
41+
print("=" * 80)
42+
print("Testing ALL 4 functions in src/tfe/resources/oauth_token.py")
43+
print("Comprehensive test coverage for all OAuth token operations")
44+
print("=" * 80)
45+
46+
# Initialize the TFE client
47+
client = TFEClient(TFEConfig.from_env())
48+
organization_name = "aayush-test" # Using specified organization
49+
50+
# Variables to store found resources for dependent tests
51+
test_token_id = None
52+
53+
# =====================================================
54+
# TEST 1: LIST OAUTH TOKENS
55+
# =====================================================
56+
print("\n1. Testing list() function:")
57+
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}")
69+
if token.oauth_client:
70+
print(f" OAuth Client: {token.oauth_client.id}")
71+
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}")
86+
87+
except NotFound:
88+
print(" ✓ No OAuth tokens found (organization may not exist or no tokens available)")
89+
except Exception as e:
90+
print(f" ✗ Error: {e}")
91+
92+
# =====================================================
93+
# TEST 2: READ OAUTH TOKEN
94+
# =====================================================
95+
if test_token_id:
96+
print("\n2. Testing read() function:")
97+
try:
98+
token = client.oauth_tokens.read(test_token_id)
99+
print(f" ✓ Read OAuth token: {token.id}")
100+
print(f" UID: {token.uid}")
101+
print(f" Service Provider User: {token.service_provider_user}")
102+
print(f" Has SSH Key: {token.has_ssh_key}")
103+
print(f" Created: {token.created_at}")
104+
if token.oauth_client:
105+
print(f" OAuth Client: {token.oauth_client.id}")
106+
107+
except Exception as e:
108+
print(f" ✗ Error: {e}")
109+
else:
110+
print("\n2. Testing read() function:")
111+
print(" ⚠ Skipped - No OAuth token available to read")
112+
113+
# =====================================================
114+
# TEST 3: UPDATE OAUTH TOKEN
115+
# =====================================================
116+
if test_token_id:
117+
print("\n3. Testing update() function:")
118+
try:
119+
# Test updating with SSH key
120+
print(" Testing update with SSH key...")
121+
ssh_key = """-----BEGIN RSA PRIVATE KEY-----
122+
MIIEpAIBAAKCAQEA2Z5QY1YZjV5xUFoGwdCrqLKxOcP7sQHGDSbxHjM1YIFbJvFj
123+
dHX2QqOyHfGTUPKN2ZN5s0rWzN3hFcJy4J5QY1YZjV5xUFoGwdCrqLKxOcP7sQHG
124+
DSbxHjM1YIFbJvFjdHX2QqOyHfGTUPKN2ZN5s0rWzN3hFcJy4wIDAQABAoIBAE1M
125+
h5k9J3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3
126+
Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3
127+
Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3
128+
Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3
129+
Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3
130+
Z1n8X3c4l3X8Y2v3Z1n8QwECggEBAO1X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X
131+
3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8X3c4l3X8Y2v3Z1n8
132+
-----END RSA PRIVATE KEY-----"""
133+
134+
options = OAuthTokenUpdateOptions(private_ssh_key=ssh_key)
135+
updated_token = client.oauth_tokens.update(test_token_id, options)
136+
print(f" ✓ Updated OAuth token: {updated_token.id}")
137+
print(f" Has SSH Key after update: {updated_token.has_ssh_key}")
138+
139+
# Test updating without SSH key (no changes)
140+
print("\n Testing update without changes...")
141+
options_empty = OAuthTokenUpdateOptions()
142+
updated_token_2 = client.oauth_tokens.update(test_token_id, options_empty)
143+
print(f" ✓ Updated OAuth token (no changes): {updated_token_2.id}")
144+
145+
except Exception as e:
146+
print(f" ✗ Error: {e}")
147+
print(" Note: This may fail if the SSH key format is invalid or constraints apply")
148+
else:
149+
print("\n3. Testing update() function:")
150+
print(" ⚠ Skipped - No OAuth token available to update")
151+
152+
# =====================================================
153+
# TEST 4: DELETE OAUTH TOKEN
154+
# =====================================================
155+
print("\n4. Testing delete() function:")
156+
157+
# Using specific OAuth token ID for deletion
158+
delete_token_id = "ot-WQf5ARHA1Qxzo9d4"
159+
160+
try:
161+
print(f" Attempting to delete OAuth token: {delete_token_id}")
162+
client.oauth_tokens.delete(delete_token_id)
163+
print(f" ✓ Successfully deleted OAuth token: {delete_token_id}")
164+
165+
# Verify deletion by trying to read the token
166+
try:
167+
client.oauth_tokens.read(delete_token_id)
168+
print(" ✗ Token still exists after deletion!")
169+
except NotFound:
170+
print(" ✓ Confirmed token was deleted - no longer accessible")
171+
except Exception as e:
172+
print(f" ? Verification failed: {e}")
173+
174+
except Exception as e:
175+
print(f" ✗ Error deleting token: {e}")
176+
177+
# Uncomment the following section ONLY if you have a disposable OAuth token
178+
# WARNING: This will permanently delete the OAuth token!
179+
"""
180+
if test_token_id:
181+
try:
182+
print(f" Attempting to delete OAuth token: {test_token_id}")
183+
client.oauth_tokens.delete(test_token_id)
184+
print(f" ✓ Successfully deleted OAuth token: {test_token_id}")
185+
186+
# Verify deletion by trying to read the token
187+
try:
188+
client.oauth_tokens.read(test_token_id)
189+
print(f" ✗ Token still exists after deletion!")
190+
except NotFound:
191+
print(f" ✓ Confirmed token was deleted - no longer accessible")
192+
except Exception as e:
193+
print(f" ? Verification failed: {e}")
194+
195+
except Exception as e:
196+
print(f" ✗ Error deleting token: {e}")
197+
else:
198+
print(" ⚠ Skipped - No OAuth token available to delete")
199+
"""
200+
201+
# =====================================================
202+
# SUMMARY
203+
# =====================================================
204+
print("\n" + "=" * 80)
205+
print("OAUTH TOKEN TESTING COMPLETE")
206+
print("=" * 80)
207+
print("Functions tested:")
208+
print("✓ 1. list() - List OAuth tokens for organization")
209+
print("✓ 2. read() - Read OAuth token by ID")
210+
print("✓ 3. update() - Update existing OAuth token")
211+
print("✓ 4. delete() - Delete OAuth token (testing with ot-WQf5ARHA1Qxzo9d4)")
212+
print("")
213+
print("All OAuth token functions have been tested!")
214+
print("Check the output above for any errors or warnings.")
215+
print("=" * 80)
216+
217+
218+
if __name__ == "__main__":
219+
main()

src/tfe/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .resources.apply import Applies
88
from .resources.configuration_version import ConfigurationVersions
99
from .resources.oauth_client import OAuthClients
10+
from .resources.oauth_token import OAuthTokens
1011
from .resources.organizations import Organizations
1112
from .resources.plan import Plans
1213
from .resources.projects import Projects
@@ -43,6 +44,7 @@ def __init__(self, config: TFEConfig | None = None):
4344
ca_bundle=cfg.ca_bundle,
4445
)
4546
self.oauth_clients = OAuthClients(self._transport)
47+
self.oauth_tokens = OAuthTokens(self._transport)
4648
# Agent resources
4749
self.agent_pools = AgentPools(self._transport)
4850
self.agents = Agents(self._transport)

src/tfe/errors.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ class ErrStateVersionUploadNotSupported(TFEError): ...
9999
ERR_REQUIRED_PROJECT = "projects are required"
100100
ERR_PROJECT_MIN_LIMIT = "must specify at least one project"
101101

102+
# OAuth Token Error Constants
103+
ERR_INVALID_OAUTH_TOKEN_ID = "invalid OAuth token ID"
104+
102105
# SSH Key Error Constants
103106
ERR_INVALID_SSH_KEY_ID = "invalid SSH key ID"
104107

src/tfe/models/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@
5151
ServiceProviderType,
5252
)
5353

54+
# Re-export all OAuth token types
55+
from .oauth_token import (
56+
OAuthToken,
57+
OAuthTokenList,
58+
OAuthTokenListOptions,
59+
OAuthTokenUpdateOptions,
60+
)
61+
5462
# Re-export all query run types
5563
from .query_run import (
5664
QueryRun,
@@ -133,6 +141,11 @@
133141
"OAuthClientRemoveProjectsOptions",
134142
"OAuthClientUpdateOptions",
135143
"ServiceProviderType",
144+
# OAuth token types
145+
"OAuthToken",
146+
"OAuthTokenList",
147+
"OAuthTokenListOptions",
148+
"OAuthTokenUpdateOptions",
136149
# SSH key types
137150
"SSHKey",
138151
"SSHKeyCreateOptions",

src/tfe/models/oauth_token.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from __future__ import annotations
2+
3+
from datetime import datetime
4+
from typing import TYPE_CHECKING
5+
6+
from pydantic import BaseModel, ConfigDict, Field
7+
8+
if TYPE_CHECKING:
9+
from .oauth_client import OAuthClient
10+
11+
12+
class OAuthToken(BaseModel):
13+
"""OAuth token represents a VCS configuration including the associated OAuth token."""
14+
15+
model_config = ConfigDict(extra="forbid")
16+
17+
id: str = Field(..., description="OAuth token ID")
18+
uid: str = Field(..., description="OAuth token UID")
19+
created_at: datetime = Field(..., description="Creation timestamp")
20+
has_ssh_key: bool = Field(..., description="Whether the token has an SSH key")
21+
service_provider_user: str = Field(..., description="Service provider user")
22+
23+
# Relationships
24+
oauth_client: OAuthClient | None = Field(
25+
None, description="The associated OAuth client"
26+
)
27+
28+
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+
42+
class OAuthTokenListOptions(BaseModel):
43+
"""Options for listing OAuth tokens."""
44+
45+
model_config = ConfigDict(extra="forbid")
46+
47+
page_number: int | None = Field(None, description="Page number")
48+
page_size: int | None = Field(None, description="Page size")
49+
50+
51+
class OAuthTokenUpdateOptions(BaseModel):
52+
"""Options for updating an OAuth token."""
53+
54+
model_config = ConfigDict(extra="forbid")
55+
56+
private_ssh_key: str | None = Field(
57+
None, description="A private SSH key to be used for git clone operations"
58+
)
59+
60+
61+
# Rebuild models to resolve forward references
62+
try:
63+
from .oauth_client import OAuthClient # noqa: F401
64+
OAuthToken.model_rebuild()
65+
OAuthTokenList.model_rebuild()
66+
except ImportError:
67+
# If OAuthClient is not available, create a dummy class
68+
pass

0 commit comments

Comments
 (0)