Skip to content

Commit

Permalink
Merge pull request #100 from preset-io/fix_export_roles_preset
Browse files Browse the repository at this point in the history
fix: export-roles in Preset
  • Loading branch information
betodealmeida authored Oct 5, 2022
2 parents 7e2271a + c959ed5 commit 9faec57
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 84 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Next
- Sync user roles (team, workspace, data access) from a file to a workspace.
- Add ``--version`` option.
- Do not prompt for workspaces if passing ``--help``.
- Fix ``export-users`` in Preset workspaces.

dbt
~~~
Expand Down
46 changes: 24 additions & 22 deletions src/preset_cli/api/clients/superset.py
Original file line number Diff line number Diff line change
Expand Up @@ -735,11 +735,17 @@ def export_roles(self) -> Iterator[RoleType]: # pylint: disable=too-many-locals
"""
Return all roles.
"""
user_email_map = {user["id"]: user["email"] for user in self.export_users()}

page = 0
while True:
params = {
# Superset
"psize_RoleModelView": MAX_PAGE_SIZE,
"page_RoleModelView": page,
# Preset
"psize_DataRoleModelView": MAX_PAGE_SIZE,
"page_DataRoleModelView": page,
}
url = self.baseurl / "roles/list/"
page += 1
Expand All @@ -760,34 +766,31 @@ def export_roles(self) -> Iterator[RoleType]: # pylint: disable=too-many-locals
role_id = int(td.find("a").attrs["href"].split("/")[-1])
else:
role_id = int(td.find("input").attrs["id"])
# TODO (betodealmeida): use roles/edit so it works with Preset
role_url = self.baseurl / "roles/show" / str(role_id)
role_url = self.baseurl / "roles/edit" / str(role_id)

_logger.debug("GET %s", role_url)
response = self.session.get(role_url)
soup = BeautifulSoup(response.text, features="html.parser")
tables = soup.find_all("table")

role_table = tables[-1]
keys: List[Tuple[str, Callable[[Any], Any]]] = [
("name", str),
("permissions", parse_html_array),
name = soup.find("input", {"name": "name"}).attrs["value"]
permissions = [
option.text.strip()
for option in soup.find("select", id="permissions").find_all(
"option",
)
if "selected" in option.attrs
]
users = [
user_email_map[int(option.attrs["value"])]
for option in soup.find("select", id="user").find_all("option")
if "selected" in option.attrs
]
role_info = {
key: parse(tr.find("td").text.strip())
for (key, parse), tr in zip(keys, role_table.find_all("tr"))
}

if len(tables) >= 2:
user_table = tables[-2]
role_info["users"] = [
tr.find_all("td")[4].text.strip()
for tr in user_table.find_all("tr")[1:]
]
else:
role_info["users"] = []

yield cast(RoleType, role_info)
yield {
"name": name,
"permissions": permissions,
"users": users,
}

def export_rls(self) -> Iterator[RuleType]:
"""
Expand Down Expand Up @@ -819,7 +822,6 @@ def export_rls(self) -> Iterator[RuleType]:
# extract the ID to fetch each RLS in a separate request, since the list
# view doesn't have all the columns we need
rule_id = int(tds[0].find("input").attrs["id"])
# TODO (betodealmeida): use roles/edit so it works with Preset
rule_url = (
self.baseurl
/ "rowlevelsecurityfiltersmodelview/show"
Expand Down
126 changes: 64 additions & 62 deletions tests/api/clients/superset_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1398,7 +1398,7 @@ def test_export_users_preset(requests_mock: Mocker) -> None:
]


def test_export_roles(requests_mock: Mocker) -> None:
def test_export_roles(mocker: MockerFixture, requests_mock: Mocker) -> None:
"""
Test ``export_roles``.
"""
Expand Down Expand Up @@ -1459,59 +1459,59 @@ def test_export_roles(requests_mock: Mocker) -> None:
""",
)
requests_mock.get(
"https://superset.example.org/roles/show/1",
"https://superset.example.org/roles/edit/1",
text="""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<table>
<tr>
<th></th>
<th>First Name</th>
<th>Last Name</th>
<th>User Name</th>
<th>Email</th>
<th>Is Active?</th>
<th>Role</th>
</tr>
<tr>
<td></td>
<td>Alice</td>
<td>Doe</td>
<td>adoe</td>
<td>adoe@example.com</td>
<td>True</td>
<td>[Admin]</td>
</tr>
</table>
<table>
<tr><th>Name</th><td>Admin</td></tr>
<tr><th>Permissions</th><td>[can this, can that]</td></tr>
</table>
<input name="name" value="Admin" />
<select id="permissions">
<option selected="" value="1">can this</option>
<option selected="" value="2">can that</option>
<option value="3">cannot </option>
</select>
<select id="user">
<option selected="" value="1">Alice Doe</option>
<option value="2">Bob Doe</option>
</select>
</body>
</html>
""",
)
requests_mock.get(
"https://superset.example.org/roles/show/2",
"https://superset.example.org/roles/edit/2",
text="""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<table>
<tr><th>Name</th><td>Public</td></tr>
<tr><th>Permissions</th><td>[]</td></tr>
</table>
<input name="name" value="Public" />
<select id="permissions">
<option value="1">can this</option>
<option value="2">can that</option>
<option value="3">cannot </option>
</select>
<select id="user">
<option value="1">Alice Doe</option>
<option value="2">Bob Doe</option>
</select>
</body>
</html>
""",
)
mocker.patch.object(
SupersetClient,
"export_users",
return_value=[
{"id": 1, "email": "adoe@example.com"},
{"id": 2, "email": "bdoe@example.com"},
],
)

