Skip to content

Commit 258e345

Browse files
committed
Merge branch 'dev'
2 parents 360e19b + c27cbb3 commit 258e345

16 files changed

+604
-57
lines changed

naas_python/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .domains.registry.handlers.PythonHandler import primaryAdaptor as registry
22
from .domains.space.handlers.PythonHandler import primaryAdaptor as space
3+
from .domains.secret.handlers.PythonHandler import primaryAdaptor as secret
34
from .utils.log import initialize_logging
45

56
logger = initialize_logging()

naas_python/cli.py

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
primaryAdaptor as typerSpaceAdaptor,
1010
)
1111

12+
# from naas_python.domains import secretCliAdaptor # , secretCliAdaptor
13+
from naas_python.domains.secret.handlers.CLISecretHandler import (
14+
primaryAdaptor as typerSecretAdaptor,
15+
)
16+
1217

1318
def _create_cli_app():
1419
app = typer.Typer(
@@ -21,6 +26,7 @@ def _create_cli_app():
2126
# Registry domain's related commands
2227
app.add_typer(typerSpaceAdaptor.app, name="space")
2328
app.add_typer(typerRegistryAdaptor.app, name="registry")
29+
app.add_typer(typerSecretAdaptor.app, name="secret")
2430

2531
return app
2632

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from typing import List
2+
from .models.Secret import Secret
3+
4+
from typing import List
5+
6+
from naas_python.domains.secret.SecretSchema import (
7+
ISecretDomain,
8+
ISecretAdaptor,
9+
Secret,
10+
SecretListResponse,
11+
SecretResponseError,
12+
SecretError,
13+
SecretCreateResponse,
14+
SecretCreateRequest,
15+
# SecretCredentialsResponse,
16+
# SecretAdd
17+
)
18+
class SecretDomain(ISecretDomain):
19+
def __init__(self, adaptor: ISecretAdaptor):
20+
self.adaptor = adaptor
21+
22+
def create(self, name: str, value: str) -> None:
23+
response = self.adaptor.create_secret(
24+
name=name, value=value,
25+
)
26+
27+
return response
28+
29+
def get(self, name: str) -> Secret:
30+
response = self.adaptor.get_secret(name=name)
31+
return response
32+
33+
def delete(self, name: str) -> None:
34+
return self.adaptor.delete_secret(name=name)
35+
36+
def list(self, page_size: int, page_number: int) -> List[Secret]:
37+
secrets = self.adaptor.list_secrets(
38+
page_size=page_size, page_number=page_number
39+
)
40+
return secrets
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from abc import ABCMeta, abstractmethod
2+
from logging import getLogger
3+
from typing import Any
4+
from typing import List
5+
6+
from naas_models.pydantic.secret_p2p import *
7+
8+
from naas_python.utils.exceptions import NaasException
9+
10+
logger = getLogger(__name__)
11+
12+
# Secondary adaptor
13+
14+
15+
class ISecretAdaptor(metaclass=ABCMeta):
16+
17+
@abstractmethod
18+
def create_secret(self, name: str, value: str) -> None:
19+
raise NotImplementedError
20+
21+
@abstractmethod
22+
def get_secret(self, name: str) -> Secret:
23+
raise NotImplementedError
24+
25+
@abstractmethod
26+
def list_secrets(self, page_size: int, page_number: int) -> List[Secret]:
27+
raise NotImplementedError
28+
29+
@abstractmethod
30+
def delete_secret(self, name: str) -> None:
31+
raise NotImplementedError
32+
33+
34+
# Domain
35+
36+
37+
class ISecretDomain(metaclass=ABCMeta):
38+
adaptor: ISecretAdaptor
39+
40+
@abstractmethod
41+
def create(self, name: str, value: str) -> None:
42+
raise NotImplementedError
43+
44+
@abstractmethod
45+
def get(self, name: str) -> Secret:
46+
raise NotImplementedError
47+
48+
@abstractmethod
49+
def list(self, page_size: int, page_number: int) -> List[Secret]:
50+
raise NotImplementedError
51+
52+
@abstractmethod
53+
def delete(self, name: str) -> None:
54+
raise NotImplementedError
55+
56+
57+
# Primary Adaptor
58+
59+
60+
class ISecretInvoker(metaclass=ABCMeta):
61+
@abstractmethod
62+
def create(self, **kwargs):
63+
raise NotImplementedError
64+
65+
@abstractmethod
66+
def get(self, **kwargs):
67+
raise NotImplementedError
68+
69+
@abstractmethod
70+
def list(self, **kwargs):
71+
raise NotImplementedError
72+
73+
@abstractmethod
74+
def delete(self, **kwargs):
75+
raise NotImplementedError
76+
77+
78+
# Exceptions
79+
class SecretValidationError(NaasException):
80+
pass
81+
class SecretConflictError(NaasException):
82+
pass
83+
class SecretNotFound(NaasException):
84+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import os
2+
3+
from rich.panel import Panel
4+
from rich import print as rprint
5+
from typing import List
6+
7+
from naas_python.domains.secret.SecretSchema import (
8+
ISecretDomain,
9+
ISecretInvoker,
10+
SecretCreateResponse,
11+
SecretGetResponse,
12+
SecretListResponse,
13+
SecretDeleteResponse,
14+
Secret
15+
)
16+
17+
class SDKSecretAdaptor(ISecretInvoker):
18+
domain: ISecretDomain
19+
20+
def __init__(self, domain: ISecretDomain):
21+
self.domain = domain
22+
23+
def create(self, name: str = "", value: str ="") -> None:
24+
"""Create a secret with the given name"""
25+
secret = self.domain.create(name=name, value=value)
26+
return secret
27+
28+
def list(self, page_size: int = 0, page_number: int = 0) -> List[Secret]:
29+
"""List all secrets for the current user"""
30+
secret_list = self.domain.list(page_size=page_size, page_number=page_number)
31+
return secret_list
32+
33+
def get(self, name="") -> Secret:
34+
"""Get a secret with the given name"""
35+
secret = self.domain.get(name=name)
36+
return secret
37+
38+
def delete(self, name="") -> None:
39+
"""Delete a secret by name"""
40+
secret = self.domain.delete(name=name)
41+
return secret
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import json
2+
import os
3+
import time
4+
from logging import getLogger
5+
from typing import List
6+
from uuid import UUID
7+
8+
import rich
9+
import typer
10+
from click import Context
11+
from pydantic import BaseModel
12+
from rich.console import Console
13+
from rich.progress import Progress
14+
from rich.table import Table
15+
from typer.core import TyperGroup
16+
from typing_extensions import Annotated
17+
18+
from naas_python.domains.secret.adaptors.primary.utils import PydanticTableModel
19+
from naas_python.domains.secret.SecretSchema import (
20+
ISecretDomain,
21+
ISecretInvoker,
22+
SecretConflictError,
23+
)
24+
# from naas_python.domains.secret.SecretSchema import SecrettryConflictError
25+
from naas_python.utils.cicd import Pipeline
26+
27+
logger = getLogger(__name__)
28+
29+
30+
class OrderCommands(TyperGroup):
31+
def list_commands(self, ctx: Context):
32+
"""Return list of commands in the order appear."""
33+
return list(self.commands)
34+
35+
36+
class TyperSecretAdaptor(ISecretInvoker):
37+
def __init__(self, domain: ISecretDomain):
38+
super().__init__()
39+
40+
self.domain = domain
41+
self.console = Console()
42+
43+
self.app = typer.Typer(
44+
cls=OrderCommands,
45+
help="Naas Secret CLI",
46+
add_completion=False,
47+
no_args_is_help=True,
48+
pretty_exceptions_enable=False,
49+
rich_markup_mode="rich",
50+
context_settings={"help_option_names": ["-h", "--help"]},
51+
)
52+
53+
# Include all commands
54+
self.app.command()(self.list)
55+
self.app.command()(self.create)
56+
self.app.command()(self.get)
57+
self.app.command()(self.delete)
58+
59+
def _list_preview(self, data: List[dict], headers: list):
60+
if not isinstance(data, list):
61+
raise TypeError("Data must be a list of dicts, not {}".format(type(data)))
62+
63+
# Determine column widths based on the longest values
64+
column_widths = [max(len(str(item)) for item in col) for col in zip(*data)]
65+
66+
# Print the headers
67+
header_format = " ".join(
68+
f"{header:<{width}}" for header, width in zip(headers, column_widths)
69+
)
70+
print(header_format)
71+
72+
# Print the data
73+
for row in data:
74+
row_format = " ".join(
75+
f"{str(item):<{width}}" for item, width in zip(row, column_widths)
76+
)
77+
print(row_format)
78+
79+
def create(
80+
self,
81+
name: str = typer.Option(..., "--name", "-n", help="Name of the secret"),
82+
value: str = typer.Option(..., "--value", "-v", help="Value of the secret"),
83+
84+
rich_preview: bool = typer.Option(
85+
False,
86+
"--rich-preview",
87+
"-rp",
88+
help="Rich preview of the Secret information as a table",
89+
),
90+
):
91+
"""Create a Secret with the given specifications"""
92+
secret = self.domain.create(
93+
name=name,
94+
value=value
95+
)
96+
97+
if secret is None:
98+
print('Secret Successfully created')
99+
100+
def get(
101+
self,
102+
name: str = typer.Option(..., "--name", "-n", help="Name of the secret"),
103+
rich_preview: bool = typer.Option(
104+
os.environ.get("NAAS_CLI_RICH_PREVIEW", False),
105+
"--rich-preview",
106+
"-rp",
107+
help="Rich preview of the secret information as a table",
108+
),
109+
):
110+
"""Get a secret with the given name"""
111+
secret = self.domain.get(name=name)
112+
113+
if rich_preview:
114+
self.console.print(PydanticTableModel([secret]).table)
115+
116+
else:
117+
print(secret.value)
118+
119+
def delete(
120+
self,
121+
name: str = typer.Option(..., "--name", "-n", help="Name of the secret"),
122+
):
123+
self.domain.delete(name=name)
124+
125+
print(f"Secret '{name}' deleted successfully")
126+
127+
def list(
128+
self,
129+
page_size: int = typer.Option(0, help="Size of each page of results"),
130+
page_number: int = typer.Option(0, help="Target page number of results"),
131+
rich_preview: bool = typer.Option(
132+
False,
133+
"--rich-preview",
134+
"-rp",
135+
help="Rich preview of the secret information as a table",
136+
),
137+
):
138+
"""List all secrets for the current user"""
139+
secret_list = self.domain.list(page_size=page_size, page_number=page_number)
140+
141+
data = []
142+
headers = []
143+
144+
# Extract the data and headers
145+
for secret in secret_list:
146+
_secret_dict = secret.dict()
147+
148+
149+
data.append(list(_secret_dict.values())) # Append a list of values to data
150+
151+
headers = [key.upper() for key in _secret_dict.keys()]
152+
153+
if len(data) == 0:
154+
print("No matching results found.")
155+
return
156+
157+
headers = [key.upper() for key in _secret_dict.keys()]
158+
159+
if rich_preview:
160+
# Create a Rich Table
161+
table = Table(show_header=True, header_style="bold")
162+
# Add columns to the table
163+
for header in headers:
164+
table.add_column(header, justify="center")
165+
166+
# Add data rows to the table
167+
for row in data:
168+
table.add_row(*row)
169+
170+
# Print the table
171+
rich.print(table)
172+
173+
else:
174+
self._list_preview(data, headers)

0 commit comments

Comments
 (0)