Skip to content

Commit d1abd22

Browse files
ABZhang0Alan Zhang
andauthored
Support subfolders folder related commands (#9055)
* Support subfolders for create, list, and show * Support subfolders for create, list, and show * Update version * Support subfolders in backup cmd * Use minor version * Remove api key test * Support parent folder name in create cmd --------- Co-authored-by: Alan Zhang <[email protected]>
1 parent 6673b83 commit d1abd22

17 files changed

+20869
-15567
lines changed

src/amg/HISTORY.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,11 @@ Release History
144144

145145
2.7.0
146146
++++++
147-
* `az grafana api-key`: deprecate command group as Grafana Labs is sunsetting API keys
147+
* `az grafana api-key`: deprecate command group as Grafana Labs is sunsetting API keys
148+
149+
2.8.0
150+
++++++
151+
* `az grafana folder create`: support parent folder argument for nested folder creation
152+
* `az grafana folder list`: support nested folders
153+
* `az grafana folder show`: support nested folders
154+
* `az grafana backup`: include corresponding nested folders when individual folders are specified

src/amg/azext_amg/_params.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def load_arguments(self, _):
102102

103103
with self.argument_context("grafana folder") as c:
104104
c.argument("title", help="title of the folder")
105+
c.argument("parent_folder", options_list=["--parent-folder"], help="Name or uid of the parent folder, if any")
105106

106107
with self.argument_context("grafana user") as c:
107108
c.argument("user", help="user login name or email")

src/amg/azext_amg/backup_core.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,14 @@ def get_all_folders(grafana_url, http_headers, **kwargs):
158158
folders_to_exclude = kwargs.get('folders_to_exclude')
159159
if folders_to_include:
160160
folders_to_include = [f.lower() for f in folders_to_include]
161-
folders = [f for f in folders if f.get('title', '').lower() in folders_to_include]
161+
folders = [f for f in folders if
162+
f.get('title', '').lower() in folders_to_include or
163+
f.get('folderTitle', '').lower() in folders_to_include]
162164
if folders_to_exclude:
163165
folders_to_exclude = [f.lower() for f in folders_to_exclude]
164-
folders = [f for f in folders if f.get('title', '').lower() not in folders_to_exclude]
166+
folders = [f for f in folders if
167+
f.get('title', '').lower() not in folders_to_exclude and
168+
f.get('folderTitle', '').lower() not in folders_to_exclude]
165169

166170
individual_folders = []
167171
for folder in folders:

src/amg/azext_amg/custom.py

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from .aaz.latest.grafana._update import Update as _GrafanaUpdate
2121

2222
from ._client_factory import cf_amg
23-
from .utils import get_yes_or_no_option
23+
from .utils import get_yes_or_no_option, search_folders
2424

2525
logger = get_logger(__name__)
2626

@@ -538,19 +538,33 @@ def test_notification_channel(cmd, grafana_name, notification_channel, resource_
538538
return json.loads(response.content)
539539

540540

541-
def create_folder(cmd, grafana_name, title, resource_group_name=None, api_key_or_token=None, subscription=None):
541+
def create_folder(cmd, grafana_name, title, parent_folder=None, resource_group_name=None, api_key_or_token=None,
542+
subscription=None):
542543
payload = {
543-
"title": title
544+
"title": title,
544545
}
546+
if parent_folder:
547+
data = _find_folder(cmd, resource_group_name, grafana_name, parent_folder, api_key_or_token=api_key_or_token)
548+
payload["parentUid"] = data["uid"]
549+
545550
response = _send_request(cmd, resource_group_name, grafana_name, "post", "/api/folders", payload,
546551
api_key_or_token=api_key_or_token, subscription=subscription)
547552
return json.loads(response.content)
548553

549554

550555
def list_folders(cmd, grafana_name, resource_group_name=None, api_key_or_token=None, subscription=None):
551-
response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/folders",
552-
api_key_or_token=api_key_or_token, subscription=subscription)
553-
return json.loads(response.content)
556+
endpoint, headers = _get_grafana_request_context(cmd, resource_group_name, grafana_name, subscription,
557+
api_key_or_token=api_key_or_token)
558+
559+
status, content = search_folders(
560+
grafana_url=endpoint,
561+
http_get_headers=headers
562+
)
563+
564+
if status >= 400:
565+
raise ArgumentUsageError(f"Failed to find folders. Error: {content}")
566+
567+
return content
554568

555569

556570
def update_folder(cmd, grafana_name, folder, title, resource_group_name=None, api_key_or_token=None):
@@ -575,22 +589,19 @@ def delete_folder(cmd, grafana_name, folder, resource_group_name=None, api_key_o
575589

576590

577591
def _find_folder(cmd, resource_group_name, grafana_name, folder, api_key_or_token=None):
578-
response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/folders/" + folder,
579-
raise_for_error_status=False, api_key_or_token=api_key_or_token)
580-
if response.status_code >= 400:
581-
response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/folders",
582-
api_key_or_token=api_key_or_token)
583-
if response.status_code >= 400:
584-
raise ArgumentUsageError(f"Couldn't find the folder '{folder}'. Ex: {response.status_code}")
585-
result = json.loads(response.content)
586-
result = [f for f in result if f["title"] == folder]
587-
if len(result) == 0:
588-
raise ArgumentUsageError(f"Couldn't find the folder '{folder}'. Ex: {response.status_code}")
589-
if len(result) > 1:
590-
raise ArgumentUsageError((f"More than one folder has the same title of '{folder}'. Please use other "
591-
f"unique identifiers"))
592-
return result[0]
593-
return json.loads(response.content)
592+
folders = list_folders(cmd, grafana_name, resource_group_name=resource_group_name,
593+
api_key_or_token=api_key_or_token)
594+
595+
# try uid first
596+
match = next((f for f in folders if f['uid'] == folder), None)
597+
if match:
598+
return match
599+
600+
# If we get here, the folder is not found by uid, so we try by title
601+
match = next((f for f in folders if f['title'] == folder), None)
602+
if not match:
603+
raise ArgumentUsageError(f"Couldn't find the folder '{folder}'.")
604+
return match
594605

