-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(pydantic-settings): Initializes the pydantic settings for rio config-trees #2
base: devel
Are you sure you want to change the base?
Changes from all commits
9d30359
0f2990c
725a33b
6fc2706
70a6a75
5fcda9f
cd82549
52a5a05
7bf568f
4158609
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* @rapyuta-robotics/io-cli-owner @rapyuta-robotics/io-first-reviewer |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name: 💬 Check Commit Hygiene | ||
|
||
on: | ||
pull_request: | ||
branches: | ||
- main | ||
- devel | ||
|
||
jobs: | ||
verify: | ||
name: Conventional Commits | ||
runs-on: ubuntu-22.04 | ||
steps: | ||
- uses: actions/checkout@v3 | ||
name: Checkout code | ||
|
||
- uses: rapyuta-robotics/[email protected] | ||
name: Check if commit messages are compliant | ||
with: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# python generated files | ||
__pycache__/ | ||
*.py[oc] | ||
build/ | ||
dist/ | ||
wheels/ | ||
*.egg-info | ||
|
||
# venv | ||
.venv |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
3.12.5 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# rapyuta-io-sdk-v2 | ||
|
||
Describe your project here. |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,27 @@ | ||||||
[project] | ||||||
name = "rapyuta-io-sdk-v2" | ||||||
version = "0.1.0" | ||||||
description = "Version:2 for Rapyuta.io SDK" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
dependencies = [ | ||||||
"httpx>=0.27.2", | ||||||
"pydantic-settings>=2.5.2", | ||||||
"python-benedict>=0.33.2", | ||||||
"pyyaml>=6.0.2", | ||||||
"setuptools>=75.1.0", | ||||||
] | ||||||
readme = "README.md" | ||||||
requires-python = ">= 3.8" | ||||||
|
||||||
[build-system] | ||||||
requires = ["hatchling"] | ||||||
build-backend = "hatchling.build" | ||||||
|
||||||
[tool.rye] | ||||||
managed = true | ||||||
dev-dependencies = [] | ||||||
|
||||||
[tool.hatch.metadata] | ||||||
allow-direct-references = true | ||||||
|
||||||
[tool.hatch.build.targets.wheel] | ||||||
packages = ["rapyuta_io_sdk_v2"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from rapyuta_io_sdk_v2.config import Configuration | ||
from rapyuta_io_sdk_v2.pydantic_configs import RRSettings | ||
__version__ = "1.17.0" |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,118 @@ | ||||||
# -*- coding: utf-8 -*- | ||||||
# Copyright 2024 Rapyuta Robotics | ||||||
# | ||||||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
# you may not use this file except in compliance with the License. | ||||||
# You may obtain a copy of the License at | ||||||
# | ||||||
# http://www.apache.org/licenses/LICENSE-2.0 | ||||||
# | ||||||
# Unless required by applicable law or agreed to in writing, software | ||||||
# distributed under the License is distributed on an "AS IS" BASIS, | ||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
# See the License for the specific language governing permissions and | ||||||
# limitations under the License. | ||||||
from contextlib import asynccontextmanager | ||||||
|
||||||
from rapyuta_io_sdk_v2.client import Client | ||||||
from typing import Optional, override, Any, AsyncGenerator, List, Dict | ||||||
import httpx | ||||||
|
||||||
class AsyncClient(Client): | ||||||
|
||||||
def __init__(self,config): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. todo: Run a
Suggested change
|
||||||
super().__init__(config) | ||||||
|
||||||
@asynccontextmanager | ||||||
async def _get_client(self) -> AsyncGenerator[httpx.AsyncClient,None]: | ||||||
async with httpx.AsyncClient( | ||||||
headers=self._get_headers(), | ||||||
) as async_client: | ||||||
yield async_client | ||||||
|
||||||
@override | ||||||
async def list_projects(self, organization_guid: str = None): | ||||||
url = "{}/v2/projects/".format(self.v2api_host) | ||||||
params = {} | ||||||
if organization_guid: | ||||||
params.update({ | ||||||
"organizations": organization_guid, | ||||||
}) | ||||||
async with self._get_client() as client: | ||||||
response = await client.get(url=url, params=params) | ||||||
response.raise_for_status() | ||||||
return response.json() | ||||||
|
||||||
@override | ||||||
async def get_project(self, project_guid: str): | ||||||
url = "{}/v2/projects/{}/".format(self.v2api_host, project_guid) | ||||||
|
||||||
async with self._get_client() as client: | ||||||
response = await client.get(url=url) | ||||||
response.raise_for_status() | ||||||
return response.json() | ||||||
|
||||||
@override | ||||||
async def list_config_trees(self) -> List[str]: | ||||||
url = "{}/v2/configtrees/".format(self.v2api_host) | ||||||
async with self._get_client() as client: | ||||||
try: | ||||||
res = await client.get(url=url) | ||||||
res.raise_for_status() | ||||||
|
||||||
except Exception as e: | ||||||
raise ValueError(f"Failed to list config trees: {res.text}") from e | ||||||
|
||||||
if tree_list := res.json().get("items"): | ||||||
return [item["metadata"]["name"] for item in tree_list] | ||||||
else: | ||||||
return [] | ||||||
|
||||||
@override | ||||||
async def get_config_tree( | ||||||
self, | ||||||
tree_name: str, rev_id: Optional[str] = None, | ||||||
include_data: bool = False, filter_content_types: Optional[List[str]] = None, | ||||||
filter_prefixes: Optional[List[str]] = None | ||||||
) : | ||||||
url = "{}/v2/configtrees/{}/".format(self.v2api_host, tree_name) | ||||||
params: Dict[str, Any] = { | ||||||
'includeData': include_data, | ||||||
'contentTypes': filter_content_types, | ||||||
'keyPrefixes': filter_prefixes, | ||||||
'revision': rev_id, | ||||||
} | ||||||
async with self._get_client() as client: | ||||||
try: | ||||||
res = await client.get( | ||||||
url=url, | ||||||
params=params | ||||||
) | ||||||
res.raise_for_status() | ||||||
except Exception as e: | ||||||
raise ValueError(f"Failed to get config tree data") | ||||||
|
||||||
raw_config_tree = res.json().get("keys", {}) | ||||||
return self._preprocess_config_tree_data(raw_config_tree) | ||||||
|
||||||
@override | ||||||
async def create_config_tree(self,tree_spec: dict): | ||||||
url = "{}/v2/configtrees/".format(self.v2api_host) | ||||||
async with self._get_client() as client: | ||||||
try: | ||||||
res = await client.post( | ||||||
url=url, | ||||||
json=tree_spec | ||||||
) | ||||||
res.raise_for_status() | ||||||
except Exception as e: | ||||||
raise ValueError(f"Failed to create config tree: {res.text}") | ||||||
raw_config_tree = res.json() | ||||||
return raw_config_tree | ||||||
|
||||||
|
||||||
|
||||||
|
||||||
|
||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
# -*- coding: utf-8 -*- | ||
# Copyright 2024 Rapyuta Robotics | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
from typing import Optional, List, Dict, Any | ||
from benedict import benedict | ||
from rapyuta_io_sdk_v2.utils import unflatten_keys | ||
import httpx, json | ||
|
||
from .utils import handle_server_errors | ||
from rapyuta_io_sdk_v2.constants import GET_USER_PATH | ||
|
||
class Client(object): | ||
PROD_V2API_URL = "https://api.rapyuta.io" | ||
def __init__(self,config): | ||
self.config = config | ||
self.v2api_host = config.hosts.get("v2api_host",self.PROD_V2API_URL) | ||
|
||
def _get_headers(self, with_project: bool = True) -> dict: | ||
headers = { | ||
"Authorization" : 'Bearer '+self.config.auth_token, | ||
"Content-Type": "application/json", | ||
"project": self.config.project_guid, | ||
"organizationguid": self.config.organization_guid, | ||
} | ||
return headers | ||
|
||
@staticmethod | ||
def _preprocess_config_tree_data(raw_config_tree: Optional[Dict[str, Any]]): | ||
if raw_config_tree: | ||
return unflatten_keys(raw_config_tree) | ||
else: | ||
return benedict() | ||
|
||
def get_authenticated_user(self) -> Optional[Dict]: | ||
try: | ||
_core_api_host = self.config.hosts.get("core_api_host") | ||
url = "{}{}".format(_core_api_host, GET_USER_PATH) | ||
headers = self._get_headers() | ||
response = httpx.get(url=url, headers=headers) | ||
handle_server_errors(response) | ||
return response.json() | ||
except Exception as e: | ||
raise | ||
|
||
def list_projects(self,organization_guid: str = None): | ||
""" | ||
|
||
:param organization_guid: | ||
:return: | ||
""" | ||
url = "{}/v2/projects/".format(self.v2api_host) | ||
headers = self._get_headers(with_project=False) | ||
params = {} | ||
if organization_guid: | ||
params.update({ | ||
"organizations": organization_guid, | ||
}) | ||
response = httpx.get(url=url,headers=headers,params=params) | ||
handle_server_errors(response) | ||
return response.json() | ||
|
||
def get_project(self, project_guid: str): | ||
""" | ||
|
||
:param project_guid: | ||
:return: | ||
""" | ||
url = "{}/v2/projects/{}/".format(self.v2api_host, project_guid) | ||
headers = self._get_headers(with_project=False) | ||
response = httpx.get(url=url,headers=headers) | ||
handle_server_errors(response) | ||
return response.json() | ||
|
||
def get_config_tree(self,tree_name: str, rev_id: Optional[str]=None, | ||
include_data: bool = False, filter_content_types: Optional[List[str]] = None, | ||
filter_prefixes: Optional[List[str]] = None): | ||
|
||
url = "{}/v2/configtrees/{}/".format(self.v2api_host, tree_name) | ||
query = { | ||
'includeData': include_data, | ||
'contentTypes': filter_content_types, | ||
'keyPrefixes': filter_prefixes, | ||
'revision': rev_id, | ||
} | ||
headers = self._get_headers() | ||
response = httpx.get(url=url,headers=headers,params=query) | ||
handle_server_errors(response) | ||
return response.json() | ||
|
||
def create_config_tree(self,tree_spec: dict): | ||
url = "{}/v2/configtrees/".format(self.v2api_host) | ||
headers = self._get_headers() | ||
response = httpx.post(url=url,headers=headers,json=tree_spec) | ||
handle_server_errors(response) | ||
return response.json() | ||
|
||
def delete_config_tree(self,tree_name:str): | ||
url = "{}/v2/configtrees/{}/".format(self.v2api_host, tree_name) | ||
headers = self._get_headers() | ||
response = httpx.delete(url=url,headers=headers) | ||
handle_server_errors(response) | ||
return response.json() | ||
|
||
def list_config_trees(self): | ||
url = "{}/v2/configtrees/".format(self.v2api_host) | ||
headers = self._get_headers() | ||
response = httpx.get(url=url,headers=headers) | ||
handle_server_errors(response) | ||
return response.json() | ||
|
||
def set_revision_config_tree(self, tree_name: str, spec: dict) -> None: | ||
url = "{}/v2/configtrees/{}/".format(self.v2api_host, tree_name) | ||
headers = self._get_headers() | ||
response = httpx.put(url=url,headers=headers,json=spec) | ||
handle_server_errors(response) | ||
|
||
data = json.loads(response.text) | ||
print(data) | ||
if not data.ok: | ||
err_msg = data.get('error') | ||
raise Exception("configtree: {}".format(err_msg)) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo: add
.idea
to this file.