Skip to content

Commit c477653

Browse files
committed
Merge branch 'main' into test
2 parents 3ed2a83 + cd2bce3 commit c477653

File tree

13 files changed

+474
-65
lines changed

13 files changed

+474
-65
lines changed

docs/DEVELOPER_GUIDE.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,27 @@ If you want to contribute to the Workato CLI codebase itself, use these developm
104104
make test # Run tests and check code style
105105
```
106106

107+
### Testing with Test PyPI
108+
109+
When testing pre-release versions from Test PyPI, you need to use both Test PyPI and regular PyPI to resolve dependencies:
110+
111+
**Using pipx (Recommended):**
112+
```bash
113+
pipx install \
114+
--index-url https://test.pypi.org/simple/ \
115+
--pip-args="--extra-index-url https://pypi.org/simple/" \
116+
workato-platform-cli
117+
```
118+
119+
**Using pip:**
120+
```bash
121+
pip install \
122+
--index-url https://test.pypi.org/simple/ \
123+
--extra-index-url https://pypi.org/simple/ \
124+
workato-platform-cli
125+
```
126+
127+
**Why this is needed:**
128+
Some dependencies (like `aiohttp-retry`) are not available on Test PyPI. Using `--extra-index-url` tells pip to fall back to the regular PyPI index when a package is not found on Test PyPI.
129+
107130
These commands are for CLI maintainers and contributors, not for developers using the CLI to build Workato integrations.

src/workato_platform_cli/cli/commands/assets.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515

1616
@click.command()
1717
@click.option(
18-
"--folder-id", help="Folder ID (uses current project folder if not specified)"
18+
"--folder-id",
19+
type=int,
20+
help="Folder ID (uses current project folder if not specified)",
1921
)
2022
@handle_cli_exceptions
2123
@inject

src/workato_platform_cli/cli/commands/profiles.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,5 +360,53 @@ async def delete(
360360
click.echo(f"❌ Failed to delete profile '{profile_name}'")
361361

362362

363+
@profiles.command()
364+
@click.argument("profile_name")
365+
@handle_cli_exceptions
366+
@inject
367+
async def create(
368+
profile_name: str,
369+
config_manager: ConfigManager = Provide[Container.config_manager],
370+
) -> None:
371+
"""Create a new profile with API credentials"""
372+
# Check if profile already exists
373+
existing_profile = config_manager.profile_manager.get_profile(profile_name)
374+
if existing_profile:
375+
click.echo(f"❌ Profile '{profile_name}' already exists")
376+
click.echo("💡 Use 'workato profiles use' to switch to it")
377+
click.echo("💡 Or use 'workato profiles delete' to remove it first")
378+
return
379+
380+
click.echo(f"🔧 Creating profile: {profile_name}")
381+
click.echo()
382+
383+
# Create profile interactively
384+
try:
385+
(
386+
profile_data,
387+
token,
388+
) = await config_manager.profile_manager.create_profile_interactive(
389+
profile_name
390+
)
391+
except click.ClickException:
392+
click.echo("❌ Profile creation cancelled")
393+
return
394+
395+
# Save profile
396+
try:
397+
config_manager.profile_manager.set_profile(profile_name, profile_data, token)
398+
except ValueError as e:
399+
click.echo(f"❌ Failed to save profile: {e}")
400+
return
401+
402+
# Set as current profile
403+
config_manager.profile_manager.set_current_profile(profile_name)
404+
405+
click.echo(f"✅ Profile '{profile_name}' created successfully")
406+
click.echo(f"✅ Set '{profile_name}' as the active profile")
407+
click.echo()
408+
click.echo("💡 You can now use this profile with Workato CLI commands")
409+
410+
363411
# Add show as click argument command
364412
show = click.argument("profile_name")(show)

src/workato_platform_cli/cli/utils/config/manager.py

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -544,38 +544,9 @@ async def _prompt_and_validate_credentials(
544544

545545
async def _create_new_profile(self, profile_name: str) -> None:
546546
"""Create a new profile interactively"""
547-
# Select region
548-
click.echo("📍 Select your Workato region")
549-
region_result = await self.profile_manager.select_region_interactive()
550-
551-
if not region_result:
552-
raise click.ClickException("Setup cancelled")
553-
554-
selected_region = region_result
555-
556-
# Get API token
557-
token = await asyncio.to_thread(
558-
get_token_with_smart_paste,
559-
prompt_text="API token",
560-
)
561-
if not token.strip():
562-
raise click.ClickException("API token cannot be empty")
563-
564-
# Test authentication and get workspace info
565-
api_config = Configuration(
566-
access_token=token, host=selected_region.url, ssl_ca_cert=certifi.where()
567-
)
568-
569-
async with Workato(configuration=api_config) as workato_api_client:
570-
user_info = await workato_api_client.users_api.get_workspace_details()
571-
572-
# Create and save profile
573-
if not selected_region.url:
574-
raise click.ClickException("Region URL is required")
575-
profile_data = ProfileData(
576-
region=selected_region.region,
577-
region_url=selected_region.url,
578-
workspace_id=user_info.id,
547+
# Create profile using shared helper
548+
profile_data, token = await self.profile_manager.create_profile_interactive(
549+
profile_name
579550
)
580551

581552
# Save profile and token

src/workato_platform_cli/cli/utils/config/profiles.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Profile management for multiple Workato environments."""
22

3+
import asyncio
34
import contextlib
45
import json
56
import os
@@ -9,13 +10,18 @@
910
from urllib.parse import urlparse
1011

1112
import asyncclick as click
13+
import certifi
1214
import inquirer
1315
import keyring
1416

1517
from keyring.backend import KeyringBackend
1618
from keyring.compat import properties
1719
from keyring.errors import KeyringError, NoKeyringError
1820

21+
from workato_platform_cli import Workato
22+
from workato_platform_cli.cli.utils.token_input import get_token_with_smart_paste
23+
from workato_platform_cli.client.workato_api.configuration import Configuration
24+
1925
from .models import AVAILABLE_REGIONS, ProfileData, ProfilesConfig, RegionInfo
2026

2127

@@ -489,3 +495,62 @@ async def select_region_interactive(
489495
return RegionInfo(region="custom", name="Custom URL", url=custom_url)
490496

491497
return selected_region
498+
499+
async def create_profile_interactive(
500+
self, profile_name: str
501+
) -> tuple[ProfileData, str]:
502+
"""Create a new profile with interactive prompts for region and token.
503+
504+
Performs region selection, token input, credential validation, and returns
505+
the validated profile data and token.
506+
507+
Args:
508+
profile_name: Name of the profile being created
509+
510+
Returns:
511+
tuple[ProfileData, str]: The validated profile data and API token
512+
513+
Raises:
514+
click.ClickException: If setup is cancelled, token is empty,
515+
or validation fails
516+
"""
517+
# Step 1: Select region
518+
click.echo("📍 Select your Workato region")
519+
selected_region = await self.select_region_interactive(profile_name)
520+
521+
if not selected_region:
522+
raise click.ClickException("Setup cancelled")
523+
524+
# Step 2: Get API token
525+
token = await asyncio.to_thread(
526+
get_token_with_smart_paste,
527+
prompt_text="API token",
528+
)
529+
if not token.strip():
530+
raise click.ClickException("API token cannot be empty")
531+
532+
# Step 3: Test authentication and get workspace info
533+
click.echo("🔄 Validating credentials...")
534+
api_config = Configuration(
535+
access_token=token, host=selected_region.url, ssl_ca_cert=certifi.where()
536+
)
537+
538+
try:
539+
async with Workato(configuration=api_config) as workato_api_client:
540+
user_info = await workato_api_client.users_api.get_workspace_details()
541+
except Exception as e:
542+
raise click.ClickException(f"Authentication failed: {e}") from e
543+
544+
# Step 4: Create profile data
545+
if not selected_region.url:
546+
raise click.ClickException("Region URL is required")
547+
548+
profile_data = ProfileData(
549+
region=selected_region.region,
550+
region_url=selected_region.url,
551+
workspace_id=user_info.id,
552+
)
553+
554+
click.echo(f"✅ Authenticated as: {user_info.name}")
555+
556+
return profile_data, token

src/workato_platform_cli/client/workato_api/models/asset.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ class Asset(BaseModel):
4242
@field_validator('type')
4343
def type_validate_enum(cls, value):
4444
"""Validates the enum"""
45-
if value not in set(['recipe', 'connection', 'lookup_table', 'workato_db_table', 'account_property', 'project_property', 'workato_schema', 'workato_template', 'lcap_app', 'lcap_page', 'custom_adapter', 'topic', 'api_group', 'api_endpoint']):
46-
raise ValueError("must be one of enum values ('recipe', 'connection', 'lookup_table', 'workato_db_table', 'account_property', 'project_property', 'workato_schema', 'workato_template', 'lcap_app', 'lcap_page', 'custom_adapter', 'topic', 'api_group', 'api_endpoint')")
45+
if value not in set(['recipe', 'connection', 'lookup_table', 'workato_db_table', 'account_property', 'project_property', 'workato_schema', 'workato_template', 'lcap_app', 'lcap_page', 'custom_adapter', 'topic', 'api_group', 'api_endpoint', 'agentic_genie', 'agentic_skill', 'data_pipeline', 'decision_engine_model', 'agentic_knowledge_base', 'mcp_server']):
46+
raise ValueError("must be one of enum values ('recipe', 'connection', 'lookup_table', 'workato_db_table', 'account_property', 'project_property', 'workato_schema', 'workato_template', 'lcap_app', 'lcap_page', 'custom_adapter', 'topic', 'api_group', 'api_endpoint', 'agentic_genie', 'agentic_skill', 'data_pipeline', 'decision_engine_model', 'agentic_knowledge_base', 'mcp_server')")
4747
return value
4848

4949
@field_validator('status')

src/workato_platform_cli/client/workato_api/models/asset_reference.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ class AssetReference(BaseModel):
4040
@field_validator('type')
4141
def type_validate_enum(cls, value):
4242
"""Validates the enum"""
43-
if value not in set(['recipe', 'connection', 'lookup_table', 'workato_db_table', 'account_property', 'project_property', 'workato_schema', 'workato_template', 'lcap_app', 'lcap_page', 'custom_adapter', 'topic', 'api_group', 'api_endpoint']):
44-
raise ValueError("must be one of enum values ('recipe', 'connection', 'lookup_table', 'workato_db_table', 'account_property', 'project_property', 'workato_schema', 'workato_template', 'lcap_app', 'lcap_page', 'custom_adapter', 'topic', 'api_group', 'api_endpoint')")
43+
if value not in set(['recipe', 'connection', 'lookup_table', 'workato_db_table', 'account_property', 'project_property', 'workato_schema', 'workato_template', 'lcap_app', 'lcap_page', 'custom_adapter', 'topic', 'api_group', 'api_endpoint', 'agentic_genie', 'agentic_skill', 'data_pipeline', 'decision_engine_model', 'agentic_knowledge_base', 'mcp_server']):
44+
raise ValueError("must be one of enum values ('recipe', 'connection', 'lookup_table', 'workato_db_table', 'account_property', 'project_property', 'workato_schema', 'workato_template', 'lcap_app', 'lcap_page', 'custom_adapter', 'topic', 'api_group', 'api_endpoint', 'agentic_genie', 'agentic_skill', 'data_pipeline', 'decision_engine_model', 'agentic_knowledge_base', 'mcp_server')")
4545
return value
4646

4747
model_config = ConfigDict(

0 commit comments

Comments
 (0)