Skip to content

publish_lineage

publish_lineage

Publish lineage mixin for DomoLineage.

Handles tracing the publish-lineage chain: subscriber → subscription → publication → publisher entity → publisher lineage

Extracted from base.py for separation of concerns.

DomoLineage_PublishMixin

Publish lineage methods for DomoLineage.

Provides methods for tracing the full publish-lineage chain from subscriber entities through subscriptions and publications to their publisher counterparts.

This mixin is mixed into DomoLineage in base.py.

get_federated_lineage async

get_federated_lineage(
    session: AsyncClient | None = None,
    debug_api: bool = False,
    return_raw: bool = False,
    parent_auth: DomoAuth | None = None,
    parent_auth_retrieval_fn: Callable | None = None,
    _debug_num_stacks_to_drop: int = 3,
    *,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[DomoLineage_Link]

Get lineage for a federated entity.

Always delegates to trace_publish_lineage() which builds the full chain: subscriber → subscription → publication → publisher → publisher lineage.

If the subscription hasn't been discovered yet, ensure_subscription() is called first. If no subscription is found the entity is not published and an empty list is returned.

Source code in src/crew_dcs/classes/subentity/lineage/publish_lineage.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
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
async def get_federated_lineage(
    self,
    session: httpx.AsyncClient | None = None,
    debug_api: bool = False,
    return_raw: bool = False,
    parent_auth: DomoAuth | None = None,
    parent_auth_retrieval_fn: Callable | None = None,
    _debug_num_stacks_to_drop: int = 3,
    *,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[DomoLineage_Link]:
    """Get lineage for a federated entity.

    Always delegates to trace_publish_lineage() which builds the full chain:
    subscriber → subscription → publication → publisher → publisher lineage.

    If the subscription hasn't been discovered yet, ensure_subscription() is
    called first.  If no subscription is found the entity is not published
    and an empty list is returned.
    """
    if not self.parent and (not self.parent_type):
        raise ValueError(
            "Parent must be set. Use from_parent() to create lineage with a parent."
        )

    # Ensure federation context exists
    if not getattr(self.parent, "Federation", None):
        self.parent.enable_federation_support()

    federation = self.parent.Federation

    # Discover subscription if not already known
    if not federation.is_published:
        await federation.ensure_subscription(
            retrieve_parent_auth_fn=parent_auth_retrieval_fn,
            parent_auth=parent_auth,
            entity_type=self.parent.entity_type,
            entity_id=str(self.parent.id),
            session=session,
            debug_api=debug_api,
        )

    if not federation.subscription:
        await logger.warning(
            f"get_federated_lineage: entity {self.parent.id} is federated but no "
            f"subscription found — skipping publisher lineage traversal.",
            entity_id=str(self.parent.id),
            entity_type=self.parent.entity_type,
        )
        return []

    # Delegate to trace_publish_lineage which builds the full chain
    # (subscriber → subscription → publication → publisher → publisher lineage)
    return await self.trace_publish_lineage(
        publish_helper=federation,
        parent_auth=parent_auth,
        parent_auth_retrieval_fn=parent_auth_retrieval_fn,
        context=context,
        **context_kwargs,
    )

get_publish_lineage async

get_publish_lineage(
    *,
    publish_helper=None,
    parent_auth: DomoAuth | None = None,
    parent_auth_retrieval_fn: Callable | None = None,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[DomoLineage_Link]

Build lineage chain for published entities.

.. deprecated:: Use :meth:trace_publish_lineage instead. This thin wrapper exists only for backward compatibility.

Source code in src/crew_dcs/classes/subentity/lineage/publish_lineage.py
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
async def get_publish_lineage(
    self,
    *,
    publish_helper=None,
    parent_auth: DomoAuth | None = None,
    parent_auth_retrieval_fn: Callable | None = None,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[DomoLineage_Link]:
    """Build lineage chain for published entities.

    .. deprecated::
        Use :meth:`trace_publish_lineage` instead.  This thin wrapper
        exists only for backward compatibility.
    """
    return await self.trace_publish_lineage(
        publish_helper=publish_helper,
        parent_auth=parent_auth,
        parent_auth_retrieval_fn=parent_auth_retrieval_fn,
        context=context,
        **context_kwargs,
    )

trace_publish_lineage async

trace_publish_lineage(
    *,
    publish_helper=None,
    parent_auth: DomoAuth | None = None,
    parent_auth_retrieval_fn: Callable | None = None,
    max_depth: int = 100,
    context: RouteContext | None = None,
    **context_kwargs
) -> list[DomoLineage_Link]

Build the full publish-lineage chain for a federated published entity.

Orchestrates: 1. Ensure subscription is resolved (via FederationContext). 2. Resolve publisher auth. 3. Fetch parent publication. 4. Call _resolve_publisher_entity (overridable hook) to find the publisher-side entity that corresponds to self.parent. 5. Fetch the publisher entity's own lineage. 6. Assemble the chain: subscriber entity → subscription → publication → publisher entity → publisher entity lineage.

Subclasses override _resolve_publisher_entity to customise step 4. For example DomoLineage_Card handles cards that are indirectly published via pages.

Parameters:

Name Type Description Default
publish_helper

FederationContext (defaults to self.Federation).

None
parent_auth DomoAuth | None

Explicit publisher auth (alternative to retrieval fn).

None
parent_auth_retrieval_fn Callable | None

Callable(domain) → DomoAuth.

None
max_depth int

Depth limit for publisher entity lineage.

100
context RouteContext | None

Route context for API calls.

None

Returns:

Type Description
list[DomoLineage_Link]

Flat list of lineage links representing the full chain.

Source code in src/crew_dcs/classes/subentity/lineage/publish_lineage.py
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
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
229
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
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
339
340
341
342
343
async def trace_publish_lineage(
    self,
    *,
    publish_helper=None,
    parent_auth: DomoAuth | None = None,
    parent_auth_retrieval_fn: Callable | None = None,
    max_depth: int = 100,
    context: RouteContext | None = None,
    **context_kwargs,
) -> list[DomoLineage_Link]:
    """Build the full publish-lineage chain for a federated published entity.

    Orchestrates:
    1. Ensure subscription is resolved (via FederationContext).
    2. Resolve publisher auth.
    3. Fetch parent publication.
    4. Call ``_resolve_publisher_entity`` (overridable hook) to find
       the publisher-side entity that corresponds to ``self.parent``.
    5. Fetch the publisher entity's own lineage.
    6. Assemble the chain:
       subscriber entity → subscription → publication → publisher entity
       → publisher entity lineage.

    Subclasses override ``_resolve_publisher_entity`` to customise step 4.
    For example ``DomoLineage_Card`` handles cards that are indirectly
    published via pages.

    Args:
        publish_helper: FederationContext (defaults to ``self.Federation``).
        parent_auth: Explicit publisher auth (alternative to retrieval fn).
        parent_auth_retrieval_fn: Callable(domain) → DomoAuth.
        max_depth: Depth limit for publisher entity lineage.
        context: Route context for API calls.

    Returns:
        Flat list of lineage links representing the full chain.
    """
    from ...DomoEverywhere.lineage import (
        DomoLineageLink_Publication,
        DomoLineageLink_Subscription,
    )

    # --- 1. Ensure subscription ---
    helper = publish_helper or self.Federation
    if not helper:
        raise ValueError(
            "No federation helper available. "
            "Call check_is_published() first or provide publish_helper."
        )

    subscription = await helper.ensure_subscription(
        retrieve_parent_auth_fn=parent_auth_retrieval_fn,
        parent_auth=parent_auth,
        entity_type=self.parent.entity_type,
        entity_id=str(self.parent.id),
        context=context,
        **context_kwargs,
    )
    if not subscription:
        raise DomoError(
            f"Failed to resolve subscription for published entity "
            f"{self.parent.entity_type}:{self.parent.id}"
        )

    # --- 2. Resolve publisher auth ---
    publisher_auth = await helper.get_publisher_auth(
        parent_auth=parent_auth,
        context=context,
        **context_kwargs,
    )

    # --- 3. Fetch parent publication ---
    publication = await helper.get_parent_publication(
        parent_auth=publisher_auth,
        context=context,
        **context_kwargs,
    )

    # --- 4. Resolve publisher entity (overridable hook) ---
    subscriber_domain = subscription.subscriber_domain
    resolution = await self._resolve_publisher_entity(
        publication=publication,
        publisher_auth=publisher_auth,
        subscriber_domain=subscriber_domain,
        context=context,
        **context_kwargs,
    )
    publisher_entity = resolution.entity
    intermediate_entities = resolution.intermediate_entities

    # --- 5. Fetch publisher entity lineage ---
    publisher_entity_lineage: list[DomoLineage_Link] = []
    publisher_entity_link = None

    if publisher_entity:
        publisher_entity_lineage = await publisher_entity.Lineage.get(
            return_raw=False,
            max_depth=max_depth,
            context=context,
            **context_kwargs,
        )

        # Find the publisher entity's own link within its lineage — it
        # already has the correct direct dependencies wired by
        # get_datacenter_lineage().  Only create a new link as a fallback.
        publisher_entity_link = next(
            (
                link
                for link in publisher_entity_lineage
                if str(link.id) == str(publisher_entity.id)
            ),
            None,
        )

        if publisher_entity_link is None:
            link_type = _map_entity_type(publisher_entity.entity_type)
            link_cls = _get_lineage_link_class(link_type)
            publisher_entity_link = link_cls(
                auth=publisher_auth,
                id=str(publisher_entity.id),
                entity=publisher_entity,
                _type=None,
                dependencies=[],
                dependents=[],
            )

    # --- 5b. Build intermediate links (e.g., page for indirect card publication) ---
    # Intermediate entities sit between publication and publisher_entity.
    # Build in reverse so each link's dependencies point to the next in the chain.
    intermediate_links: list[DomoLineage_Link] = []
    next_dependency = publisher_entity_link

    for intermediate_entity in reversed(intermediate_entities):
        inter_link_type = _map_entity_type(intermediate_entity.entity_type)
        inter_link_cls = _get_lineage_link_class(inter_link_type)
        inter_link = inter_link_cls(
            auth=publisher_auth,
            id=str(intermediate_entity.id),
            entity=intermediate_entity,
            _type=None,
            dependencies=[next_dependency] if next_dependency else [],
            dependents=[],
        )
        if next_dependency:
            next_dependency.dependents = [inter_link]
        intermediate_links.insert(0, inter_link)
        next_dependency = inter_link

    # Determine what the publication link should point to
    publication_first_dep = (
        intermediate_links[0] if intermediate_links else publisher_entity_link
    )

    # --- 6. Assemble the chain ---
    publication_link = DomoLineageLink_Publication(
        auth=publisher_auth,
        id=str(publication.id),
        entity=publication,
        _type="PUBLICATION",
        dependents=[],
        dependencies=([publication_first_dep] if publication_first_dep else []),
    )

    subscription_link = DomoLineageLink_Subscription(
        auth=self.parent.auth,
        id=str(subscription.id),
        entity=subscription,
        _type="SUBSCRIPTION",
        dependents=[],
        dependencies=[publication_link],
    )

    subscriber_link = _get_lineage_link_class(
        _map_entity_type(self.parent.entity_type)
    )(
        auth=self.parent.auth,
        id=str(self.parent.id),
        entity=self.parent,
        _type=None,
        dependencies=[subscription_link],
        dependents=[],
    )

    # Wire back-pointers
    subscription_link.dependents = [subscriber_link]
    publication_link.dependents = [subscription_link]
    if publication_first_dep:
        publication_first_dep.dependents = [publication_link]

    chain = [
        subscriber_link,
        subscription_link,
        publication_link,
    ]
    chain.extend(intermediate_links)
    # publisher_entity_link is already within publisher_entity_lineage
    # (found by id match), so only extend — don't append separately.
    chain.extend(publisher_entity_lineage)

    return chain