auth = Auth()
client = SupersetClient("https://superset.example.org/", auth)
Expand All @@ -1529,7 +1529,9 @@ def test_export_roles(requests_mock: Mocker) -> None:
]


def test_export_roles_anchor_role_id(requests_mock: Mocker) -> None:
def test_export_roles_anchor_role_id(
mocker: MockerFixture, requests_mock: Mocker,
) -> None:
"""
Test ``export_roles``.
"""
Expand Down Expand Up @@ -1590,59 +1592,59 @@ def test_export_roles_anchor_role_id(requests_mock: Mocker) -> None:
""",
)
requests_mock.get(
"https://superset.example.org/roles/show/1",
"https://superset.example.org/roles/edit/1",
text="""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<table>
<tr>
<th></th>
<th>First Name</th>
<th>Last Name</th>
<th>User Name</th>
<th>Email</th>
<th>Is Active?</th>
<th>Role</th>
</tr>
<tr>
<td></td>
<td>Alice</td>
<td>Doe</td>
<td>adoe</td>
<td>adoe@example.com</td>
<td>True</td>
<td>[Admin]</td>
</tr>
</table>
<table>
<tr><th>Name</th><td>Admin</td></tr>
<tr><th>Permissions</th><td>[can this, can that]</td></tr>
</table>
<input name="name" value="Admin" />
<select id="permissions">
<option selected="" value="1">can this</option>
<option selected="" value="2">can that</option>
<option value="3">cannot </option>
</select>
<select id="user">
<option selected="" value="1">Alice Doe</option>
<option value="2">Bob Doe</option>
</select>
</body>
</html>
""",
)
requests_mock.get(
"https://superset.example.org/roles/show/2",
"https://superset.example.org/roles/edit/2",
text="""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<table>
<tr><th>Name</th><td>Public</td></tr>
<tr><th>Permissions</th><td>[]</td></tr>
</table>
<input name="name" value="Public" />
<select id="permissions">
<option value="1">can this</option>
<option value="2">can that</option>
<option value="3">cannot </option>
</select>
<select id="user">
<option value="1">Alice Doe</option>
<option value="2">Bob Doe</option>
</select>
</body>
</html>
""",
)
mocker.patch.object(
SupersetClient,
"export_users",
return_value=[
{"id": 1, "email": "adoe@example.com"},
{"id": 2, "email": "bdoe@example.com"},
],
)

auth = Auth()
client = SupersetClient("https://superset.example.org/", auth)
Expand Down

0 comments on commit 9faec57

Please sign in to comment.