Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cs_tools/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,12 @@ def groups_search(
timeout = options.pop("timeout", httpx.USE_CLIENT_DEFAULT)
return self.post("api/rest/2.0/groups/search", headers=headers, timeout=timeout, json=options)

@pydantic.validate_call(validate_return=True, config=validators.METHOD_CONFIG)
@_transport.CachePolicy.mark_cacheable
def groups_search_v1(self, **options: Any) -> Awaitable[httpx.Response]: # noqa: ARG002
"""Get a list of ThoughtSpot groups with v1 endpoint."""
return self.get("callosum/v1/tspublic/v1/group")

@pydantic.validate_call(validate_return=True, config=validators.METHOD_CONFIG)
def groups_create(self, **options: Any) -> Awaitable[httpx.Response]:
"""Create a ThoughtSpot group."""
Expand Down
8 changes: 6 additions & 2 deletions cs_tools/cli/tools/archiver/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,12 @@ def identify(

with tracker["GATHER_METADATA"]:
if only_groups or ignore_groups:
c = workflows.paginator(ts.api.groups_search, record_size=150_000, timeout=60 * 15)
d = utils.run_sync(c)
c = ts.api.groups_search_v1()
r = utils.run_sync(c)
_ = r.json()

# c = workflows.paginator(ts.api.groups_search, record_size=150_000, timeout=60 * 15)
# d = utils.run_sync(c)

all_groups = [
{
Expand Down
57 changes: 57 additions & 0 deletions cs_tools/cli/tools/searchable/api_transformer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from typing import Any
import datetime as dt
import functools as ft
import itertools as it
Expand All @@ -10,11 +11,13 @@
import awesomeversion

from cs_tools import _types, validators
from cs_tools._types import TableRowsFormat
from cs_tools.datastructures import SessionContext

from . import models

log = logging.getLogger(__name__)
ArbitraryJsonFormat = list[dict[str, Any]]


def ts_cluster(data: SessionContext) -> _types.TableRowsFormat:
Expand Down Expand Up @@ -111,6 +114,30 @@ def ts_group(data: list[_types.APIResult], *, cluster: _types.GUID) -> _types.Ta
return reshaped


def to_group_v1(data: ArbitraryJsonFormat, cluster: str) -> TableRowsFormat:
"""Reshapes groups/search -> searchable.models.Group. Needed for V1 API."""
out: TableRowsFormat = []

for row in data:
for org_id in row["header"].get("orgIds", [0]):
out.append(
models.Group.validated_init(
cluster_guid=cluster,
org_id=org_id,
group_guid=row["header"]["id"],
group_name=row["header"]["name"],
description=row["header"].get("description"),
display_name=row["header"]["displayName"],
sharing_visibility=row["visibility"],
created=row["header"]["created"] / 1000,
modified=row["header"]["modified"] / 1000,
group_type=row["type"],
)
)

return [model.model_dump() for model in out]


def ts_org_membership(data: list[_types.APIResult], *, cluster: _types.GUID) -> _types.TableRowsFormat:
"""Reshapes users/search -> searchable.models.OrgMembership."""
reshaped: _types.TableRowsFormat = []
Expand Down Expand Up @@ -188,6 +215,21 @@ def ts_group_membership(data: list[_types.APIResult], *, cluster: _types.GUID) -
return reshaped


def to_group_membership(data: ArbitraryJsonFormat, cluster: str) -> TableRowsFormat:
"""Reshapes {groups|users}/search -> searchable.models.GroupMembership. Needed for V1 API."""
out: TableRowsFormat = []

for row in data:
for group in row["assignedGroups"]:
out.append(
models.GroupMembership.validated_init(
cluster_guid=cluster, principal_guid=row["header"]["id"], group_guid=group
)
)

return [model.model_dump() for model in out]


def ts_group_privilege(data: list[_types.APIResult], *, cluster: _types.GUID) -> _types.TableRowsFormat:
"""Reshapes {groups|users}/search -> searchable.models.GroupPrivilege."""
reshaped: _types.TableRowsFormat = []
Expand All @@ -205,6 +247,21 @@ def ts_group_privilege(data: list[_types.APIResult], *, cluster: _types.GUID) ->
return reshaped


def to_group_privilege(data: ArbitraryJsonFormat, cluster: str) -> TableRowsFormat:
"""Reshapes {groups|users}/search -> searchable.models.GroupPrivilege. Needed for V1 API."""
out: TableRowsFormat = []

for row in data:
for privilege in row["privileges"]:
out.append(
models.GroupPrivilege.validated_init(
cluster_guid=cluster, group_guid=row["header"]["id"], privilege=privilege
)
)

return [model.model_dump() for model in out]


def ts_tag(data: list[_types.APIResult], *, cluster: _types.GUID, current_org: int) -> _types.TableRowsFormat:
"""Reshapes {groups|users}/search -> searchable.models.Tag."""
reshaped: _types.TableRowsFormat = []
Expand Down
15 changes: 10 additions & 5 deletions cs_tools/cli/tools/searchable/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,26 +450,31 @@ def metadata(
temp.dump(models.Org.__tablename__, data=d)

with tracker["TS_GROUP"]:
c = workflows.paginator(ts.api.groups_search, record_size=5_000, timeout=60 * 15)
_ = utils.run_sync(c)
c = ts.api.groups_search_v1()
r = utils.run_sync(c)
_ = r.json()

# commenting out calling of v2 api for CWT customer
# c = workflows.paginator(ts.api.groups_search, record_size=5_000, timeout=60 * 15)
# _ = utils.run_sync(c)

# DUMP GROUP DATA
d = api_transformer.ts_group(data=_, cluster=CLUSTER_UUID)
d = api_transformer.to_group_v1(data=_, cluster=CLUSTER_UUID)
temp.dump(models.Group.__tablename__, data=d)

# TODO: REMOVE AFTER 10.3.0.SW is n-1 (see COMPAT_GUIDS ref below.)
seen_group_guids.update([group["group_guid"] for group in d])

# DUMP GROUP->GROUP_MEMBERSHIP DATA
d = api_transformer.ts_group_membership(data=_, cluster=CLUSTER_UUID)
d = api_transformer.to_group_membership(data=_, cluster=CLUSTER_UUID)
temp.dump(models.GroupMembership.__tablename__, data=d)

with tracker["TS_PRIVILEGE"]:
# TODO: ROLE->PRIVILEGE DATA.
# TODO: GROUP->ROLE DATA.

# DUMP GROUP->PRIVILEGE DATA
d = api_transformer.ts_group_privilege(data=_, cluster=CLUSTER_UUID)
d = api_transformer.to_group_privilege(data=_, cluster=CLUSTER_UUID)
temp.dump(models.GroupPrivilege.__tablename__, data=d)

if org["id"] == 0 and not primary_org_done:
Expand Down
14 changes: 9 additions & 5 deletions cs_tools/cli/tools/user-management/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,23 +333,27 @@ def sync(
CLUSTER_UUID = ts.session_context.thoughtspot.cluster_id

with tracker["TS_GROUP"]:
c = workflows.paginator(ts.api.groups_search, record_size=150_000, timeout=60 * 15)
_ = utils.run_sync(c)
c = ts.api.groups_search_v1()
r = utils.run_sync(c)
_ = r.json()

# c = workflows.paginator(ts.api.groups_search, record_size=150_000, timeout=60 * 15)
# _ = utils.run_sync(c)

# DUMP GROUP DATA
d = searchable.api_transformer.ts_group(data=_, cluster=CLUSTER_UUID)
d = searchable.api_transformer.to_group_v1(data=_, cluster=CLUSTER_UUID)
existing[searchable.models.Group.__tablename__].extend(d)

# DUMP GROUP->GROUP_MEMBERSHIP DATA
d = searchable.api_transformer.ts_group_membership(data=_, cluster=CLUSTER_UUID)
d = searchable.api_transformer.to_group_membership(data=_, cluster=CLUSTER_UUID)
existing[searchable.models.GroupMembership.__tablename__].extend(d)

with tracker["TS_PRIVILEGE"]:
# TODO: ROLE->PRIVILEGE DATA.
# TODO: GROUP->ROLE DATA.

# DUMP GROUP->PRIVILEGE DATA
d = searchable.api_transformer.ts_group_privilege(data=_, cluster=CLUSTER_UUID)
d = searchable.api_transformer.to_group_privilege(data=_, cluster=CLUSTER_UUID)
existing[searchable.models.GroupPrivilege.__tablename__].extend(d)

with tracker["TS_USER"]:
Expand Down