Skip to content

card_default

card_default

Default DomoCard implementation

DomoCardAccessController

DomoCardAccessController(auth: DomoAuth, parent)

Bases: DomoAccessRelationshipController

Access controller for Domo cards.

Source code in src/crew_dcs/classes/DomoCard/access_controller.py
26
27
28
29
def __init__(self, auth: DomoAuth, parent):
    super().__init__(auth=auth, parent_object=parent)
    self.parent = parent
    self.relationships: list[Relationship] = []

add_owners async

add_owners(
    domo_users: list[Any] | None = None,
    domo_groups: list[Any] | None = None,
    user_ids: list[str | int] | None = None,
    group_ids: list[str | int] | None = None,
    return_raw: bool = False,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> bool

Add owners to the card.

Source code in src/crew_dcs/classes/DomoCard/access_controller.py
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
async def add_owners(
    self,
    domo_users: list[Any] | None = None,
    domo_groups: list[Any] | None = None,
    user_ids: list[str | int] | None = None,
    group_ids: list[str | int] | None = None,
    return_raw: bool = False,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> bool:
    """Add owners to the card."""
    context = RouteContext.build_context(context=context, **context_kwargs)

    user_ids = self._collect_ids(domo_users, user_ids)
    group_ids = self._collect_ids(domo_groups, group_ids)

    if not user_ids and not group_ids:
        raise ValueError("Must provide user or group identifiers to add owners")

    existing = await self._get_owner_entries(context=context)
    additions = [{"id": str(uid), "type": "USER"} for uid in user_ids] + [
        {"id": str(gid), "type": "GROUP"} for gid in group_ids
    ]

    combined = {f"{o['type']}:{o['id']}": o for o in existing}
    combined.update({f"{o['type']}:{o['id']}": o for o in additions})

    res = await card_routes.replace_card_owners(
        auth=self.auth,
        card_id=self.parent_id,
        owners=list(combined.values()),
        context=context,
    )

    return res if return_raw else res.is_success

create_relationship async

create_relationship(
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs
) -> bool

Create an access relationship for the card.

Source code in src/crew_dcs/classes/DomoCard/access_controller.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
async def create_relationship(
    self,
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs,
) -> bool:
    """Create an access relationship for the card."""
    if relationship_type == RelationshipType.HAS_ACCESS_OWNER:
        return await self.add_owners(
            user_ids=(
                [target_entity_id]
                if target_entity_type == EntityType.USER
                else None
            ),
            group_ids=(
                [target_entity_id]
                if target_entity_type == EntityType.GROUP
                else None
            ),
            **kwargs,
        )

    return await self.share(
        user_ids=(
            [target_entity_id] if target_entity_type == EntityType.USER else None
        ),
        group_ids=(
            [target_entity_id] if target_entity_type == EntityType.GROUP else None
        ),
        **kwargs,
    )

get_accesslist async

get_accesslist(
    return_raw: bool = False,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Relationship]

Get access list for the card as relationships.

Source code in src/crew_dcs/classes/DomoCard/access_controller.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
async def get_accesslist(
    self,
    return_raw: bool = False,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Relationship]:
    """Get access list for the card as relationships."""
    context = RouteContext.build_context(context=context, **context_kwargs)

    res = await card_routes.get_card_access_list(
        auth=self.auth,
        card_id=self.parent_id,
        is_expand_users=True,
        return_raw=return_raw,
        context=context,
    )

    if return_raw:
        return res

    relationships: list[Relationship] = []
    users = res.response.get("users", []) if isinstance(res.response, dict) else []
    groups = (
        res.response.get("groups", []) if isinstance(res.response, dict) else []
    )

    for user in users:
        user_id = user.get("id") or user.get("userId")
        if not user_id:
            continue
        relationships.append(
            Relationship(
                from_entity_id=str(user_id),
                from_entity_type=EntityType.USER,
                to_entity_id=str(self.parent_id),
                to_entity_type=EntityType.CARD,
                relationship_type=self._map_access_level(
                    user.get("accessLevel")
                    or user.get("permission")
                    or user.get("access")
                ),
                metadata={
                    "is_explicit_share": user.get("isExplicitShare", False),
                    **user,
                },
            )
        )

    for group in groups:
        group_id = group.get("id") or group.get("groupId")
        if not group_id:
            continue
        relationships.append(
            Relationship(
                from_entity_id=str(group_id),
                from_entity_type=EntityType.GROUP,
                to_entity_id=str(self.parent_id),
                to_entity_type=EntityType.CARD,
                relationship_type=self._map_access_level(
                    group.get("accessLevel")
                    or group.get("permission")
                    or group.get("access")
                ),
                metadata=group,
            )
        )

    owner_relationships = await self.get_owners(context=context)

    for owner_rel in owner_relationships:
        existing = next(
            (
                r
                for r in relationships
                if r.from_entity_id == owner_rel.from_entity_id
                and r.from_entity_type == owner_rel.from_entity_type
            ),
            None,
        )
        if existing:
            existing.relationship_type = RelationshipType.HAS_ACCESS_OWNER
            existing.metadata["is_owner"] = True
        else:
            relationships.append(owner_rel)

    self.relationships = relationships
    self.invalidate_cache()
    return relationships

get_direct_relationships async

get_direct_relationships() -> list[Relationship]

Get direct relationships for the card.

Source code in src/crew_dcs/classes/DomoCard/access_controller.py
35
36
37
async def get_direct_relationships(self) -> list[Relationship]:
    """Get direct relationships for the card."""
    return await self.get_accesslist()

get_owner_entities async

get_owner_entities(
    is_suppress_errors: bool = True,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Any]

Resolve owner entities (users/groups) for this card.

Parameters:

Name Type Description Default
is_suppress_errors bool

When True, suppress DomoError resolution failures.

True
context RouteContext | None

Optional RouteContext for API call configuration

None
**context_kwargs

Additional context parameters

{}

Returns:

Type Description
list[Any]

list of DomoUser/DomoGroup entities

Source code in src/crew_dcs/classes/DomoCard/access_controller.py
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
async def get_owner_entities(
    self,
    is_suppress_errors: bool = True,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Any]:
    """Resolve owner entities (users/groups) for this card.

    Args:
        is_suppress_errors: When True, suppress DomoError resolution failures.
        context: Optional RouteContext for API call configuration
        **context_kwargs: Additional context parameters

    Returns:
        list of DomoUser/DomoGroup entities
    """
    from ..DomoGroup.core import DomoGroup
    from ..DomoUser import DomoUser

    context = RouteContext.build_context(context=context, **context_kwargs)

    relationships = await self.get_owners(context=context)

    tasks = []
    for rel in relationships:
        try:
            if rel.from_entity_type == EntityType.USER:
                tasks.append(
                    DomoUser.get_by_id(
                        auth=self.auth,
                        user_id=rel.from_entity_id,
                        context=context,
                    )
                )
            elif rel.from_entity_type == EntityType.GROUP:
                tasks.append(
                    DomoGroup.get_by_id(
                        auth=self.auth,
                        group_id=rel.from_entity_id,
                        context=context,
                    )
                )
        except DomoError:
            if not is_suppress_errors:
                raise

    if not tasks:
        return []

    return await dmce.gather_with_concurrency(n=60, *tasks)  # noqa: B026

get_owners async

get_owners(
    *, context: RouteContext | None = None, **context_kwargs
) -> list[Relationship]

Get owner relationships for the card.

Source code in src/crew_dcs/classes/DomoCard/access_controller.py
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
async def get_owners(
    self,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Relationship]:
    """Get owner relationships for the card."""
    context = RouteContext.build_context(context=context, **context_kwargs)

    res = await card_routes.get_card_metadata(
        auth=self.auth,
        card_id=self.parent_id,
        optional_parts="owners",
        context=context,
    )

    owners = (
        res.response.get("owners", []) if isinstance(res.response, dict) else []
    )

    relationships: list[Relationship] = []
    for owner in owners:
        owner_id = owner.get("id")
        owner_type = owner.get("type", "USER")
        if not owner_id:
            continue
        relationships.append(
            Relationship(
                from_entity_id=str(owner_id),
                from_entity_type=(
                    EntityType.GROUP if owner_type == "GROUP" else EntityType.USER
                ),
                to_entity_id=str(self.parent_id),
                to_entity_type=EntityType.CARD,
                relationship_type=RelationshipType.HAS_ACCESS_OWNER,
                metadata={"is_owner": True, **owner},
            )
        )

    return relationships

remove_owners async

remove_owners(
    domo_users: list[Any] | None = None,
    domo_groups: list[Any] | None = None,
    user_ids: list[str | int] | None = None,
    group_ids: list[str | int] | None = None,
    return_raw: bool = False,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> bool

Remove owners from the card.

Source code in src/crew_dcs/classes/DomoCard/access_controller.py
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
async def remove_owners(
    self,
    domo_users: list[Any] | None = None,
    domo_groups: list[Any] | None = None,
    user_ids: list[str | int] | None = None,
    group_ids: list[str | int] | None = None,
    return_raw: bool = False,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> bool:
    """Remove owners from the card."""
    context = RouteContext.build_context(context=context, **context_kwargs)

    user_ids = set(self._collect_ids(domo_users, user_ids))
    group_ids = set(self._collect_ids(domo_groups, group_ids))

    existing = await self._get_owner_entries(context=context)
    remaining = [
        owner
        for owner in existing
        if not (
            (owner.get("type") == "USER" and owner.get("id") in user_ids)
            or (owner.get("type") == "GROUP" and owner.get("id") in group_ids)
        )
    ]

    if not remaining:
        raise ValueError("Cannot remove all owners; owners would be empty")

    res = await card_routes.replace_card_owners(
        auth=self.auth,
        card_id=self.parent_id,
        owners=remaining,
        context=context,
    )

    return res if return_raw else res.is_success

remove_relationship async

remove_relationship(
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs
) -> bool

Remove an access relationship for the card.

Source code in src/crew_dcs/classes/DomoCard/access_controller.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
async def remove_relationship(
    self,
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs,
) -> bool:
    """Remove an access relationship for the card."""
    if relationship_type == RelationshipType.HAS_ACCESS_OWNER:
        return await self.remove_owners(
            user_ids=(
                [target_entity_id]
                if target_entity_type == EntityType.USER
                else None
            ),
            group_ids=(
                [target_entity_id]
                if target_entity_type == EntityType.GROUP
                else None
            ),
            **kwargs,
        )

    raise NotImplementedError(
        "Removing card share relationships is not supported by current routes."
    )

share async

share(
    domo_users: list[Any] | None = None,
    domo_groups: list[Any] | None = None,
    user_ids: list[str | int] | None = None,
    group_ids: list[str | int] | None = None,
    message: str | None = None,
    return_raw: bool = False,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> bool

Share the card with users or groups.

Source code in src/crew_dcs/classes/DomoCard/access_controller.py
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
async def share(
    self,
    domo_users: list[Any] | None = None,
    domo_groups: list[Any] | None = None,
    user_ids: list[str | int] | None = None,
    group_ids: list[str | int] | None = None,
    message: str | None = None,
    return_raw: bool = False,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> bool:
    """Share the card with users or groups."""
    context = RouteContext.build_context(context=context, **context_kwargs)

    user_ids = self._collect_ids(domo_users, user_ids)
    group_ids = self._collect_ids(domo_groups, group_ids)

    if not user_ids and not group_ids:
        raise ValueError("Must provide user or group identifiers to share card")

    res = await share_resource(
        auth=self.auth,
        resource_ids=self.parent_id,
        resource_type=ShareResource_Enum.CARD,
        group_ids=group_ids,
        user_ids=user_ids,
        message=message,
        return_raw=return_raw,
        context=context,
    )

    return res if return_raw else res.is_success

DomoCard_BeastModesManager dataclass

DomoCard_BeastModesManager(
    parent: DomoEntity,
    beast_modes: list[DomoBeastMode] = list(),
)

Bases: DomoSubEntity

Manager for beast modes associated with a DomoCard.

Provides typed access to all beast modes linked to a card.

Usage

bms = await card.BeastModes.get() card.BeastModes.beast_modes # list[DomoBeastMode]

get async

get(
    *, context: RouteContext | None = None, **context_kwargs
) -> list[DomoBeastMode]

Fetch beast modes linked to this card.

Delegates to DomoBeastModes.get_by_card and caches the result.

Parameters:

Name Type Description Default
context RouteContext | None

Optional RouteContext for request configuration

None
**context_kwargs

Additional context parameters

{}

Returns:

Type Description
list[DomoBeastMode]

List of DomoBeastMode instances linked to this card

Source code in src/crew_dcs/classes/DomoCard/manager_beastmodes.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
async def get(
    self,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[DomoBeastMode]:  # noqa: F821
    """Fetch beast modes linked to this card.

    Delegates to DomoBeastModes.get_by_card and caches the result.

    Args:
        context: Optional RouteContext for request configuration
        **context_kwargs: Additional context parameters

    Returns:
        List of DomoBeastMode instances linked to this card
    """
    from ..DomoBeastMode.manager import DomoBeastModes

    self.beast_modes = await DomoBeastModes(auth=self.parent.auth).get_by_card(
        card_id=self.parent.id,
        context=context,
        **context_kwargs,
    )
    return self.beast_modes

DomoCard_DatasetsManager dataclass

DomoCard_DatasetsManager(
    auth: DomoAuth,
    parent: DomoCard_Default = None,
    datasets: list[Any] = list(),
)

Bases: DomoManager

Manager for datasets associated with a DomoCard.

Provides access to all datasets used by a card through its datasources. Inherits from DomoManager to follow standard entity manager patterns.

get async

get(
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    parent_auth_retrieval_fn: (
        Callable[[str], Any] | None
    ) = None,
    **context_kwargs
) -> list[Any]

Get all datasets associated with this card.

This retrieves datasets from the card's datasources and returns DomoDataset instances for each one.

Parameters:

Name Type Description Default
debug_api bool

Enable API debugging

False
session AsyncClient | None

Optional httpx session for request reuse

None

Returns:

Type Description
list[Any]

list[DomoDataset]: List of dataset objects associated with the card

Source code in src/crew_dcs/classes/DomoCard/manager_dataset.py
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
async def get(
    self,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    parent_auth_retrieval_fn: Callable[[str], Any] | None = None,
    **context_kwargs,
) -> list[Any]:  # Returns list[DomoDataset]
    """Get all datasets associated with this card.

    This retrieves datasets from the card's datasources and returns
    DomoDataset instances for each one.

    Args:
        debug_api: Enable API debugging
        session: Optional httpx session for request reuse

    Returns:
        list[DomoDataset]: List of dataset objects associated with the card
    """
    from ..DomoDataset import DomoDataset

    context = RouteContext.build_context(
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=1,
        **context_kwargs,
    )
    # Get datasources from card metadata if not already loaded
    if not self.parent.raw.get("datasources"):
        # Reload card with datasources
        res = await card_routes.get_card_metadata(
            auth=self.auth,
            card_id=self.parent.id,
            optional_parts="datasources",
            context=context,
        )
        self.parent.raw = res.response

    datasources = self.parent.raw.get("datasources", [])

    if not datasources:
        return []

    # Get dataset IDs from datasources
    dataset_ids = [
        ds.get("dataSourceId") for ds in datasources if ds.get("dataSourceId")
    ]

    if not dataset_ids:
        return []

    # Fetch all datasets concurrently
    # Auto-enable publish check and lineage tracing when parent_auth_retrieval_fn is provided
    datasets = await dmce.gather_with_concurrency(
        *[
            DomoDataset.get_by_id(
                auth=self.auth,
                dataset_id=dataset_id,
                check_if_published=bool(parent_auth_retrieval_fn),
                parent_auth_retrieval_fn=parent_auth_retrieval_fn,
                context=context,
            )
            for dataset_id in dataset_ids
        ],
        n=60,
    )

    self.datasets = datasets
    return datasets

DomoCard_Default dataclass

DomoCard_Default(
    auth: DomoAuth,
    id: str,
    raw: dict,
    Lineage: DomoLineage | None = None,
    Definition: DomoCard_DefinitionManager | None = None,
    Datasets: DomoCard_DatasetsManager | None = None,
    Owners: DomoCard_OwnerManager | None = None,
    Pages: DomoCard_PageManager | None = None,
    Access: DomoCardAccessController | None = None,
    BeastModes: DomoCard_BeastModesManager | None = None,
    title: str | None = None,
    description: str | None = None,
    type: str | None = None,
    urn: str | None = None,
    chart_type: str | None = None,
    badge_type: str | None = None,
    dataset_id: str | None = None,
    datastore_id: str | None = None,
    domo_collections: list[Any] = list(),
    domo_source_code: Any = None,
    certification: dict | None = None,
    _init_owners: InitVar[list[Any] | None] = None,
    pages: list[Any] = list(),
)

Bases: DomoEntity_w_Lineage

Base DomoCard implementation with core functionality

datasets property

datasets: list[Any]

Legacy property access - prefer using Datasets.get() for async operations

entity_name property

entity_name: str

Get the display name for this card.

Cards use the 'title' field as their display name.

Returns:

Type Description
str

Card title, or card ID as fallback

is_federated property

is_federated: bool

Check if this card is federated

name property

name: str

Get the display name for this card.

Implements abstract property from DomoEntity_w_Lineage. Cards use the 'title' field as their display name.

Returns:

Type Description
str

Card title, or "Untitled Card {id}" as fallback

owners property

owners: list[Any]

Return owners from the Owners manager when available.

add_owners async

add_owners(
    owners: list[Any],
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Any]

Add owners (users or groups) to this card.

Source code in src/crew_dcs/classes/DomoCard/card_default.py
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
async def add_owners(
    self,
    owners: list[Any],
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Any]:
    """Add owners (users or groups) to this card."""
    if not self.Owners:
        self.Owners = DomoCard_OwnerManager.from_parent(auth=self.auth, parent=self)

    return await self.Owners.add_owners(
        owners=owners,
        debug_api=debug_api,
        session=session,
        context=context,
        **context_kwargs,
    )

from_dict classmethod

from_dict(
    auth: DomoAuth,
    obj: dict[str, Any],
    owners: list[Any] | None = None,
    pages: list[Any] | None = None,
)

Synchronous factory method for dict → Card construction.

Build a DomoCard_Default instance from a metadata dictionary and owners list.

This method does not invoke any other class methods or perform additional API calls. All required data must be provided in the arguments.

Parameters:

Name Type Description Default
auth DomoAuth

DomoAuth instance for authentication

required
obj dict[str, Any]

Card metadata dictionary (from API)

required
owners list[Any] | None

List of owner entities (DomoUser/DomoGroup), optional

None

Returns:

Type Description

DomoCard_Default instance

Source code in src/crew_dcs/classes/DomoCard/card_default.py
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
@classmethod
def from_dict(
    cls,
    auth: DomoAuth,
    obj: dict[str, Any],
    owners: list[Any] | None = None,
    pages: list[Any] | None = None,
):
    """Synchronous factory method for dict → Card construction.

    Build a DomoCard_Default instance from a metadata dictionary and owners list.

    This method does not invoke any other class methods or perform additional API calls.
    All required data must be provided in the arguments.

    Args:
        auth: DomoAuth instance for authentication
        obj: Card metadata dictionary (from API)
        owners: List of owner entities (DomoUser/DomoGroup), optional

    Returns:
        DomoCard_Default instance
    """
    owners = owners or []
    pages = pages or []

    card = cls(
        auth=auth,
        id=obj.get("id"),
        raw=obj,
        title=obj.get("title"),
        description=obj.get("description"),
        type=obj.get("type"),
        urn=obj.get("urn"),
        certification=obj.get("certification"),
        chart_type=obj.get("metadata", {}).get("chartType"),
        badge_type=obj.get("metadata", {}).get("chartType"),  # same as chartType
        dataset_id=(
            obj.get("datasources", [])[0].get("dataSourceId")
            if obj.get("datasources")
            else None
        ),
        _init_owners=owners,
        pages=pages,
        datastore_id=obj.get("domoapp", {}).get("id"),
    )

    return card  # noqa: RET504

get_by_id async classmethod

get_by_id(
    auth: DomoAuth,
    card_id: str,
    optional_parts: str = "metadata,certification,datasources,drillPath,owners,properties,domoapp",
    check_if_published: bool | None = None,
    resolve_pages: bool | None = None,
    resolve_datasources: bool | None = None,
    parent_auth_retrieval_fn: Callable | None = None,
    parent_auth: DomoAuth | None = None,
    max_subscriptions_to_check: int | None = None,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    return_raw: bool = False,
    is_suppress_errors: bool = False,
    *,
    context: RouteContext | None = None,
    **context_kwargs
)

Retrieve a DomoCard by ID, including publication/certification status.

Parameters:

Name Type Description Default
auth DomoAuth

DomoAuth instance

required
card_id str

Card ID to retrieve

required
optional_parts str

Comma-separated metadata parts to include (owners, certification, etc.)

'metadata,certification,datasources,drillPath,owners,properties,domoapp'
check_if_published bool | None

When True, checks if card is published (federated + subscriptions). If None and either parent_auth or parent_auth_retrieval_fn is provided, defaults to True.

None
resolve_pages bool | None

When True, resolve page entities from metadata and link in lineage. If None, preserves current behavior (resolve pages if present).

None
resolve_datasources bool | None

When True, resolve dataset entities from datasources and link in lineage. If None, defaults to False (no dataset resolution).

None
parent_auth_retrieval_fn Callable | None

Callable returning publisher auth when given publisher domain

None
parent_auth DomoAuth | None

Pre-existing publisher auth (alternative to parent_auth_retrieval_fn)

None
max_subscriptions_to_check int | None

Optional limit when scanning subscriptions

None
debug_api bool

Enable API debug logging

False
session AsyncClient | None

Optional httpx session

None
return_raw bool

If True, return raw API response

False
is_suppress_errors bool

If True, suppress errors during owner resolution (handled via Access)

False
context RouteContext | None

Optional RouteContext for API call configuration

None
**context_kwargs

Additional context parameters

{}

Returns:

Type Description

DomoCard_Default instance (or raw response if return_raw)

Processing steps
  1. Fetch card metadata from API (includes owners, certification, datasources, etc.)
  2. If return_raw, return API response directly
  3. If card is federated and check_if_published is True, run additional check for publication status
  4. Build DomoCard_Default instance
Source code in src/crew_dcs/classes/DomoCard/card_default.py
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
@classmethod
@log_call(
    level_name="entity",
    config=LogDecoratorConfig(result_processor=DomoEntityObjectProcessor()),
)
async def get_by_id(  # noqa: C901
    cls,
    auth: DomoAuth,
    card_id: str,
    optional_parts: str = "metadata,certification,datasources,drillPath,owners,properties,domoapp",
    check_if_published: bool | None = None,
    resolve_pages: bool | None = None,
    resolve_datasources: bool | None = None,
    parent_auth_retrieval_fn: Callable | None = None,
    parent_auth: DomoAuth | None = None,
    max_subscriptions_to_check: int | None = None,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    return_raw: bool = False,
    is_suppress_errors: bool = False,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
):
    """
    Retrieve a DomoCard by ID, including publication/certification status.

    Args:
        auth: DomoAuth instance
        card_id: Card ID to retrieve
        optional_parts: Comma-separated metadata parts to include (owners, certification, etc.)
        check_if_published: When True, checks if card is published (federated + subscriptions).
            If None and either parent_auth or parent_auth_retrieval_fn is provided, defaults to True.
        resolve_pages: When True, resolve page entities from metadata and link in lineage.
            If None, preserves current behavior (resolve pages if present).
        resolve_datasources: When True, resolve dataset entities from datasources and link in lineage.
            If None, defaults to False (no dataset resolution).
        parent_auth_retrieval_fn: Callable returning publisher auth when given publisher domain
        parent_auth: Pre-existing publisher auth (alternative to parent_auth_retrieval_fn)
        max_subscriptions_to_check: Optional limit when scanning subscriptions
        debug_api: Enable API debug logging
        session: Optional httpx session
        return_raw: If True, return raw API response
        is_suppress_errors: If True, suppress errors during owner resolution (handled via Access)
        context: Optional RouteContext for API call configuration
        **context_kwargs: Additional context parameters

    Returns:
        DomoCard_Default instance (or raw response if return_raw)

    Processing steps:
        1. Fetch card metadata from API (includes owners, certification, datasources, etc.)
        2. If return_raw, return API response directly
        3. If card is federated and check_if_published is True, run additional check for publication status
        4. Build DomoCard_Default instance
    """
    # Auto-enable publish checking when parent auth is provided (either directly or via retrieval fn)
    # unless explicitly disabled
    if (parent_auth_retrieval_fn or parent_auth) and check_if_published is None:
        check_if_published = True

    context = RouteContext.build_context(
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=1,
        **context_kwargs,
    )

    resolve_pages = True if resolve_pages is None else resolve_pages
    resolve_datasources = (
        False if resolve_datasources is None else resolve_datasources
    )

    res = await card_routes.get_card_metadata(
        auth=auth,
        card_id=card_id,
        optional_parts=optional_parts,
        context=context,
    )

    if return_raw:
        return res

    pages: list[Any] = []

    # Check if published (if federated and check enabled)
    is_published = False
    subscription = None
    if (
        check_if_published
        and cls._is_federated(res.response)
        and (parent_auth_retrieval_fn or parent_auth)
    ):
        # Create a retrieval function from parent_auth if only parent_auth was provided
        effective_retrieval_fn = parent_auth_retrieval_fn
        if not effective_retrieval_fn and parent_auth:
            effective_retrieval_fn = lambda _domain: parent_auth  # noqa: E731

        # Local import to avoid circular dependency
        from ..subentity.lineage.federation_context import FederationContext

        probe = FederationContext.from_entity_id(
            auth=auth,
            entity_id=str(card_id),
            entity_type="CARD",
        )
        is_published = await probe.check_if_published(
            retrieve_parent_auth_fn=effective_retrieval_fn,
            entity_type="CARD",
            session=session,
            debug_api=debug_api,
            max_subscriptions_to_check=max_subscriptions_to_check,
            context=context,
        )
        if is_published:
            subscription = probe.subscription

    domo_card = cls.from_dict(
        auth=auth,
        obj=res.response,
        owners=[],
        pages=pages,
    )

    if resolve_pages and domo_card.Pages:
        pages = await domo_card.Pages.get(context=context)
        domo_card.pages = pages

    datasets: list[Any] = []
    if resolve_datasources and domo_card.Datasets:
        datasets = await domo_card.Datasets.get(
            parent_auth_retrieval_fn=parent_auth_retrieval_fn,
            context=context,
        )

    if domo_card.Lineage and (resolve_pages or resolve_datasources):
        from ..DomoDataset.lineage import DomoLineageLink_Dataset
        from ..DomoPage.lineage import DomoLineageLink_Page
        from .lineage import DomoLineageLink_Card

        card_link = DomoLineageLink_Card(
            auth=domo_card.auth,
            id=str(domo_card.id),
            entity=domo_card,
            _type=None,
            dependents=[],
            dependencies=[],
        )

        dataset_links: list[Any] = []
        for dataset in datasets:
            dataset_link = DomoLineageLink_Dataset(
                auth=dataset.auth,
                id=str(dataset.id),
                entity=dataset,
                _type=None,
                dependencies=[],
                dependents=[card_link],
            )
            dataset_links.append(dataset_link)
            card_link.dependencies.append(dataset_link)

        page_links: list[Any] = []
        for page in pages:
            page_link = DomoLineageLink_Page(
                auth=page.auth,
                id=str(page.id),
                entity=page,
                _type=None,
                dependencies=[card_link],
                dependents=[],
            )
            page_links.append(page_link)
            card_link.dependents.append(page_link)

        domo_card.Lineage.lineage = [card_link, *dataset_links, *page_links]
        domo_card.Lineage.immediate_dependencies = list(card_link.dependencies)
        domo_card.Lineage.immediate_dependents = list(card_link.dependents)

    if is_published and subscription:
        helper = domo_card.enable_federation_support()
        helper.hydrate_from_existing(
            subscription=subscription,
            parent_auth_retrieval_fn=parent_auth_retrieval_fn,
            parent_auth=parent_auth,
            content_type="CARD",
            entity_id=str(card_id),
        )

    # Auto-trace lineage if parent_auth_retrieval_fn is provided
    if parent_auth_retrieval_fn and domo_card.Lineage:
        await domo_card.Lineage.get(
            parent_auth_retrieval_fn=parent_auth_retrieval_fn,
            parent_auth=parent_auth,
            session=session,
            debug_api=debug_api,
            context=context,
        )

    return domo_card

get_owners async

get_owners(
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Any]

Refresh owners for this card.

Source code in src/crew_dcs/classes/DomoCard/card_default.py
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
async def get_owners(
    self,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Any]:
    """Refresh owners for this card."""
    if not self.Owners:
        self.Owners = DomoCard_OwnerManager.from_parent(auth=self.auth, parent=self)

    return await self.Owners.get(
        debug_api=debug_api,
        session=session,
        context=context,
        **context_kwargs,
    )

get_pages async

get_pages(
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Any]

Refresh pages this card appears on.

Source code in src/crew_dcs/classes/DomoCard/card_default.py
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
async def get_pages(
    self,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Any]:
    """Refresh pages this card appears on."""
    if not self.Pages:
        self.Pages = DomoCard_PageManager.from_parent(auth=self.auth, parent=self)

    return await self.Pages.get(
        debug_api=debug_api,
        session=session,
        context=context,
        **context_kwargs,
    )

hydrate_card_metadata async classmethod

hydrate_card_metadata(
    cards: list[DomoCard_Default],
    auth: DomoAuth,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> None

Batch-hydrate card metadata (chartType/badge_type) from the API.

The lineage API does not include card metadata. This method fetches metadata for all cards in a single batch request and updates their badge_type and chart_type fields in-place.

Parameters:

Name Type Description Default
cards list[DomoCard_Default]

List of DomoCard instances to hydrate

required
auth DomoAuth

Authentication object

required
context RouteContext | None

Optional RouteContext

None
**context_kwargs

Additional context parameters

{}
Source code in src/crew_dcs/classes/DomoCard/card_default.py
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
@classmethod
async def hydrate_card_metadata(
    cls,
    cards: list[DomoCard_Default],
    auth: DomoAuth,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> None:
    """Batch-hydrate card metadata (chartType/badge_type) from the API.

    The lineage API does not include card metadata. This method fetches
    metadata for all cards in a single batch request and updates their
    badge_type and chart_type fields in-place.

    Args:
        cards: List of DomoCard instances to hydrate
        auth: Authentication object
        context: Optional RouteContext
        **context_kwargs: Additional context parameters
    """
    if not cards:
        return

    context = RouteContext.build_context(context=context, **context_kwargs)

    # Filter to cards that don't already have badge_type
    cards_needing_hydration = [c for c in cards if not c.badge_type]
    if not cards_needing_hydration:
        return

    card_ids = [str(c.id) for c in cards_needing_hydration]

    res = await card_routes.get_cards_by_ids(
        card_ids=card_ids,
        auth=auth,
        context=context,
    )

    if not res.is_success:
        return

    # Build lookup by ID
    card_map = {str(c["id"]): c for c in res.response}

    for card in cards_needing_hydration:
        card_data = card_map.get(str(card.id))
        if not card_data:
            continue

        metadata = card_data.get("metadata", {})
        chart_type = metadata.get("chartType")
        if chart_type:
            card.badge_type = chart_type
            card.chart_type = chart_type

        # Also update raw if present
        if card.raw:
            card.raw.setdefault("metadata", {}).update(metadata)

remove_owners async

remove_owners(
    owners: list[Any],
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Any]

Remove owners (users or groups) from this card.

Source code in src/crew_dcs/classes/DomoCard/card_default.py
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
async def remove_owners(
    self,
    owners: list[Any],
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Any]:
    """Remove owners (users or groups) from this card."""
    if not self.Owners:
        self.Owners = DomoCard_OwnerManager.from_parent(auth=self.auth, parent=self)

    return await self.Owners.remove_owners(
        owners=owners,
        debug_api=debug_api,
        session=session,
        context=context,
        **context_kwargs,
    )

to_dict

to_dict(
    override_fn: Callable | None = None,
    return_snake_case: bool = False,
) -> dict

Serialize card for export/debugging with safe owner serialization.

Source code in src/crew_dcs/classes/DomoCard/card_default.py
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def to_dict(
    self, override_fn: Callable | None = None, return_snake_case: bool = False
) -> dict:
    """Serialize card for export/debugging with safe owner serialization."""
    if override_fn:
        return override_fn(self)

    result = super().to_dict(return_snake_case=return_snake_case)

    owners_key = "owners" if return_snake_case else "Owners"
    pages_key = "pages" if return_snake_case else "Pages"

    def _serialize_owner(owner: Any) -> Any:
        if owner is None:
            return None
        if isinstance(owner, dict):
            return owner

        owner_id = getattr(owner, "id", None)
        owner_type = getattr(owner, "entity_type", None) or owner.__class__.__name__
        display_name = getattr(owner, "display_name", None) or getattr(
            owner, "name", None
        )

        if return_snake_case:
            owner_dict = {"id": owner_id, "entity_type": owner_type}
            if display_name:
                owner_dict["display_name"] = display_name
        else:
            owner_dict = {"id": owner_id, "entityType": owner_type}
            if display_name:
                owner_dict["displayName"] = display_name

        return owner_dict

    if self.owners:
        result[owners_key] = [
            o for o in (_serialize_owner(o) for o in self.owners) if o is not None
        ]

    def _serialize_page(page: Any) -> Any:
        return self._serialize_related_entity(
            page,
            return_snake_case=return_snake_case,
            include_entity_type=False,
        )

    if self.pages:
        result[pages_key] = [
            p for p in (_serialize_page(p) for p in self.pages) if p is not None
        ]

    return result

DomoCard_OwnerManager dataclass

DomoCard_OwnerManager(
    auth: DomoAuth | None = None,
    parent: DomoCard_Default | None = None,
    owners: list[Any] = list(),
)

Bases: DomoRelationshipController

Manager for owners associated with a DomoCard.

add_owners async

add_owners(
    owners: list[Any],
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Any]

Add owners (users or groups) to this card.

Source code in src/crew_dcs/classes/DomoCard/manager_owner.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
async def add_owners(
    self,
    owners: list[Any],
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Any]:
    """Add owners (users or groups) to this card."""
    if not owners:
        raise ValueError("owners must be a non-empty list")

    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=1,
        **context_kwargs,
    )

    existing_owners = await self.get(context=context)
    existing = [self._normalize_owner(owner) for owner in existing_owners]
    existing = [owner for owner in existing if owner]

    additions = [self._normalize_owner(owner) for owner in owners]
    additions = [owner for owner in additions if owner]

    if not additions:
        raise ValueError("owners did not contain valid user or group entries")

    combined = {f"{o['type']}:{o['id']}": o for o in existing}
    combined.update({f"{o['type']}:{o['id']}": o for o in additions})

    await card_routes.replace_card_owners(
        auth=self.auth,
        card_id=self.parent.id,
        owners=list(combined.values()),
        context=context,
    )

    return await self.get(context=context)

create_relationship async

create_relationship(
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs
) -> bool

Create an owner relationship for this card.

Source code in src/crew_dcs/classes/DomoCard/manager_owner.py
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
async def create_relationship(
    self,
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs,
) -> bool:
    """Create an owner relationship for this card."""
    if relationship_type != RelationshipType.HAS_ACCESS_OWNER:
        raise ValueError(
            "Only owner relationships are supported by DomoCard_OwnerManager"
        )

    if target_entity_type == EntityType.USER:
        await self.add_owners([{"id": target_entity_id, "type": "USER"}], **kwargs)
        return True

    if target_entity_type == EntityType.GROUP:
        await self.add_owners([{"id": target_entity_id, "type": "GROUP"}], **kwargs)
        return True

    raise ValueError(f"Unsupported owner entity type: {target_entity_type}")

get async

get(
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Any]

Get owners for this card.

Source code in src/crew_dcs/classes/DomoCard/manager_owner.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
async def get(
    self,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Any]:  # Returns list[DomoUser|DomoGroup]
    """Get owners for this card."""
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=1,
        **context_kwargs,
    )

    if not self.parent.Access:
        from .access_controller import DomoCardAccessController

        self.parent.Access = DomoCardAccessController.from_parent(
            parent=self.parent
        )

    owners = await self.parent.Access.get_owner_entities(
        is_suppress_errors=True,
        context=context,
    )

    self.owners = owners
    self.parent.owners = owners

    return owners

get_direct_relationships async

get_direct_relationships() -> list[Relationship]

Get owner relationships for this card.

Source code in src/crew_dcs/classes/DomoCard/manager_owner.py
38
39
40
41
42
43
44
45
46
47
async def get_direct_relationships(self) -> list[Relationship]:
    """Get owner relationships for this card."""
    if not self.parent.Access:
        from .access_controller import DomoCardAccessController

        self.parent.Access = DomoCardAccessController.from_parent(
            parent=self.parent
        )

    return await self.parent.Access.get_owners()

remove_owners async

remove_owners(
    owners: list[Any],
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Any]

Remove owners (users or groups) from this card.

Source code in src/crew_dcs/classes/DomoCard/manager_owner.py
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
async def remove_owners(
    self,
    owners: list[Any],
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Any]:
    """Remove owners (users or groups) from this card."""
    if not owners:
        raise ValueError("owners must be a non-empty list")

    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=1,
        **context_kwargs,
    )

    existing_owners = await self.get(context=context)
    existing = [self._normalize_owner(owner) for owner in existing_owners]
    existing = [owner for owner in existing if owner]

    removals = [self._normalize_owner(owner) for owner in owners]
    removals = [owner for owner in removals if owner]

    if not removals:
        raise ValueError("owners did not contain valid user or group entries")

    remove_keys = {f"{o['type']}:{o['id']}" for o in removals}
    updated = [
        owner
        for owner in existing
        if f"{owner['type']}:{owner['id']}" not in remove_keys
    ]

    if not updated:
        raise ValueError("Cannot remove all owners; owners would be empty")

    await card_routes.replace_card_owners(
        auth=self.auth,
        card_id=self.parent.id,
        owners=updated,
        context=context,
    )

    return await self.get(context=context)

remove_relationship async

remove_relationship(
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs
) -> bool

Remove an owner relationship for this card.

Source code in src/crew_dcs/classes/DomoCard/manager_owner.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
async def remove_relationship(
    self,
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs,
) -> bool:
    """Remove an owner relationship for this card."""
    if relationship_type != RelationshipType.HAS_ACCESS_OWNER:
        raise ValueError(
            "Only owner relationships are supported by DomoCard_OwnerManager"
        )

    if target_entity_type == EntityType.USER:
        await self.remove_owners(
            [{"id": target_entity_id, "type": "USER"}], **kwargs
        )
        return True

    if target_entity_type == EntityType.GROUP:
        await self.remove_owners(
            [{"id": target_entity_id, "type": "GROUP"}], **kwargs
        )
        return True

    raise ValueError(f"Unsupported owner entity type: {target_entity_type}")

DomoCard_PageManager dataclass

DomoCard_PageManager(
    auth: DomoAuth | None = None,
    parent: DomoCard_Default | None = None,
    pages: list[Any] = list(),
)

Bases: DomoRelationshipController

Manager for pages associated with a DomoCard.

add_pages async

add_pages(
    page_ids: list[str | int],
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Any]

Add this card to the specified pages.

Source code in src/crew_dcs/classes/DomoCard/manager_page.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
async def add_pages(
    self,
    page_ids: list[str | int],
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Any]:
    """Add this card to the specified pages."""
    if not page_ids:
        raise ValueError("page_ids must be a non-empty list")

    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=1,
        **context_kwargs,
    )

    existing_pages = await self.get(context=context)
    existing_ids = {str(getattr(pg, "id", pg)) for pg in existing_pages}
    updated_ids = sorted({*existing_ids, *{str(pid) for pid in page_ids}})

    await card_routes.replace_card_pages(
        auth=self.auth,
        card_id=self.parent.id,
        page_ids=updated_ids,
        context=context,
    )

    return await self.get(context=context)

create_relationship async

create_relationship(
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs
) -> bool

Create a card -> page relationship by adding the card to a page.

Source code in src/crew_dcs/classes/DomoCard/manager_page.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
async def create_relationship(
    self,
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs,
) -> bool:
    """Create a card -> page relationship by adding the card to a page."""
    if relationship_type != RelationshipType.CONTAINS:
        raise ValueError(
            "Only CONTAINS relationships are supported by DomoCard_PageManager"
        )

    if target_entity_type != EntityType.PAGE:
        raise ValueError(
            "Only PAGE relationships are supported by DomoCard_PageManager"
        )

    await self.add_pages([target_entity_id], **kwargs)
    return True

get async

get(
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Any]

Get pages that include this card.

Source code in src/crew_dcs/classes/DomoCard/manager_page.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
async def get(
    self,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Any]:  # Returns list[DomoPage]
    """Get pages that include this card."""
    from ..DomoPage import DomoPage

    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=1,
        **context_kwargs,
    )

    res = await card_routes.get_card_metadata(
        auth=self.auth,
        card_id=self.parent.id,
        optional_parts="pages",
        context=context,
    )

    page_items = res.response.get("pages", [])
    if not page_items:
        self.pages = []
        self.parent.pages = []
        return []

    page_ids: list[str] = []
    for page in page_items:
        page_id = None
        if isinstance(page, dict):
            page_id = page.get("id") or page.get("pageId") or page.get("page_id")
        else:
            page_id = getattr(page, "id", None) or getattr(page, "pageId", None)

        if page_id is not None:
            page_ids.append(str(page_id))

    if not page_ids:
        self.pages = []
        self.parent.pages = []
        return []

    pages = await dmce.gather_with_concurrency(
        n=60,
        *[  # noqa: B026
            DomoPage.get_by_id(auth=self.auth, page_id=page_id, context=context)
            for page_id in page_ids
        ],
    )

    self.pages = pages
    self.parent.pages = pages

    return pages

get_direct_relationships async

get_direct_relationships() -> list[Relationship]

Get relationships between this card and its pages.

Source code in src/crew_dcs/classes/DomoCard/manager_page.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
async def get_direct_relationships(self) -> list[Relationship]:
    """Get relationships between this card and its pages."""
    pages = await self.get()
    return [
        Relationship(
            from_entity_id=str(self.parent.id),
            from_entity_type=EntityType.CARD,
            to_entity_id=str(getattr(page, "id", page)),
            to_entity_type=EntityType.PAGE,
            relationship_type=RelationshipType.CONTAINS,
            metadata={"card_id": str(self.parent.id)},
        )
        for page in pages
    ]

remove_pages async

remove_pages(
    page_ids: list[str | int],
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[Any]

Remove this card from the specified pages.

Source code in src/crew_dcs/classes/DomoCard/manager_page.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
async def remove_pages(
    self,
    page_ids: list[str | int],
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[Any]:
    """Remove this card from the specified pages."""
    if not page_ids:
        raise ValueError("page_ids must be a non-empty list")

    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=1,
        **context_kwargs,
    )

    existing_pages = await self.get(context=context)
    existing_ids = {str(getattr(pg, "id", pg)) for pg in existing_pages}
    updated_ids = sorted(existing_ids.difference({str(pid) for pid in page_ids}))

    if not updated_ids:
        raise ValueError("Cannot remove all pages; page_ids would be empty")

    await card_routes.replace_card_pages(
        auth=self.auth,
        card_id=self.parent.id,
        page_ids=updated_ids,
        context=context,
    )

    return await self.get(context=context)

remove_relationship async

remove_relationship(
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs
) -> bool

Remove a card -> page relationship by removing the card from a page.

Source code in src/crew_dcs/classes/DomoCard/manager_page.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
async def remove_relationship(
    self,
    target_entity_id: str,
    target_entity_type: EntityType,
    relationship_type: RelationshipType,
    **kwargs,
) -> bool:
    """Remove a card -> page relationship by removing the card from a page."""
    if relationship_type != RelationshipType.CONTAINS:
        raise ValueError(
            "Only CONTAINS relationships are supported by DomoCard_PageManager"
        )

    if target_entity_type != EntityType.PAGE:
        raise ValueError(
            "Only PAGE relationships are supported by DomoCard_PageManager"
        )

    await self.remove_pages([target_entity_id], **kwargs)
    return True