Skip to content

Commit b0d88b8

Browse files
committed
chore: update code
1 parent a37cd23 commit b0d88b8

File tree

15 files changed

+392
-373
lines changed

15 files changed

+392
-373
lines changed

justfile

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Prints this help message
2+
default:
3+
@just --list
4+
5+
RIO := "timeout 5 uv run rio"
6+
PYTEST := "uv run pytest"
7+
DIR := justfile_directory() + "/tests"
8+
VERBOSE := "false"
9+
COVERAGE := "false"
10+
11+
12+
# Run all tests sequentially to avoid resource conflicts
13+
run-all COVERAGE="false":
14+
#!/bin/bash
15+
set -euo pipefail
16+
echo "Running all tests sequentially to prevent resource conflicts..."
17+
18+
# Set coverage flags if requested
19+
COVERAGE_FLAGS=""
20+
if [ "{{COVERAGE}}" = "true" ]; then
21+
COVERAGE_FLAGS="--cov=riocli --cov-report=html --cov-report=term-missing"
22+
echo "Coverage reporting enabled"
23+
fi
24+
25+
# Run tests in a specific order to manage dependencies
26+
echo "=== Running Authentication Tests ==="
27+
{{PYTEST}} {{DIR}}/main/test_auth.py -v $COVERAGE_FLAGS
28+
29+
echo "=== Running Project Tests ==="
30+
{{PYTEST}} {{DIR}}/main/test_project.py -v $COVERAGE_FLAGS
31+
32+
echo "=== Running Package Tests ==="
33+
{{PYTEST}} {{DIR}}/main/test_packages.py -v $COVERAGE_FLAGS
34+
35+
echo "=== Running Secret Tests ==="
36+
{{PYTEST}} {{DIR}}/main/test_secrets.py -v $COVERAGE_FLAGS
37+
38+
echo "=== Running Static Route Tests ==="
39+
{{PYTEST}} {{DIR}}/main/test_staticroute.py -v $COVERAGE_FLAGS
40+
41+
echo "=== Running Service Account Tests ==="
42+
{{PYTEST}} {{DIR}}/main/test_service_accounts.py -v $COVERAGE_FLAGS
43+
44+
echo "=== Running Deployment Tests ==="
45+
{{PYTEST}} {{DIR}}/main/test_deployment.py -v $COVERAGE_FLAGS
46+
47+
echo "=== Running Usergroup Tests ==="
48+
{{PYTEST}} {{DIR}}/main/test_usergroup.py -v $COVERAGE_FLAGS
49+
50+
echo "All tests completed successfully!"
51+
if [ "{{COVERAGE}}" = "true" ]; then
52+
echo "Coverage report generated in htmlcov/ directory"
53+
fi
54+
55+
# Run tests for a specific resource type
56+
run RESOURCE COVERAGE="false":
57+
#!/bin/bash
58+
set -euo pipefail
59+
60+
# Set coverage flags if requested
61+
COVERAGE_FLAGS=""
62+
if [ "{{COVERAGE}}" = "true" ]; then
63+
COVERAGE_FLAGS="--cov=riocli --cov-report=html --cov-report=term-missing"
64+
echo "Coverage reporting enabled"
65+
fi
66+
67+
case "{{RESOURCE}}" in
68+
"auth"|"authentication")
69+
echo "Running Authentication tests..."
70+
{{PYTEST}} {{DIR}}/main/test_auth.py -v $COVERAGE_FLAGS
71+
;;
72+
"project"|"projects")
73+
echo "Running Project tests..."
74+
{{PYTEST}} {{DIR}}/main/test_project.py -v $COVERAGE_FLAGS
75+
;;
76+
"package"|"packages")
77+
echo "Running Package tests..."
78+
{{PYTEST}} {{DIR}}/main/test_packages.py -v $COVERAGE_FLAGS
79+
;;
80+
"secret"|"secrets")
81+
echo "Running Secret tests..."
82+
{{PYTEST}} {{DIR}}/main/test_secrets.py -v $COVERAGE_FLAGS
83+
;;
84+
"staticroute"|"static-route"|"static_route")
85+
echo "Running Static Route tests..."
86+
{{PYTEST}} {{DIR}}/main/test_staticroute.py -v $COVERAGE_FLAGS
87+
;;
88+
"service-account"|"service_account"|"serviceaccount"|"sa")
89+
echo "Running Service Account tests..."
90+
{{PYTEST}} {{DIR}}/main/test_service_accounts.py -v $COVERAGE_FLAGS
91+
;;
92+
"deployment"|"deployments")
93+
echo "Running Deployment tests..."
94+
{{PYTEST}} {{DIR}}/main/test_deployment.py -v $COVERAGE_FLAGS
95+
;;
96+
"usergroup"|"usergroups"|"user-group"|"user_group")
97+
echo "Running Usergroup tests..."
98+
{{PYTEST}} {{DIR}}/main/test_usergroup.py -v $COVERAGE_FLAGS
99+
;;
100+
"all")
101+
just run-all {{COVERAGE}}
102+
;;
103+
*)
104+
echo "Unknown resource type: {{RESOURCE}}"
105+
echo "Available resources: auth, project, package, secret, staticroute, service-account, deployment, usergroup"
106+
echo "Use 'just run all' to run all tests"
107+
echo "Add 'true' as second argument to enable coverage: just run <resource> true"
108+
exit 1
109+
;;
110+
esac
111+
112+
if [ "{{COVERAGE}}" = "true" ]; then
113+
echo "Coverage report generated in htmlcov/ directory"
114+
fi