595606

596607
def list_api_keys(cmd, grafana_name, resource_group_name=None):
@@ -914,14 +925,8 @@ def _get_grafana_endpoint(cmd, resource_group_name, grafana_name, subscription):
914925

915926
def _send_request(cmd, resource_group_name, grafana_name, http_method, path, body=None, raise_for_error_status=True,
916927
api_key_or_token=None, subscription=None):
917-
endpoint = _get_grafana_endpoint(cmd, resource_group_name, grafana_name, subscription)
918-
919-
creds = _get_data_plane_creds(cmd, api_key_or_token, subscription)
920-
921-
headers = {
922-
"content-type": "application/json",
923-
"authorization": "Bearer " + creds[1]
924-
}
928+
endpoint, headers = _get_grafana_request_context(cmd, resource_group_name, grafana_name, subscription,
929+
api_key_or_token=api_key_or_token)
925930

926931
# TODO: handle re-try on 429
927932
response = requests.request(http_method, url=endpoint + path, headers=headers, json=body, timeout=60,
@@ -932,3 +937,14 @@ def _send_request(cmd, resource_group_name, grafana_name, http_method, path, bod
932937
response.raise_for_status()
933938
# TODO: log headers, requests and response
934939
return response
940+
941+
942+
def _get_grafana_request_context(cmd, resource_group_name, grafana_name, subscription, api_key_or_token=None):
943+
endpoint = _get_grafana_endpoint(cmd, resource_group_name, grafana_name, subscription)
944+
creds = _get_data_plane_creds(cmd, api_key_or_token, subscription)
945+
headers = {
946+
"content-type": "application/json",
947+
"authorization": "Bearer " + creds[1]
948+
}
949+
950+
return endpoint, headers

0 commit comments

Comments
 (0)