diff --git a/src/preset_cli/api/clients/superset.py b/src/preset_cli/api/clients/superset.py index 79bac3df..aa1751ff 100644 --- a/src/preset_cli/api/clients/superset.py +++ b/src/preset_cli/api/clients/superset.py @@ -855,7 +855,7 @@ def export_rls(self) -> Iterator[RuleType]: }, ) - def import_role(self, role: RoleType) -> None: + def import_role(self, role: RoleType) -> None: # pylint: disable=too-many-locals """ Import a given role. @@ -905,6 +905,32 @@ def import_role(self, role: RoleType) -> None: "user": user_ids, "permissions": permission_ids, } + + # update if existing + search_url = self.baseurl / "roles/list/" % {"_flt_3_name": role["name"]} + _logger.debug("GET %s", search_url) + response = self.session.get(search_url) + soup = BeautifulSoup(response.text, features="html.parser") + tables = soup.find_all("table") + if len(tables) == 2: + table = tables[1] + trs = table.find_all("tr") + if len(trs) == 2: + tr = trs[1] # pylint: disable=invalid-name + tds = tr.find_all("td") + + td = tds[0] # pylint: disable=invalid-name + if td.find("a"): + role_id = int(td.find("a").attrs["href"].split("/")[-1]) + else: + role_id = int(td.find("input").attrs["id"]) + + update_url = self.baseurl / "roles/edit" / str(role_id) + _logger.debug("POST %s\n%s", update_url, json.dumps(data, indent=4)) + response = self.session.post(update_url, data=data) + validate_response(response) + return + _logger.debug("POST %s\n%s", url, json.dumps(data, indent=4)) response = self.session.post(url, data=data) validate_response(response) diff --git a/tests/api/clients/superset_test.py b/tests/api/clients/superset_test.py index 1071a419..157db172 100644 --- a/tests/api/clients/superset_test.py +++ b/tests/api/clients/superset_test.py @@ -1938,6 +1938,20 @@ def test_import_role(mocker: MockerFixture, requests_mock: Mocker) -> None: """, ) requests_mock.post("https://superset.example.org/roles/add") + requests_mock.get( + "https://superset.example.org/roles/list/?_flt_3_name=Admin", + text=""" + + + + + + +
+ + + """, + ) mocker.patch.object( SupersetClient, "export_users", @@ -1981,6 +1995,137 @@ def test_import_role(mocker: MockerFixture, requests_mock: Mocker) -> None: ] +def test_import_role_update(mocker: MockerFixture, requests_mock: Mocker) -> None: + """ + Test the ``import_role`` method on updates. + """ + _logger = mocker.patch("preset_cli.api.clients.superset._logger") + requests_mock.get( + "https://superset.example.org/roles/add", + text=""" + + """, + ) + requests_mock.post("https://superset.example.org/roles/add") + requests_mock.post("https://superset.example.org/roles/edit/1") + requests_mock.post("https://superset.example.org/roles/edit/2") + requests_mock.get( + "https://superset.example.org/roles/list/?_flt_3_name=Admin", + text=""" + + + + + + +
+ + + + + + + + + +
Name
Admin
+ + + """, + ) + requests_mock.get( + "https://superset.example.org/roles/list/?_flt_3_name=Public", + text=""" + + + + + + +
+ + + + + + + + + +
Name
EditPublic
+ + + """, + ) + requests_mock.get( + "https://superset.example.org/roles/list/?_flt_3_name=Other", + text=""" + + + + + + +
+
+ + + """, + ) + mocker.patch.object( + SupersetClient, + "export_users", + return_value=[ + {"id": 1, "email": "admin@example.com"}, + {"id": 2, "email": "adoe@example.com"}, + ], + ) + + role: RoleType = { + "name": "Admin", + "permissions": [ + "can do something that is not in Preset", + "all database access on all_database_access", + "schema access on [Google Sheets].[main]", + "database access on [Not added].(id:1)", + "datasource access on [Not added].[nope](id:42)", + ], + "users": ["admin@example.com", "adoe@example.com", "bdoe@example.com"], + } + + auth = Auth() + client = SupersetClient("https://superset.example.org/", auth) + client.import_role(role) + + assert ( + requests_mock.last_request.text + == "name=Admin&user=1&user=2&permissions=1&permissions=2" + ) + + assert _logger.warning.mock_calls == [ + mock.call( + "Permission %s not found in target", + "can do something that is not in Preset", + ), + mock.call("Permission %s not found in target", "Database access on Not added"), + mock.call( + "Permission %s not found in target", + "Dataset access on Not added.nope", + ), + ] + + # extra tests + role["name"] = "Public" + client.import_role(role) + assert requests_mock.last_request.url == "https://superset.example.org/roles/edit/2" + role["name"] = "Other" + client.import_role(role) + assert requests_mock.last_request.url == "https://superset.example.org/roles/add" + + def test_import_rls(requests_mock: Mocker) -> None: """ Test the ``import_rls`` method.