riocli/apply/parse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ def _get_dependency_graph(
294294

295295
if dependencies is not None:
296296
for d in dependencies:
297-
graph.add(d, key)
297+
graph.add(key, d)
298298
diagram.edge(key, d)
299299

300300
return graph, diagram

riocli/auth/login.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
# limitations under the License.
1414
import click
1515
from click_help_colors import HelpColorsCommand
16+
from munch import munchify
1617

1718
from riocli.auth.util import (
1819
get_token,
1920
select_organization,
2021
select_project,
21-
validate_and_set_token,
2222
)
2323
from riocli.config import get_config_from_context
2424
from riocli.constants import Colors, Symbols
@@ -127,8 +127,13 @@ def login(
127127

128128
if auth_token:
129129
config.data["auth_token"] = auth_token
130-
if not validate_and_set_token(ctx, config):
131-
raise SystemExit(1)
130+
# if not validate_and_set_token(ctx, config, interactive=interactive):
131+
# raise SystemExit(1)
132+
v2_cli = config.new_v2_client()
133+
134+
subject = munchify(v2_cli.get_subject(auth_token))
135+
136+
config.data["email_id"] = subject.data.email
132137
else:
133138
if interactive:
134139
email = email or click.prompt("Email")

riocli/auth/util.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,10 @@ def api_refresh_token(
204204

205205
@with_spinner(text="Validating token...")
206206
def validate_and_set_token(
207-
ctx: click.Context, config: Configuration, spinner=None
207+
ctx: click.Context,
208+
config: Configuration,
209+
interactive: bool,
210+
spinner=None,
208211
) -> bool:
209212
"""Validates an auth token."""
210213
if "environment" in ctx.obj.data:

riocli/configtree/import_keys.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,9 @@ def import_keys(
208208

209209
for key, value in data.items():
210210
key_metadata = metadata.get(key, None)
211-
if key_metadata is not None and isinstance(key_metadata, Metadata):
211+
if isinstance(key_metadata, Metadata):
212212
key_metadata = key_metadata.get_dict()
213-
214-
rev.store(key=key, value=str(value), perms=644, metadata=key_metadata)
213+
rev.store(key=key, value=value, perms=644, metadata=key_metadata)
215214
spinner.write(
216215
click.style(
217216
f"\t{Symbols.SUCCESS} Key {key} added.",

riocli/configtree/merge.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def merge_revisions(
155155
if key_metadata is not None and isinstance(key_metadata, Metadata):
156156
key_metadata = key_metadata.get_dict()
157157

158-
rev.store(key=key, value=str(value), perms=644, metadata=key_metadata)
158+
rev.store(key=key, value=value, perms=644, metadata=key_metadata)
159159

160160
payload = {
161161
"kind": "ConfigTree",

riocli/configtree/revision.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414
from __future__ import annotations
1515

16+
import json
1617
import os
1718
from base64 import b64encode
1819
from hashlib import md5
@@ -107,6 +108,9 @@ def store(
107108
perms: int = 644,
108109
metadata: dict | None = None,
109110
) -> None:
111+
# Fix: Ensure non-string values are serialized to JSON
112+
if not isinstance(value, str):
113+
value = json.dumps(value, ensure_ascii=False)
110114
str_val = str(value)
111115
enc_val = str_val.encode("utf-8")
112116

riocli/deployment/model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def delete_object(self, v2_client: Client, *args, **kwargs) -> None:
6161

6262
@override
6363
def list_dependencies(self) -> list[str] | None:
64-
self._obj.list_dependencies()
64+
return self._obj.list_dependencies()
6565

6666

6767
def wait_for_dependencies(

riocli/service_account/token.py

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,19 @@ def list_tokens(
7272

7373
data = []
7474
for token in tokens.items:
75+
# Handle None expiry_at (infinite duration tokens)
76+
if token.expiry_at is None:
77+
expiry_display = "No expiry (infinite)"
78+
time_remaining = "Never expires"
79+
else:
80+
expiry_display = convert_utc_to_offset(token.expiry_at, offset_str=offset)
81+
time_remaining = calculate_time_remaining(token.expiry_at)
82+
7583
data.append(
7684
[
7785
token.id,
78-
convert_utc_to_offset(token.expiry_at, offset_str=offset),
79-
calculate_time_remaining(token.expiry_at),
86+
expiry_display,
87+
time_remaining,
8088
]
8189
)
8290
tabulate_data(data, headers)
@@ -92,7 +100,7 @@ def list_tokens(
92100
help_options_color=Colors.GREEN,
93101
)
94102
@click.argument("account-name", type=str)
95-
@click.argument("expiry-at", type=str)
103+
@click.argument("expiry-at", type=str, required=False, default=None)
96104
@click.option(
97105
"--owner", "owner", type=str, default=None, help="Define owner for the token."
98106
)
@@ -105,29 +113,48 @@ def list_tokens(
105113
)
106114
def create_token(
107115
account_name: str,
108-
expiry_at: str,
116+
expiry_at: str | None,
109117
owner: str,
110118
offset: str,
111119
) -> None:
112120
"""
113121
Create a token for a service account.
114122
115-
Set expiry timestamp to set an expiry time of the token.
116-
Expiry can be a relative time like '3 days' or an ISO 8601 timestamp.
123+
The EXPIRY-AT argument supports two formats:
124+
125+
1. Custom expiry: Specify a relative time (e.g., '3 days', '2 hours')
126+
or an ISO 8601 timestamp (e.g., '2025-12-31T23:59:59+00:00')
127+
128+
2. No expiry: Leave empty or omit the argument to create a token
129+
with infinite duration
117130
"""
118131
try:
119132
client = new_v2_client()
120133
iso_expiry_at = parse_human_readable_time_to_iso(expiry_at)
121-
token_config = ServiceAccountToken(
122-
expiry_at=iso_expiry_at,
123-
owner=owner,
124-
)
134+
135+
# Handle the three scenarios
136+
if iso_expiry_at is None:
137+
# Default expiry - let backend handle
138+
token_config = ServiceAccountToken(owner=owner)
139+
else:
140+
# Custom expiry or no expiry
141+
token_config = ServiceAccountToken(
142+
expiry_at=iso_expiry_at,
143+
owner=owner,
144+
)
125145

126146
token_obj = client.create_service_account_token(
127147
name=account_name, expiry_at=token_config
128148
)
149+
150+
# Handle display of expiry information
151+
if token_obj.expiry_at is None:
152+
expiry_msg = "no expiry (infinite duration)"
153+
else:
154+
expiry_msg = convert_utc_to_offset(token_obj.expiry_at, offset_str=offset)
155+
129156
click.secho(
130-
f"{Symbols.SUCCESS}Token Created with expiry at: {convert_utc_to_offset(token_obj.expiry_at, offset_str=offset)}",
157+
f"{Symbols.SUCCESS}Token created with expiry at: {expiry_msg}",
131158
fg=Colors.GREEN,
132159
)
133160
click.echo(token_obj.token)
@@ -144,7 +171,7 @@ def create_token(
144171
)
145172
@click.argument("account-name", type=str)
146173
@click.argument("id", type=str)
147-
@click.argument("expiry-at", type=str)
174+
@click.argument("expiry-at", type=str, required=False, default=None)
148175
@click.option(
149176
"--offset",
150177
"offset",
@@ -155,28 +182,49 @@ def create_token(
155182
def refresh_token(
156183
account_name: str,
157184
id: str,
158-
expiry_at: str,
185+
expiry_at: str | None,
159186
offset: str,
160187
) -> None:
161188
"""
162189
Refresh an existing token for a service account.
163190
164-
Set expiry timestamp to set a new expiry time for the token.
165-
Expiry can be a relative time like '3 days' or an ISO 8601 timestamp.
191+
The EXPIRY-AT argument supports two formats:
192+
193+
1. Custom expiry: Specify a relative time (e.g., '3 days', '2 hours')
194+
or an ISO 8601 timestamp (e.g., '2025-12-31T23:59:59+00:00')
195+
196+
2. No expiry: Leave empty or omit the argument to refresh the token
197+
with infinite duration
166198
"""
167199
try:
168200
client = new_v2_client()
169201
iso_expiry_at = parse_human_readable_time_to_iso(expiry_at)
170-
token_config = ServiceAccountToken(
171-
expiry_at=iso_expiry_at,
172-
)
202+
203+
# Handle the three scenarios
204+
if iso_expiry_at is None:
205+
# Default expiry - let backend handle
206+
token_config = ServiceAccountToken()
207+
else:
208+
# Custom expiry or no expiry
209+
token_config = ServiceAccountToken(
210+
expiry_at=iso_expiry_at,
211+
)
173212

174213
token_obj = client.refresh_service_account_token(
175214
name=account_name, token_id=id, expiry_at=token_config
176215
)
177216

217+
# Handle display of expiry information
218+
if token_obj.expiry_at is None:
219+
if expiry_at.strip() == "0":
220+
expiry_msg = "no expiry (infinite duration)"
221+
else:
222+
expiry_msg = "default expiry (90 days)"
223+
else:
224+
expiry_msg = convert_utc_to_offset(token_obj.expiry_at, offset_str=offset)
225+
178226
click.secho(
179-
f"{Symbols.SUCCESS}Token refreshed with expiry at: {convert_utc_to_offset(token_obj.expiry_at, offset_str=offset)}",
227+
f"{Symbols.SUCCESS}Token refreshed with expiry at: {expiry_msg}",
180228
fg=Colors.GREEN,
181229
)
182230
click.echo(token_obj.token)

0 commit comments

Comments
 (0)