Skip to content

DomoWorkflow

DomoWorkflow

DomoWorkflow dataclass

DomoWorkflow(
    auth: DomoAuth,
    id: str,
    raw: dict = dict(),
    name: str | None = None,
    description: str | None = None,
    owner_id: str | None = None,
    created_by: str | None = None,
    created_dt: datetime | None = None,
    updated_by: str | None = None,
    updated_dt: datetime | None = None,
    last_version_updated_dt: datetime | None = None,
    last_version_updated_by: str | None = None,
    current_version: DomoWorkflow_Version | None = None,
)

Bases: DomoEntity

A Domo workflow model and its versions.

Attributes:

Name Type Description
id str

Workflow model UUID.

auth DomoAuth

Authenticated Domo session.

raw dict

Raw API payload the entity was built from.

name str | None

Workflow name.

description str | None

Workflow description.

owner_id str | None

User ID of the owner.

created_by str | None

User ID who created the model.

created_dt datetime | None

Creation timestamp.

updated_by str | None

User ID who last updated the model.

updated_dt datetime | None

Last-update timestamp.

last_version_updated_dt datetime | None

When a version was last updated.

last_version_updated_by str | None

User ID who last updated a version.

versions DomoWorkflow_Versions

Manager for this model's versions.

current_version DomoWorkflow_Version | None

Most recently fetched version, if any.

display_url property

display_url: str

Return the Domo UI URL for this workflow.

from_dict classmethod

from_dict(
    auth: DomoAuth, obj: dict[str, Any]
) -> DomoWorkflow

Build a workflow from a /v1/models API payload.

Parameters:

Name Type Description Default
auth DomoAuth

Authenticated Domo session.

required
obj dict[str, Any]

Raw workflow model dict.

required

Returns:

Type Description
DomoWorkflow

The populated workflow.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
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
@classmethod
def from_dict(cls, auth: DomoAuth, obj: dict[str, Any]) -> DomoWorkflow:
    """Build a workflow from a ``/v1/models`` API payload.

    Args:
        auth: Authenticated Domo session.
        obj: Raw workflow model dict.

    Returns:
        The populated workflow.
    """
    return cls(
        auth=auth,
        id=obj.get("id", ""),
        raw=obj,
        name=obj.get("name"),
        description=obj.get("description"),
        owner_id=obj.get("owner"),
        created_by=obj.get("createdBy"),
        created_dt=convert_string_to_datetime(obj.get("createdOn")),
        updated_by=obj.get("updatedBy"),
        updated_dt=convert_string_to_datetime(obj.get("updatedOn")),
        last_version_updated_dt=convert_string_to_datetime(
            obj.get("lastVersionUpdatedOn")
        ),
        last_version_updated_by=obj.get("lastVersionUpdatedBy"),
    )

get_by_id async classmethod

get_by_id(
    auth: DomoAuth,
    id: str,
    debug_num_stacks_to_drop: int = 2,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> DomoWorkflow | None

Fetch a workflow by ID (DomoEntity contract).

Parameters:

Name Type Description Default
auth DomoAuth

Authenticated Domo session.

required
id str

Workflow model UUID.

required
debug_num_stacks_to_drop int

Stack frames to drop in debug logging.

2
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
DomoWorkflow | None

The workflow, or None when the lookup is unsuccessful.

Source code in src/crew_dcs/classes/DomoWorkflow/core.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
@classmethod
async def get_by_id(
    cls,
    auth: DomoAuth,
    id: str,
    debug_num_stacks_to_drop: int = 2,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> DomoWorkflow | None:
    """Fetch a workflow by ID (``DomoEntity`` contract).

    Args:
        auth: Authenticated Domo session.
        id: Workflow model UUID.
        debug_num_stacks_to_drop: Stack frames to drop in debug logging.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The workflow, or None when the lookup is unsuccessful.
    """
    return await cls.get_entity_by_id(
        auth=auth,
        entity_id=id,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        debug_api=debug_api,
        session=session,
        context=context,
        **context_kwargs,
    )

get_entity_by_id async classmethod

get_entity_by_id(
    auth: DomoAuth,
    entity_id: str,
    debug_num_stacks_to_drop: int = 2,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> DomoWorkflow | None

Fetch a workflow model by ID.

Parameters:

Name Type Description Default
auth DomoAuth

Authenticated Domo session.

required
entity_id str

Workflow model UUID.

required
debug_num_stacks_to_drop int

Stack frames to drop in debug logging.

2
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
DomoWorkflow | None

The workflow, or None when the lookup is unsuccessful.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
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
@classmethod
async def get_entity_by_id(
    cls,
    auth: DomoAuth,
    entity_id: str,
    debug_num_stacks_to_drop: int = 2,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> DomoWorkflow | None:
    """Fetch a workflow model by ID.

    Args:
        auth: Authenticated Domo session.
        entity_id: Workflow model UUID.
        debug_num_stacks_to_drop: Stack frames to drop in debug logging.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The workflow, or None when the lookup is unsuccessful.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        debug_num_stacks_to_drop=debug_num_stacks_to_drop,
        **context_kwargs,
    )

    res = await workflow_routes.get_workflow_by_id(
        auth=auth,
        workflow_id=entity_id,
        context=context,
    )

    if not res.is_success:
        return None

    return cls.from_dict(auth=auth, obj=res.response)

get_execution_metrics async

get_execution_metrics(
    model_version: str | None = None,
    status: str | None = None,
    trigger_types: str | None = None,
    started_by: str | None = None,
    started_after: str | None = None,
    finished_before: str | None = None,
    return_raw: bool = False,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

Get execution metrics for this workflow model.

Parameters:

Name Type Description Default
model_version str | None

Only executions of this model version.

None
status str | None

Only executions with this status.

None
trigger_types str | None

Only executions started by these trigger types.

None
started_by str | None

Only executions started by this user ID.

None
started_after str | None

Only executions started after this timestamp.

None
finished_before str | None

Only executions finished before this timestamp.

None
return_raw bool

If True, return the response without raising on failure.

False
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
ResponseGetData

The API response.

Raises:

Type Description
Workflow_GET_Error

If the response is not successful and return_raw is False.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
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
357
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
async def get_execution_metrics(
    self,
    model_version: str | None = None,
    status: str | None = None,
    trigger_types: str | None = None,
    started_by: str | None = None,
    started_after: str | None = None,
    finished_before: str | None = None,
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """Get execution metrics for this workflow model.

    Args:
        model_version: Only executions of this model version.
        status: Only executions with this status.
        trigger_types: Only executions started by these trigger types.
        started_by: Only executions started by this user ID.
        started_after: Only executions started after this timestamp.
        finished_before: Only executions finished before this timestamp.
        return_raw: If True, return the response without raising on failure.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The API response.

    Raises:
        Workflow_GET_Error: If the response is not successful and
            ``return_raw`` is False.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    return await workflow_routes.get_workflow_execution_metrics(
        auth=self.auth,
        model_id=self.id,
        model_version=model_version,
        status=status,
        trigger_types=trigger_types,
        started_by=started_by,
        started_after=started_after,
        finished_before=finished_before,
        return_raw=return_raw,
        context=context,
    )

get_locks async

get_locks(
    return_raw: bool = False,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

Get the locks held on this workflow model.

Parameters:

Name Type Description Default
return_raw bool

If True, return the response without raising on failure.

False
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
ResponseGetData

The API response.

Raises:

Type Description
Workflow_GET_Error

If the response is not successful and return_raw is False.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
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
async def get_locks(
    self,
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """Get the locks held on this workflow model.

    Args:
        return_raw: If True, return the response without raising on failure.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The API response.

    Raises:
        Workflow_GET_Error: If the response is not successful and
            ``return_raw`` is False.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    return await workflow_routes.get_workflow_locks(
        auth=self.auth,
        model_id=self.id,
        return_raw=return_raw,
        context=context,
    )

get_permissions async

get_permissions(
    return_raw: bool = False,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

Get the permissions on this workflow model.

Parameters:

Name Type Description Default
return_raw bool

If True, return the response without raising on failure.

False
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
ResponseGetData

The API response.

Raises:

Type Description
Workflow_GET_Error

If the response is not successful and return_raw is False.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
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
async def get_permissions(
    self,
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """Get the permissions on this workflow model.

    Args:
        return_raw: If True, return the response without raising on failure.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The API response.

    Raises:
        Workflow_GET_Error: If the response is not successful and
            ``return_raw`` is False.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    return await workflow_routes.get_workflow_permissions(
        auth=self.auth,
        model_id=self.id,
        return_raw=return_raw,
        context=context,
    )

get_version async

get_version(
    version: str = "1.0.0",
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> DomoWorkflow_Version | None

Fetch one specific version through the versions manager.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
async def get_version(
    self,
    version: str = "1.0.0",
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> DomoWorkflow_Version | None:
    """Fetch one specific version through the versions manager."""
    ver = await self.versions.get_by_version(
        version=version,
        debug_api=debug_api,
        session=session,
        context=context,
        **context_kwargs,
    )
    self.current_version = ver
    return ver

list_triggers async

list_triggers(
    return_raw: bool = False,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

List the triggers attached to this workflow model.

Parameters:

Name Type Description Default
return_raw bool

If True, return the response without raising on failure.

False
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
ResponseGetData

The API response.

Raises:

Type Description
Workflow_GET_Error

If the response is not successful and return_raw is False.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
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
async def list_triggers(
    self,
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """List the triggers attached to this workflow model.

    Args:
        return_raw: If True, return the response without raising on failure.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The API response.

    Raises:
        Workflow_GET_Error: If the response is not successful and
            ``return_raw`` is False.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    return await workflow_routes.list_workflow_triggers_by_model(
        auth=self.auth,
        workflow_id=self.id,
        return_raw=return_raw,
        context=context,
    )

upsert async classmethod

upsert(
    auth: DomoAuth,
    name: str,
    saved_json: str,
    version: str = "1.0.0",
    description: str = "",
    model_id: str | None = None,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> DomoWorkflow

Create a model (or reuse an existing one) then PUT the version.

If model_id is not supplied a new model is created via POST.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
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
577
578
@classmethod
async def upsert(
    cls,
    auth: DomoAuth,
    name: str,
    saved_json: str,
    version: str = "1.0.0",
    description: str = "",
    model_id: str | None = None,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> DomoWorkflow:
    """Create a model (or reuse an existing one) then PUT the version.

    If `model_id` is not supplied a new model is created via POST.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    is_new_model = model_id is None
    if is_new_model:
        res = await workflow_routes.upsert_workflow_model(
            auth=auth,
            name=name,
            description=description,
            context=context,
        )
        res.response = res.response or {}
        model_id = res.response.get("id")

    # New models have no versions yet — POST to create; existing models use PUT
    if is_new_model:
        await workflow_routes.create_workflow_version_from_saved_json(
            auth=auth,
            model_id=model_id,
            version=version,
            saved_json=saved_json,
            context=context,
        )
    else:
        await cls._assert_version_is_updatable(
            auth=auth,
            model_id=model_id,
            version=version,
            context=context,
        )
        await workflow_routes.upsert_workflow_version(
            auth=auth,
            model_id=model_id,
            version=version,
            saved_json=saved_json,
            context=context,
        )

    return await cls.get_entity_by_id(
        auth=auth,
        entity_id=model_id,
        context=context,
    )

validate_model_version async classmethod

validate_model_version(
    auth: DomoAuth,
    model_id: str,
    version: str,
    use_domo_validator: bool = False,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> tuple[bool, list[str]]

Fetch a workflow version by ID and validate its graph topology.

Runs local structural validation (connection rules + True/False branch labels on every conditional gateway) by default. Pass use_domo_validator=True to additionally call Domo's official /api/workflow/v2/models/validate endpoint and fold in any errors it reports.

Returns (is_valid, errors) — errors is empty when is_valid is True.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
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
@classmethod
async def validate_model_version(
    cls,
    auth: DomoAuth,
    model_id: str,
    version: str,
    use_domo_validator: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> tuple[bool, list[str]]:
    """Fetch a workflow version by ID and validate its graph topology.

    Runs local structural validation (connection rules + True/False branch
    labels on every conditional gateway) by default. Pass
    ``use_domo_validator=True`` to additionally call Domo's official
    ``/api/workflow/v2/models/validate`` endpoint and fold in any errors
    it reports.

    Returns (is_valid, errors) — errors is empty when is_valid is True.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    workflow = await cls.get_entity_by_id(
        auth=auth, entity_id=model_id, context=context
    )
    if workflow is None:
        return False, [f"workflow not found: {model_id}"]

    ver = await workflow.get_version(version=version, context=context)
    if ver is None or not ver.tiles:
        return False, [f"workflow version not found: {model_id}@{version}"]

    is_valid, errors = ver.validate_definition()
    errors = list(errors)

    if use_domo_validator:
        try:
            res = await ver.validate_with_domo(auth=auth, context=context)
            if not res.is_success:
                is_valid = False
                errors.append(
                    f"Domo validator rejected the workflow: {res.response}"
                )
        except Exception as exc:  # noqa: BLE001
            is_valid = False
            errors.append(f"Domo validator call failed: {exc}")

    return is_valid, errors

DomoWorkflow_Definition dataclass

DomoWorkflow_Definition(
    schema_version: int = 2,
    inputs: dict[str, DomoWorkflow_Parameter] = dict(),
    outputs: dict[str, DomoWorkflow_Parameter] = dict(),
    tiles: list[DomoWorkflow_Tile] = list(),
    edges: list[DomoWorkflow_Edge] = list(),
    data_list: list = list(),
    Design: DomoWorkflow_Design | None = None,
    raw: dict = dict(),
)

Parsed representation of a workflow's savedJson.

Separates designElements into typed tiles and edges, and exposes helpers for programmatic construction.

layouts property writable

layouts: list

Alias for data_list to align with Design/Tiles/Layouts terminology.

add_boolean_branch_edges

add_boolean_branch_edges(
    gateway_id: str,
    true_target_id: str,
    false_target_id: str,
    *,
    true_label: str = "True",
    false_label: str = "False",
    condition_type: str = "Basic"
) -> tuple[DomoWorkflow_Edge, DomoWorkflow_Edge]

Add the required True/False branches for a conditional gateway.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
def add_boolean_branch_edges(
    self,
    gateway_id: str,
    true_target_id: str,
    false_target_id: str,
    *,
    true_label: str = "True",
    false_label: str = "False",
    condition_type: str = "Basic",
) -> tuple[DomoWorkflow_Edge, DomoWorkflow_Edge]:
    """Add the required True/False branches for a conditional gateway."""
    self._sync_design_from_legacy_fields()
    assert self.Design is not None
    true_edge, false_edge = self.Design.add_boolean_branch_edges(
        gateway_id=gateway_id,
        true_target_id=true_target_id,
        false_target_id=false_target_id,
        true_label=true_label,
        false_label=false_label,
        condition_type=condition_type,
    )
    self._sync_legacy_fields_from_design()
    return true_edge, false_edge

add_edge

add_edge(
    source_id: str,
    target_id: str,
    title: str | None = None,
    condition_type: str | None = None,
) -> DomoWorkflow_Edge

Create and append an edge between two tile IDs.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def add_edge(
    self,
    source_id: str,
    target_id: str,
    title: str | None = None,
    condition_type: str | None = None,
) -> DomoWorkflow_Edge:
    """Create and append an edge between two tile IDs."""
    self._sync_design_from_legacy_fields()
    assert self.Design is not None
    edge = self.Design.add_edge(
        source_id=source_id,
        target_id=target_id,
        title=title,
        condition_type=condition_type,
    )
    self._sync_legacy_fields_from_design()
    return edge

add_input

add_input(param: DomoWorkflow_Parameter) -> None

Add a parameter to the schema inputs.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
256
257
258
def add_input(self, param: DomoWorkflow_Parameter) -> None:
    """Add a parameter to the schema inputs."""
    self.inputs[param.param_name] = param

add_output

add_output(param: DomoWorkflow_Parameter) -> None

Add a parameter to the schema outputs.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
260
261
262
def add_output(self, param: DomoWorkflow_Parameter) -> None:
    """Add a parameter to the schema outputs."""
    self.outputs[param.param_name] = param

add_tile

add_tile(
    tile: DomoWorkflow_Tile,
    x: float | None = None,
    y: float | None = None,
) -> DomoWorkflow_Tile

Append a tile, auto-positioning if x/y not given (stack vertically).

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
143
144
145
146
147
148
149
150
151
152
153
154
def add_tile(
    self,
    tile: DomoWorkflow_Tile,
    x: float | None = None,
    y: float | None = None,
) -> DomoWorkflow_Tile:
    """Append a tile, auto-positioning if x/y not given (stack vertically)."""
    self._sync_design_from_legacy_fields()
    assert self.Design is not None
    tile = self.Design.add_tile(tile=tile, x=x, y=y)
    self._sync_legacy_fields_from_design()
    return tile

empty classmethod

Create an empty definition suitable for a brand-new workflow.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
332
333
334
335
@classmethod
def empty(cls) -> DomoWorkflow_Definition:
    """Create an empty definition suitable for a brand-new workflow."""
    return cls()

from_saved_json classmethod

from_saved_json(
    saved_json_str: str,
) -> DomoWorkflow_Definition

Parse the savedJson string stored on a version object.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
78
79
80
81
82
@classmethod
def from_saved_json(cls, saved_json_str: str) -> DomoWorkflow_Definition:
    """Parse the savedJson string stored on a version object."""
    raw = json.loads(saved_json_str)
    return cls._from_raw(raw)

get_connection_validation_errors

get_connection_validation_errors(
    *,
    logic_tile_types: tuple[str, ...] = (
        "conditionalGatewayNode",
    ),
    allow_dangling_tile_types: tuple[str, ...] = (
        "endNode",
    ),
    required_logic_outputs: int = 2
) -> list[str]

Return graph-connection validation errors without raising.

Rules: - Only tiles in allow_dangling_tile_types may have zero outgoing edges. - Tiles in logic_tile_types must have exactly required_logic_outputs outgoing edges. - Edges must reference existing source/target tile IDs.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
def get_connection_validation_errors(
    self,
    *,
    logic_tile_types: tuple[str, ...] = ("conditionalGatewayNode",),
    allow_dangling_tile_types: tuple[str, ...] = ("endNode",),
    required_logic_outputs: int = 2,
) -> list[str]:
    """Return graph-connection validation errors without raising.

    Rules:
    - Only tiles in ``allow_dangling_tile_types`` may have zero outgoing edges.
    - Tiles in ``logic_tile_types`` must have exactly ``required_logic_outputs``
      outgoing edges.
    - Edges must reference existing source/target tile IDs.
    """
    self._sync_design_from_legacy_fields()
    assert self.Design is not None
    return self.Design.get_connection_validation_errors(
        logic_tile_types=logic_tile_types,
        allow_dangling_tile_types=allow_dangling_tile_types,
        required_logic_outputs=required_logic_outputs,
    )

set_guarded_codeengine_flow

set_guarded_codeengine_flow(
    *,
    package_id: str,
    package_version: str,
    success_functions: list[str],
    failure_notification_emails: list[str] | None = None,
    inputs_by_function: (
        dict[str, list[DomoWorkflow_Parameter]] | None
    ) = None,
    start_title: str = "Start",
    end_title: str = "End",
    notification_title: str = "Notify Owners",
    notification_message: str = "Workflow preflight failed",
    start_x: float = 80.0,
    start_y: float = 80.0,
    y_step: float = 150.0
) -> None

Build a guarded workflow graph via Design manager.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
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
def set_guarded_codeengine_flow(
    self,
    *,
    package_id: str,
    package_version: str,
    success_functions: list[str],
    failure_notification_emails: list[str] | None = None,
    inputs_by_function: dict[str, list[DomoWorkflow_Parameter]] | None = None,
    start_title: str = "Start",
    end_title: str = "End",
    notification_title: str = "Notify Owners",
    notification_message: str = "Workflow preflight failed",
    start_x: float = 80.0,
    start_y: float = 80.0,
    y_step: float = 150.0,
) -> None:
    """Build a guarded workflow graph via Design manager."""
    self._sync_design_from_legacy_fields()
    assert self.Design is not None
    self.Design.set_guarded_codeengine_flow(
        package_id=package_id,
        package_version=package_version,
        success_functions=success_functions,
        failure_notification_emails=failure_notification_emails,
        inputs_by_function=inputs_by_function,
        start_title=start_title,
        end_title=end_title,
        notification_title=notification_title,
        notification_message=notification_message,
        start_x=start_x,
        start_y=start_y,
        y_step=y_step,
    )
    self._sync_legacy_fields_from_design()

set_linear_codeengine_flow

set_linear_codeengine_flow(
    *,
    package_id: str,
    package_version: str,
    function_names: list[str],
    inputs_by_function: (
        dict[str, list[DomoWorkflow_Parameter]] | None
    ) = None,
    start_title: str = "Start",
    end_title: str = "End",
    start_x: float = 80.0,
    start_y: float = 80.0,
    y_step: float = 150.0
) -> None

Replace current graph with a linear CodeEngine flow via Design manager.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
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
def set_linear_codeengine_flow(
    self,
    *,
    package_id: str,
    package_version: str,
    function_names: list[str],
    inputs_by_function: dict[str, list[DomoWorkflow_Parameter]] | None = None,
    start_title: str = "Start",
    end_title: str = "End",
    start_x: float = 80.0,
    start_y: float = 80.0,
    y_step: float = 150.0,
) -> None:
    """Replace current graph with a linear CodeEngine flow via Design manager."""
    self._sync_design_from_legacy_fields()
    assert self.Design is not None
    self.Design.set_linear_codeengine_flow(
        package_id=package_id,
        package_version=package_version,
        function_names=function_names,
        inputs_by_function=inputs_by_function,
        start_title=start_title,
        end_title=end_title,
        start_x=start_x,
        start_y=start_y,
        y_step=y_step,
    )
    self._sync_legacy_fields_from_design()

to_saved_json

to_saved_json() -> str

Serialize back to the savedJson string expected by the API.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def to_saved_json(self) -> str:
    """Serialize back to the savedJson string expected by the API."""
    self._sync_design_from_legacy_fields()
    design_payload = (
        self.Design.to_dict()
        if self.Design is not None
        else {
            "version": self.schema_version,
            "dataList": self.data_list,
            "designElements": [t.to_dict() for t in self.tiles]
            + [e.to_dict() for e in self.edges],
        }
    )
    payload = {
        "schema": {
            "inputs": {k: v.to_dict() for k, v in self.inputs.items()},
            "outputs": {k: v.to_dict() for k, v in self.outputs.items()},
        },
        "version": design_payload["version"],
        "dataList": design_payload["dataList"],
        "designElements": design_payload["designElements"],
    }
    return json.dumps(payload)

validate_boolean_conditionals

validate_boolean_conditionals(
    required_labels: tuple[str, str] | None = None,
) -> None

Raise if any conditional gateway does not have a complete two-branch split.

If required_labels is provided, the gateway must have exactly those two branch labels. If it is omitted, the gateway only needs two distinct, non-empty condition edges.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
199
200
201
202
203
204
205
206
207
208
209
210
211
def validate_boolean_conditionals(
    self,
    required_labels: tuple[str, str] | None = None,
) -> None:
    """Raise if any conditional gateway does not have a complete two-branch split.

    If `required_labels` is provided, the gateway must have exactly those two
    branch labels. If it is omitted, the gateway only needs two distinct,
    non-empty condition edges.
    """
    self._sync_design_from_legacy_fields()
    assert self.Design is not None
    self.Design.validate_boolean_conditionals(required_labels=required_labels)

validate_connection_rules

validate_connection_rules(
    *,
    logic_tile_types: tuple[str, ...] = (
        "conditionalGatewayNode",
    ),
    allow_dangling_tile_types: tuple[str, ...] = (
        "endNode",
    ),
    required_logic_outputs: int = 2
) -> None

Raise when connection rules are violated.

This is a stricter topological check than validate_boolean_conditionals and is intended to answer whether the workflow graph is structurally valid.

Source code in src/crew_dcs/classes/DomoWorkflow/definition.py
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def validate_connection_rules(
    self,
    *,
    logic_tile_types: tuple[str, ...] = ("conditionalGatewayNode",),
    allow_dangling_tile_types: tuple[str, ...] = ("endNode",),
    required_logic_outputs: int = 2,
) -> None:
    """Raise when connection rules are violated.

    This is a stricter topological check than ``validate_boolean_conditionals``
    and is intended to answer whether the workflow graph is structurally valid.
    """
    self._sync_design_from_legacy_fields()
    assert self.Design is not None
    self.Design.validate_connection_rules(
        logic_tile_types=logic_tile_types,
        allow_dangling_tile_types=allow_dangling_tile_types,
        required_logic_outputs=required_logic_outputs,
    )

DomoWorkflow_DeployedVersionError

DomoWorkflow_DeployedVersionError(
    model_id: str,
    version: str,
    deployed_on: datetime | None = None,
    active: bool | None = None,
    cls_instance: Any | None = None,
)

Bases: ClassError

Raised when attempting to update a deployed workflow version in place.

Build the error message for a deployed, non-updatable version.

Parameters:

Name Type Description Default
model_id str

Workflow model UUID.

required
version str

Semver string of the deployed version.

required
deployed_on datetime | None

When the version was deployed, if known.

None
active bool | None

Whether the version is currently active.

None
cls_instance Any | None

Optional entity that raised the error, for context.

None
Source code in src/crew_dcs/classes/DomoWorkflow/core.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def __init__(
    self,
    model_id: str,
    version: str,
    deployed_on: dt.datetime | None = None,
    active: bool | None = None,
    cls_instance: Any | None = None,
) -> None:
    """Build the error message for a deployed, non-updatable version.

    Args:
        model_id: Workflow model UUID.
        version: Semver string of the deployed version.
        deployed_on: When the version was deployed, if known.
        active: Whether the version is currently active.
        cls_instance: Optional entity that raised the error, for context.
    """
    deployed_txt = deployed_on.isoformat() if deployed_on else "unknown"
    active_txt = "true" if active else "false"
    super().__init__(
        cls_instance=cls_instance,
        entity_id=model_id,
        message=(
            f"Workflow version '{version}' is deployed (active={active_txt}, "
            f"deployedOn={deployed_txt}) and cannot be updated in place. "
            "Create a new version (for example, 1.0.1) and publish that version instead."
        ),
    )

DomoWorkflow_Design dataclass

DomoWorkflow_Design(
    schema_version: int = 2,
    tiles: list[DomoWorkflow_Tile] = list(),
    edges: list[DomoWorkflow_Edge] = list(),
    layouts: list[dict[str, Any]] = list(),
    inputs: dict[str, DomoWorkflow_Parameter] = dict(),
    outputs: dict[str, DomoWorkflow_Parameter] = dict(),
    raw: dict = dict(),
)

Workflow design manager for graph elements and topology validation.

This mirrors the sub-entity composition style used by other entities: design details live here while higher-level model/definition concerns remain in the parent class.

add_boolean_branch_edges

add_boolean_branch_edges(
    gateway_id: str,
    true_target_id: str,
    false_target_id: str,
    *,
    true_label: str = "True",
    false_label: str = "False",
    condition_type: str = "Basic"
) -> tuple[DomoWorkflow_Edge, DomoWorkflow_Edge]

Add the required True/False branches for a conditional gateway.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
def add_boolean_branch_edges(
    self,
    gateway_id: str,
    true_target_id: str,
    false_target_id: str,
    *,
    true_label: str = "True",
    false_label: str = "False",
    condition_type: str = "Basic",
) -> tuple[DomoWorkflow_Edge, DomoWorkflow_Edge]:
    """Add the required True/False branches for a conditional gateway."""
    true_edge = self.add_edge(
        gateway_id,
        true_target_id,
        title=true_label,
        condition_type=condition_type,
    )
    false_edge = self.add_edge(
        gateway_id,
        false_target_id,
        title=false_label,
        condition_type=condition_type,
    )
    return true_edge, false_edge

add_edge

add_edge(
    source_id: str,
    target_id: str,
    title: str | None = None,
    condition_type: str | None = None,
) -> DomoWorkflow_Edge

Create and append an edge between two tile IDs.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def add_edge(
    self,
    source_id: str,
    target_id: str,
    title: str | None = None,
    condition_type: str | None = None,
) -> DomoWorkflow_Edge:
    """Create and append an edge between two tile IDs."""
    edge = DomoWorkflow_Edge.create(
        source_id=source_id,
        target_id=target_id,
        title=title,
        condition_type=condition_type,
    )
    self.edges.append(edge)
    return edge

add_input

add_input(param: DomoWorkflow_Parameter) -> None

Add a parameter to the workflow-level schema inputs.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
72
73
74
def add_input(self, param: DomoWorkflow_Parameter) -> None:
    """Add a parameter to the workflow-level schema inputs."""
    self.inputs[param.param_name] = param

add_output

add_output(param: DomoWorkflow_Parameter) -> None

Add a parameter to the workflow-level schema outputs.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
76
77
78
def add_output(self, param: DomoWorkflow_Parameter) -> None:
    """Add a parameter to the workflow-level schema outputs."""
    self.outputs[param.param_name] = param

add_tile

add_tile(
    tile: DomoWorkflow_Tile,
    x: float | None = None,
    y: float | None = None,
) -> DomoWorkflow_Tile

Append a tile, auto-positioning if x/y not given (stack vertically).

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def add_tile(
    self,
    tile: DomoWorkflow_Tile,
    x: float | None = None,
    y: float | None = None,
) -> DomoWorkflow_Tile:
    """Append a tile, auto-positioning if x/y not given (stack vertically)."""
    if x is not None:
        tile.x = x
    if y is not None:
        tile.y = y
    elif x is None and y is None:
        if self.tiles:
            last = self.tiles[-1]
            tile.x = last.x
            tile.y = last.y + 150
        else:
            tile.x = 0.0
            tile.y = 0.0
    tile.index = len(self.tiles)
    self.tiles.append(tile)
    return tile

empty classmethod

Create an empty design suitable for a brand-new workflow.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
640
641
642
643
@classmethod
def empty(cls) -> DomoWorkflow_Design:
    """Create an empty design suitable for a brand-new workflow."""
    return cls()

ensure_edge

ensure_edge(
    source_id: str,
    target_id: str,
    title: str | None = None,
    condition_type: str | None = None,
) -> DomoWorkflow_Edge | None

Idempotently add an edge; no-op if an equivalent edge already exists.

Returns the created edge, or None if nothing needed to change.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def ensure_edge(
    self,
    source_id: str,
    target_id: str,
    title: str | None = None,
    condition_type: str | None = None,
) -> DomoWorkflow_Edge | None:
    """Idempotently add an edge; no-op if an equivalent edge already exists.

    Returns the created edge, or None if nothing needed to change.
    """
    if self.has_edge(source_id, target_id, label=title):
        return None
    return self.add_edge(
        source_id, target_id, title=title, condition_type=condition_type
    )

find_tile

find_tile(
    *, title: str, tile_type: str | None = None
) -> DomoWorkflow_Tile

Find a single tile by title (and optionally tile_type).

Raises ValueError unless exactly one tile matches — use this instead of hand-rolled list comprehensions when writing targeted graph repairs.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
def find_tile(
    self,
    *,
    title: str,
    tile_type: str | None = None,
) -> DomoWorkflow_Tile:
    """Find a single tile by title (and optionally tile_type).

    Raises ValueError unless exactly one tile matches — use this instead of
    hand-rolled list comprehensions when writing targeted graph repairs.
    """
    matches = [
        tile
        for tile in self.tiles
        if tile.title == title
        and (tile_type is None or tile.tile_type == tile_type)
    ]
    if len(matches) != 1:
        raise ValueError(
            f"expected exactly 1 tile for title={title!r} type={tile_type!r}, "
            f"found {len(matches)}"
        )
    return matches[0]

from_raw classmethod

from_raw(raw: dict[str, Any]) -> DomoWorkflow_Design

Build a design object from workflow savedJson payload.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
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
@classmethod
def from_raw(cls, raw: dict[str, Any]) -> DomoWorkflow_Design:
    """Build a design object from workflow savedJson payload."""
    edge_types = {"defaultEdge", "conditionEdge"}
    tiles: list[DomoWorkflow_Tile] = []
    edges: list[DomoWorkflow_Edge] = []
    tile_index = 0

    for element in raw.get("designElements") or []:
        elem_type = element.get("type", "")
        if elem_type in edge_types:
            edges.append(DomoWorkflow_Edge.from_dict(element))
        else:
            tiles.append(DomoWorkflow_Tile.from_dict(element, index=tile_index))
            tile_index += 1

    schema = raw.get("schema") or {}
    inputs = {
        k: DomoWorkflow_Parameter.from_dict(v)
        for k, v in (schema.get("inputs") or {}).items()
    }
    outputs = {
        k: DomoWorkflow_Parameter.from_dict(v)
        for k, v in (schema.get("outputs") or {}).items()
    }

    return cls(
        schema_version=raw.get("version", 2),
        tiles=tiles,
        edges=edges,
        layouts=raw.get("dataList") or [],
        inputs=inputs,
        outputs=outputs,
        raw=raw,
    )

get_connection_validation_errors

get_connection_validation_errors(
    *,
    logic_tile_types: tuple[str, ...] = (
        "conditionalGatewayNode",
    ),
    allow_dangling_tile_types: tuple[str, ...] = (
        "endNode",
    ),
    required_logic_outputs: int = 2
) -> list[str]

Return graph-connection validation errors without raising.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
def get_connection_validation_errors(
    self,
    *,
    logic_tile_types: tuple[str, ...] = ("conditionalGatewayNode",),
    allow_dangling_tile_types: tuple[str, ...] = ("endNode",),
    required_logic_outputs: int = 2,
) -> list[str]:
    """Return graph-connection validation errors without raising."""
    tile_by_id = {tile.id: tile for tile in self.tiles}
    outgoing_by_source: dict[str, list[DomoWorkflow_Edge]] = {
        tile.id: [] for tile in self.tiles
    }

    errors: list[str] = []
    for edge in self.edges:
        if edge.source not in tile_by_id:
            errors.append(
                f"edge '{edge.id}' source '{edge.source}' does not reference an existing tile"
            )
            continue
        if edge.target not in tile_by_id:
            errors.append(
                f"edge '{edge.id}' target '{edge.target}' does not reference an existing tile"
            )
            continue
        outgoing_by_source[edge.source].append(edge)

    for tile in self.tiles:
        outgoing = outgoing_by_source.get(tile.id, [])
        tile_label = tile.title or tile.id

        if tile.tile_type not in allow_dangling_tile_types and len(outgoing) == 0:
            errors.append(
                f"tile '{tile_label}' ({tile.tile_type}) has no outgoing connection"
            )

        if (
            tile.tile_type in logic_tile_types
            and len(outgoing) != required_logic_outputs
        ):
            errors.append(
                f"logic tile '{tile_label}' ({tile.tile_type}) must have exactly "
                f"{required_logic_outputs} outgoing connections (found {len(outgoing)})"
            )

    return errors

has_edge

has_edge(
    source_id: str,
    target_id: str,
    *,
    label: str | None = None
) -> bool

Return True if an edge from source_id to target_id already exists.

When label is given, also requires the edge's title to match.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
def has_edge(
    self,
    source_id: str,
    target_id: str,
    *,
    label: str | None = None,
) -> bool:
    """Return True if an edge from source_id to target_id already exists.

    When label is given, also requires the edge's title to match.
    """
    return any(
        edge.source == source_id
        and edge.target == target_id
        and (label is None or edge.title == label)
        for edge in self.edges
    )

prune_orphan_tiles

prune_orphan_tiles(
    *, tile_types: tuple[str, ...] = ("EMPTY",)
) -> list[str]

Remove tiles of the given type(s) that have no incoming or outgoing edges.

Returns the ids of removed tiles (empty list if nothing was pruned).

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
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
def prune_orphan_tiles(
    self,
    *,
    tile_types: tuple[str, ...] = ("EMPTY",),
) -> list[str]:
    """Remove tiles of the given type(s) that have no incoming or outgoing edges.

    Returns the ids of removed tiles (empty list if nothing was pruned).
    """
    connected_ids = {edge.target for edge in self.edges} | {
        edge.source for edge in self.edges
    }

    keep: list[DomoWorkflow_Tile] = []
    removed: list[str] = []
    for tile in self.tiles:
        if tile.tile_type in tile_types and tile.id not in connected_ids:
            removed.append(tile.id)
            continue
        keep.append(tile)

    if removed:
        self.tiles = keep
        for idx, tile in enumerate(self.tiles):
            tile.index = idx

    return removed

rewire_gateway_branches

rewire_gateway_branches(
    gateway_id: str,
    true_target_id: str,
    false_target_id: str,
    *,
    true_label: str = "True",
    false_label: str = "False",
    condition_type: str = "Basic"
) -> tuple[DomoWorkflow_Edge, DomoWorkflow_Edge]

Replace ALL outgoing edges of a gateway with a fresh True/False pair.

Idempotent to call repeatedly — safe whether the gateway currently has no branches, partial/incorrect branches, or already-correct branches.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def rewire_gateway_branches(
    self,
    gateway_id: str,
    true_target_id: str,
    false_target_id: str,
    *,
    true_label: str = "True",
    false_label: str = "False",
    condition_type: str = "Basic",
) -> tuple[DomoWorkflow_Edge, DomoWorkflow_Edge]:
    """Replace ALL outgoing edges of a gateway with a fresh True/False pair.

    Idempotent to call repeatedly — safe whether the gateway currently has
    no branches, partial/incorrect branches, or already-correct branches.
    """
    self.edges = [edge for edge in self.edges if edge.source != gateway_id]
    return self.add_boolean_branch_edges(
        gateway_id,
        true_target_id,
        false_target_id,
        true_label=true_label,
        false_label=false_label,
        condition_type=condition_type,
    )

set_guarded_codeengine_flow

set_guarded_codeengine_flow(
    *,
    package_id: str,
    package_version: str,
    success_functions: list[str],
    failure_notification_emails: list[str] | None = None,
    inputs_by_function: (
        dict[str, list[DomoWorkflow_Parameter]] | None
    ) = None,
    start_title: str = "Start",
    end_title: str = "End",
    notification_title: str = "Notify Owners",
    notification_message: str = "Workflow preflight failed",
    start_x: float = 80.0,
    start_y: float = 80.0,
    y_step: float = 150.0
) -> None

Build a preflight-gated workflow with an email notification branch.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
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
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
def set_guarded_codeengine_flow(
    self,
    *,
    package_id: str,
    package_version: str,
    success_functions: list[str],
    failure_notification_emails: list[str] | None = None,
    inputs_by_function: dict[str, list[DomoWorkflow_Parameter]] | None = None,
    start_title: str = "Start",
    end_title: str = "End",
    notification_title: str = "Notify Owners",
    notification_message: str = "Workflow preflight failed",
    start_x: float = 80.0,
    start_y: float = 80.0,
    y_step: float = 150.0,
) -> None:
    """Build a preflight-gated workflow with an email notification branch."""
    from .tile.code_engine import DomoWorkflow_Tile_CodeEngine
    from .tile.conditional import DomoWorkflow_Tile_Conditional
    from .tile.end import DomoWorkflow_Tile_End
    from .tile.misc import DomoWorkflow_Tile_UserTask
    from .tile.start import DomoWorkflow_Tile_Start

    if not success_functions:
        raise ValueError("success_functions must contain at least one function")

    inputs_by_function = inputs_by_function or {}
    failure_notification_emails = failure_notification_emails or []

    start_tile = DomoWorkflow_Tile_Start(
        id="start",
        tile_type="rootNode",
        index=0,
        x=start_x,
        y=start_y,
        title=start_title,
    )

    tiles: list[DomoWorkflow_Tile] = [start_tile]
    current_y = start_y + y_step

    preflight_github = DomoWorkflow_Tile_CodeEngine.create(
        title="get_github_pat",
        package_id=package_id,
        function_name="get_github_pat",
        version=package_version,
        tile_id=str(uuid.uuid4()),
        x=start_x,
        y=current_y,
        inputs=inputs_by_function.get("get_github_pat", []),
    )
    tiles.append(preflight_github)
    current_y += y_step

    github_gateway = DomoWorkflow_Tile_Conditional(
        id=str(uuid.uuid4()),
        tile_type="conditionalGatewayNode",
        index=len(tiles),
        x=start_x,
        y=current_y,
        title="GitHub PAT ready",
        inclusive=False,
    )
    tiles.append(github_gateway)
    current_y += y_step

    preflight_domo = DomoWorkflow_Tile_CodeEngine.create(
        title="get_domo_token",
        package_id=package_id,
        function_name="get_domo_token",
        version=package_version,
        tile_id=str(uuid.uuid4()),
        x=start_x,
        y=current_y,
        inputs=inputs_by_function.get("get_domo_token", []),
    )
    tiles.append(preflight_domo)
    current_y += y_step

    domo_gateway = DomoWorkflow_Tile_Conditional(
        id=str(uuid.uuid4()),
        tile_type="conditionalGatewayNode",
        index=len(tiles),
        x=start_x,
        y=current_y,
        title="Domo token ready",
        inclusive=False,
    )
    tiles.append(domo_gateway)
    current_y += y_step

    success_tiles: list[DomoWorkflow_Tile_CodeEngine] = []
    for fn_name in success_functions:
        tile = DomoWorkflow_Tile_CodeEngine.create(
            title=fn_name,
            package_id=package_id,
            function_name=fn_name,
            version=package_version,
            tile_id=str(uuid.uuid4()),
            x=start_x,
            y=current_y,
            inputs=inputs_by_function.get(fn_name, []),
        )
        success_tiles.append(tile)
        tiles.append(tile)
        current_y += y_step

    notification_tile = DomoWorkflow_Tile_UserTask(
        id=str(uuid.uuid4()),
        tile_type="userTaskNode",
        index=len(tiles),
        x=start_x,
        y=current_y,
        title=notification_title,
        config_type="email",
        email={
            "emailAddresses": failure_notification_emails,
            "subject": "Domo fileset workflow needs attention",
            "body": [
                {"type": "paragraph", "children": [{"text": notification_message}]}
            ],
        },
    )
    tiles.append(notification_tile)
    current_y += y_step

    end_tile = DomoWorkflow_Tile_End(
        id="end",
        tile_type="endNode",
        index=len(tiles),
        x=start_x,
        y=current_y,
        title=end_title,
    )
    tiles.append(end_tile)

    self.tiles = tiles
    self.edges = []

    for idx, tile in enumerate(self.tiles):
        tile.index = idx

    self.add_edge("start", preflight_github.id)
    self.add_edge(preflight_github.id, github_gateway.id)
    self.add_boolean_branch_edges(
        github_gateway.id,
        preflight_domo.id,
        notification_tile.id,
    )
    self.add_edge(preflight_domo.id, domo_gateway.id)
    self.add_boolean_branch_edges(
        domo_gateway.id,
        success_tiles[0].id,
        notification_tile.id,
    )

    prev_id = success_tiles[0].id
    for tile in success_tiles[1:]:
        self.add_edge(prev_id, tile.id)
        prev_id = tile.id

    self.add_edge(prev_id, end_tile.id)
    self.add_edge(notification_tile.id, end_tile.id)

    self.validate_boolean_conditionals(required_labels=("True", "False"))

set_linear_codeengine_flow

set_linear_codeengine_flow(
    *,
    package_id: str,
    package_version: str,
    function_names: list[str],
    inputs_by_function: (
        dict[str, list[DomoWorkflow_Parameter]] | None
    ) = None,
    start_title: str = "Start",
    end_title: str = "End",
    start_x: float = 80.0,
    start_y: float = 80.0,
    y_step: float = 150.0
) -> None

Replace current tiles/edges with a start -> CodeEngine chain -> end.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
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
def set_linear_codeengine_flow(
    self,
    *,
    package_id: str,
    package_version: str,
    function_names: list[str],
    inputs_by_function: dict[str, list[DomoWorkflow_Parameter]] | None = None,
    start_title: str = "Start",
    end_title: str = "End",
    start_x: float = 80.0,
    start_y: float = 80.0,
    y_step: float = 150.0,
) -> None:
    """Replace current tiles/edges with a start -> CodeEngine chain -> end."""
    from .tile.code_engine import DomoWorkflow_Tile_CodeEngine
    from .tile.end import DomoWorkflow_Tile_End
    from .tile.start import DomoWorkflow_Tile_Start

    if not function_names:
        raise ValueError("function_names must contain at least one function")

    inputs_by_function = inputs_by_function or {}

    start_id = "start"
    end_id = "end"

    start_tile = DomoWorkflow_Tile_Start(
        id=start_id,
        tile_type="rootNode",
        index=0,
        x=start_x,
        y=start_y,
        title=start_title,
    )

    tiles = [start_tile]
    curr_y = start_y + y_step

    for fn_name in function_names:
        tile_id = str(uuid.uuid4())
        tile = DomoWorkflow_Tile_CodeEngine.create(
            title=fn_name,
            package_id=package_id,
            function_name=fn_name,
            version=package_version,
            tile_id=tile_id,
            x=start_x,
            y=curr_y,
            inputs=inputs_by_function.get(fn_name, []),
        )
        tiles.append(tile)
        curr_y += y_step

    end_tile = DomoWorkflow_Tile_End(
        id=end_id,
        tile_type="endNode",
        index=len(tiles),
        x=start_x,
        y=curr_y,
        title=end_title,
    )
    tiles.append(end_tile)

    self.tiles = tiles
    self.edges = []

    for idx, tile in enumerate(self.tiles):
        tile.index = idx

    prev_id = start_id
    for tile in self.tiles[1:]:
        self.add_edge(prev_id, tile.id)
        prev_id = tile.id

to_dict

to_dict() -> dict[str, Any]

Serialize the design payload expected by Domo APIs.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def to_dict(self) -> dict[str, Any]:
    """Serialize the design payload expected by Domo APIs."""
    design_elements = [t.to_dict() for t in self.tiles] + [
        e.to_dict() for e in self.edges
    ]
    return {
        "schema": {
            "inputs": {k: v.to_dict() for k, v in self.inputs.items()},
            "outputs": {k: v.to_dict() for k, v in self.outputs.items()},
        },
        "version": self.schema_version,
        "dataList": self.layouts,
        "designElements": design_elements,
    }

validate_boolean_conditionals

validate_boolean_conditionals(
    required_labels: tuple[str, str] | None = None,
) -> None

Raise if any conditional gateway does not have a complete two-branch split.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
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
def validate_boolean_conditionals(
    self,
    required_labels: tuple[str, str] | None = None,
) -> None:
    """Raise if any conditional gateway does not have a complete two-branch split."""
    edges_by_source: dict[str, list[DomoWorkflow_Edge]] = {}
    for edge in self.edges:
        edges_by_source.setdefault(edge.source, []).append(edge)

    errors: list[str] = []
    for tile in self.tiles:
        if tile.tile_type != "conditionalGatewayNode":
            continue

        branch_edges = [
            edge
            for edge in edges_by_source.get(tile.id, [])
            if edge.edge_type == "conditionEdge"
        ]
        labels = {edge.title for edge in branch_edges}
        if required_labels is None:
            label_ok = (
                len(branch_edges) == 2
                and len(labels) == 2
                and None not in labels
                and "" not in labels
            )
        else:
            label_ok = len(branch_edges) == 2 and labels == set(required_labels)

        if not label_ok:
            errors.append(
                f"conditional gateway '{tile.title or tile.id}' must have exactly two labeled branches"
            )

        if any(not edge.target for edge in branch_edges):
            errors.append(
                f"conditional gateway '{tile.title or tile.id}' has an unconnected branch"
            )

    if errors:
        raise ValueError("; ".join(errors))

validate_connection_rules

validate_connection_rules(
    *,
    logic_tile_types: tuple[str, ...] = (
        "conditionalGatewayNode",
    ),
    allow_dangling_tile_types: tuple[str, ...] = (
        "endNode",
    ),
    required_logic_outputs: int = 2
) -> None

Raise when connection rules are violated.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
def validate_connection_rules(
    self,
    *,
    logic_tile_types: tuple[str, ...] = ("conditionalGatewayNode",),
    allow_dangling_tile_types: tuple[str, ...] = ("endNode",),
    required_logic_outputs: int = 2,
) -> None:
    """Raise when connection rules are violated."""
    errors = self.get_connection_validation_errors(
        logic_tile_types=logic_tile_types,
        allow_dangling_tile_types=allow_dangling_tile_types,
        required_logic_outputs=required_logic_outputs,
    )
    if errors:
        raise ValueError("; ".join(errors))

validate_with_domo async

validate_with_domo(
    *,
    auth: DomoAuth,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    context: RouteContext | None = None,
    **context_kwargs
) -> ResponseGetData

Validate this design with Domo's official workflow validator endpoint.

Source code in src/crew_dcs/classes/DomoWorkflow/design.py
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
async def validate_with_domo(
    self,
    *,
    auth: DomoAuth,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    context: RouteContext | None = None,
    **context_kwargs,
) -> rgd.ResponseGetData:
    """Validate this design with Domo's official workflow validator endpoint."""
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )
    return await workflow_routes.validate_workflow_model(
        auth=auth,
        body=self.to_dict(),
        context=context,
    )

DomoWorkflow_Edge dataclass

DomoWorkflow_Edge(
    id: str,
    source: str,
    target: str,
    edge_type: str = "defaultEdge",
    path: list | None = None,
    title: str | None = None,
    condition_type: str | None = None,
    position: dict[str, float] | None = None,
    source_position: str | None = None,
    target_position: str | None = None,
    data: dict = dict(),
    raw: dict = dict(),
)

Represents a workflow edge (defaultEdge or conditionEdge).

create classmethod

create(
    source_id: str,
    target_id: str,
    title: str | None = None,
    edge_type: str = "defaultEdge",
    condition_type: str | None = None,
    edge_id: str | None = None,
) -> DomoWorkflow_Edge

Build an edge programmatically.

Source code in src/crew_dcs/classes/DomoWorkflow/tile/edge.py
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
@classmethod
def create(
    cls,
    source_id: str,
    target_id: str,
    title: str | None = None,
    edge_type: str = "defaultEdge",
    condition_type: str | None = None,
    edge_id: str | None = None,
) -> DomoWorkflow_Edge:
    """Build an edge programmatically."""
    return cls(
        id=edge_id or str(uuid.uuid4()),
        source=source_id,
        target=target_id,
        edge_type="conditionEdge" if title else edge_type,
        title=title,
        condition_type=condition_type,
        position={"x": 0.0, "y": 0.0} if title else None,
        raw={},
    )

DomoWorkflow_Parameter dataclass

DomoWorkflow_Parameter(
    id: str | None,
    param_name: str,
    display_name: str,
    data_type: str,
    is_list: bool = False,
    required: bool = True,
    nullable: bool = False,
    value: Any = None,
    flag: str | None = None,
    visible: bool = True,
    children: list = list(),
    ai_description: str | None = None,
    config_type: str | None = None,
    custom_mapping_type: str | None = None,
    entity_sub_type: str | None = None,
    mapped_to: str | None = None,
    default_values: Any = None,
    raw: dict = dict(),
)

Represents a single input or output parameter on a workflow or tile.

create classmethod

create(
    param_name: str,
    display_name: str | None = None,
    data_type: str = "text",
    required: bool = True,
    is_list: bool = False,
    value: Any = None,
    flag: str | None = None,
) -> DomoWorkflow_Parameter

Ergonomic factory for building parameters programmatically.

Source code in src/crew_dcs/classes/DomoWorkflow/parameter.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@classmethod
def create(
    cls,
    param_name: str,
    display_name: str | None = None,
    data_type: str = "text",
    required: bool = True,
    is_list: bool = False,
    value: Any = None,
    flag: str | None = None,
) -> DomoWorkflow_Parameter:
    """Ergonomic factory for building parameters programmatically."""
    return cls(
        id=None,
        param_name=param_name,
        display_name=display_name or param_name,
        data_type=data_type,
        is_list=is_list,
        required=required,
        value=value,
        flag=flag,
        raw={},
    )

DomoWorkflow_Tile dataclass

DomoWorkflow_Tile(
    id: str,
    tile_type: str,
    index: int,
    x: float = 0.0,
    y: float = 0.0,
    title: str | None = None,
    dimensions: dict | None = None,
    data: dict = dict(),
    raw: dict = dict(),
)

Base class for all workflow designElement tiles.

from_dict classmethod

from_dict(
    obj: dict[str, Any], index: int = 0
) -> DomoWorkflow_Tile

Dispatch to the registered subclass by type field.

Source code in src/crew_dcs/classes/DomoWorkflow/tile/base.py
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
@classmethod
def from_dict(cls, obj: dict[str, Any], index: int = 0) -> DomoWorkflow_Tile:
    """Dispatch to the registered subclass by `type` field."""
    tile_type = obj.get("type", "EMPTY")

    if cls is DomoWorkflow_Tile:
        target_cls = _TILE_TYPE_REGISTRY.get(tile_type, DomoWorkflow_Tile_Unknown)
        return target_cls.from_dict(obj, index=index)

    data = obj.get("data") or {}
    position = obj.get("position") or {}

    instance = cls(
        id=obj.get("id", ""),
        tile_type=tile_type,
        index=index,
        x=position.get("x", 0.0),
        y=position.get("y", 0.0),
        title=data.get("title"),
        dimensions=data.get("dimensions"),
        data=data,
        raw=obj,
    )
    instance._extract_fields(data)
    return instance

to_dict

to_dict() -> dict[str, Any]

Serialize back to the designElement wire shape.

Source code in src/crew_dcs/classes/DomoWorkflow/tile/base.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def to_dict(self) -> dict[str, Any]:
    """Serialize back to the designElement wire shape."""
    data = dict(self.data)
    data["title"] = self.title
    data.pop("position", None)  # position lives at top-level, not inside data
    data["dimensions"] = self.dimensions or {"width": 200, "height": 60}
    out: dict[str, Any] = {
        "id": self.id,
        "type": self.tile_type,
        "position": {"x": self.x, "y": self.y},
        "data": data,
    }
    # Preserve cosmetic top-level fields from the original wire payload
    for key in ("style", "index"):
        if key in self.raw:
            out[key] = self.raw[key]
    return out

DomoWorkflow_Tile_CodeEngine dataclass

DomoWorkflow_Tile_CodeEngine(
    id: str,
    tile_type: str,
    index: int,
    x: float = 0.0,
    y: float = 0.0,
    title: str | None = None,
    dimensions: dict | None = None,
    data: dict = dict(),
    raw: dict = dict(),
    task_type: str | None = None,
    metadata: dict = dict(),
    inputs: list[DomoWorkflow_Parameter] = list(),
    outputs: list[DomoWorkflow_Parameter] = list(),
    uses_structured_outputs: bool = False,
    selected_task_title: str | None = None,
    selected_task_description: str | None = None,
    package_id: str | None = None,
    function_name: str | None = None,
    package_version: str | None = None,
    settings: dict = dict(),
)

Bases: DomoWorkflow_Tile_ServiceTask

serviceTaskNode with taskType='nebulaFunction' — a CodeEngine action tile.

This is the primary tile type for programmatic workflow construction. Use the create() classmethod for the cleanest ergonomics.

create classmethod

create(
    title: str,
    package_id: str,
    function_name: str,
    version: str,
    inputs: list[DomoWorkflow_Parameter] | None = None,
    outputs: list[DomoWorkflow_Parameter] | None = None,
    settings: dict | None = None,
    tile_id: str | None = None,
    x: float = 0.0,
    y: float = 0.0,
) -> DomoWorkflow_Tile_CodeEngine

Build a CodeEngine tile programmatically — no raw dict needed.

Parameters:

Name Type Description Default
title str

Display name shown on the tile in the Domo UI.

required
package_id str

CodeEngine package identifier.

required
function_name str

Name of the function to invoke.

required
version str

Package version string (e.g. "1.0.0").

required
inputs list[DomoWorkflow_Parameter] | None

Parameter list for the tile's inputs.

None
outputs list[DomoWorkflow_Parameter] | None

Parameter list for the tile's outputs.

None
settings dict | None

Extra metadata settings dict (optional).

None
tile_id str | None

Override the auto-generated UUID for the tile id.

None
x float

Canvas x position.

0.0
y float

Canvas y position.

0.0
Source code in src/crew_dcs/classes/DomoWorkflow/tile/code_engine.py
 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
@classmethod
def create(
    cls,
    title: str,
    package_id: str,
    function_name: str,
    version: str,
    inputs: list[DomoWorkflow_Parameter] | None = None,
    outputs: list[DomoWorkflow_Parameter] | None = None,
    settings: dict | None = None,
    tile_id: str | None = None,
    x: float = 0.0,
    y: float = 0.0,
) -> DomoWorkflow_Tile_CodeEngine:
    """Build a CodeEngine tile programmatically — no raw dict needed.

    Args:
        title: Display name shown on the tile in the Domo UI.
        package_id: CodeEngine package identifier.
        function_name: Name of the function to invoke.
        version: Package version string (e.g. "1.0.0").
        inputs: Parameter list for the tile's inputs.
        outputs: Parameter list for the tile's outputs.
        settings: Extra metadata settings dict (optional).
        tile_id: Override the auto-generated UUID for the tile id.
        x: Canvas x position.
        y: Canvas y position.
    """
    metadata = {
        "packageId": package_id,
        "functionName": function_name,
        "version": version,
        "settings": settings or {},
    }
    data = {
        "title": title,
        "taskType": "nebulaFunction",
        "metadata": metadata,
        "input": [p.to_dict() for p in (inputs or [])],
        "output": [p.to_dict() for p in (outputs or [])],
        "usesStructuredOutputs": False,
        "position": {"x": x, "y": y},
    }
    return cls(
        id=tile_id or str(uuid.uuid4()),
        tile_type="serviceTaskNode",
        index=0,
        x=x,
        y=y,
        title=title,
        data=data,
        raw={},
        task_type="nebulaFunction",
        metadata=metadata,
        inputs=inputs or [],
        outputs=outputs or [],
        package_id=package_id,
        function_name=function_name,
        package_version=version,
        settings=settings or {},
    )

DomoWorkflow_Tile_Conditional dataclass

DomoWorkflow_Tile_Conditional(
    id: str,
    tile_type: str,
    index: int,
    x: float = 0.0,
    y: float = 0.0,
    title: str | None = None,
    dimensions: dict | None = None,
    data: dict = dict(),
    raw: dict = dict(),
    inclusive: bool = False,
)

Bases: DomoWorkflow_Tile

Branch gateway tile (conditionalGatewayNode).

DomoWorkflow_Tile_End dataclass

DomoWorkflow_Tile_End(
    id: str,
    tile_type: str,
    index: int,
    x: float = 0.0,
    y: float = 0.0,
    title: str | None = None,
    dimensions: dict | None = None,
    data: dict = dict(),
    raw: dict = dict(),
    terminating: bool = False,
)

Bases: DomoWorkflow_Tile

The end (endNode) tile.

DomoWorkflow_Tile_ServiceTask dataclass

DomoWorkflow_Tile_ServiceTask(
    id: str,
    tile_type: str,
    index: int,
    x: float = 0.0,
    y: float = 0.0,
    title: str | None = None,
    dimensions: dict | None = None,
    data: dict = dict(),
    raw: dict = dict(),
    task_type: str | None = None,
    metadata: dict = dict(),
    inputs: list[DomoWorkflow_Parameter] = list(),
    outputs: list[DomoWorkflow_Parameter] = list(),
    uses_structured_outputs: bool = False,
    selected_task_title: str | None = None,
    selected_task_description: str | None = None,
)

Bases: DomoWorkflow_Tile

Generic action tile (serviceTaskNode).

When task_type == 'nebulaFunction' the registry will dispatch to DomoWorkflow_Tile_CodeEngine instead; this class handles all other task types (None, 'artificialIntelligence', …).

DomoWorkflow_Tile_Start dataclass

DomoWorkflow_Tile_Start(
    id: str,
    tile_type: str,
    index: int,
    x: float = 0.0,
    y: float = 0.0,
    title: str | None = None,
    dimensions: dict | None = None,
    data: dict = dict(),
    raw: dict = dict(),
    is_form_start: bool = False,
    form_id: str | None = None,
    inputs: list[DomoWorkflow_Parameter] = list(),
)

Bases: DomoWorkflow_Tile

The start (rootNode) tile — one per workflow.

DomoWorkflow_Tile_Unknown dataclass

DomoWorkflow_Tile_Unknown(
    id: str,
    tile_type: str,
    index: int,
    x: float = 0.0,
    y: float = 0.0,
    title: str | None = None,
    dimensions: dict | None = None,
    data: dict = dict(),
    raw: dict = dict(),
)

Bases: DomoWorkflow_Tile

Fallback for tile types not yet registered.

DomoWorkflow_Trigger_Timer dataclass

DomoWorkflow_Trigger_Timer(
    id: str,
    auth: DomoAuth,
    name: str,
    model_id: str,
    model_version: str,
    cron: str,
    time_zone: str = "UTC",
    entity_id: str | None = None,
    active: bool = True,
    created_by: str | None = None,
    created_dt: datetime | None = None,
    updated_by: str | None = None,
    updated_dt: datetime | None = None,
    deleted_dt: datetime | None = None,
    raw: dict = dict(),
)

A TIMER trigger that fires a workflow model/version on a cron schedule.

Timer triggers are NOT part of a workflow's canvas (designElements/savedJson, the thing DomoWorkflow_Tile subclasses represent) — they are a separate resource created via POST /api/workflow/v2/triggers, scoped to the creating user (entityType="USER", entityId=; Domo rejects any other entityType/entityId combo for TIMER triggers with "Timer triggers can only be created for the current user"). Reverse-engineered against domo-community since no shipped workflow there used a non-manual trigger.

create async classmethod

create(
    auth: DomoAuth,
    name: str,
    model_id: str,
    model_version: str,
    cron: str,
    time_zone: str = "UTC",
    entity_id: str | None = None,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> DomoWorkflow_Trigger_Timer

Create a TIMER trigger that fires model_id@model_version on a cron schedule.

cron is a Quartz-style 6-field expression: "sec min hour day month dow" (e.g. "0 0 2 * * ?" = daily at 02:00). entity_id defaults to the authenticated user — Domo requires TIMER triggers be scoped to their creator.

Source code in src/crew_dcs/classes/DomoWorkflow/trigger.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 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
@classmethod
async def create(
    cls,
    auth: DomoAuth,
    name: str,
    model_id: str,
    model_version: str,
    cron: str,
    time_zone: str = "UTC",
    entity_id: str | None = None,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> DomoWorkflow_Trigger_Timer:
    """Create a TIMER trigger that fires `model_id`@`model_version` on a cron schedule.

    `cron` is a Quartz-style 6-field expression: "sec min hour day month dow"
    (e.g. "0 0 2 * * ?" = daily at 02:00). `entity_id` defaults to the
    authenticated user — Domo requires TIMER triggers be scoped to their creator.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    if entity_id is None:
        if not auth.user_id:
            await auth.who_am_i()
        entity_id = auth.user_id

    res = await workflow_routes.create_workflow_trigger(
        auth=auth,
        name=name,
        trigger_type="TIMER",
        model_id=model_id,
        model_version=model_version,
        entity_type="USER",
        entity_id=entity_id,
        metadata={
            "type": "TIMER",
            "cron": cron,
            "timeZone": time_zone,
            "interval": "Every Day",
            "minuteDuration": None,
            "activeHours": None,
            "showActiveHours": True,
            "startHour": 0,
            "dayOfWeek": None,
            "dayOfMonth": None,
        },
        context=context,
    )

    return cls.from_dict(auth=auth, obj=res.response)

create_daily async classmethod

create_daily(
    auth: DomoAuth,
    name: str,
    model_id: str,
    model_version: str,
    hour: int,
    minute: int = 0,
    time_zone: str = "UTC",
    entity_id: str | None = None,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> DomoWorkflow_Trigger_Timer

Convenience wrapper for the common "run once a day at HH:MM" schedule.

Source code in src/crew_dcs/classes/DomoWorkflow/trigger.py
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
@classmethod
async def create_daily(
    cls,
    auth: DomoAuth,
    name: str,
    model_id: str,
    model_version: str,
    hour: int,
    minute: int = 0,
    time_zone: str = "UTC",
    entity_id: str | None = None,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> DomoWorkflow_Trigger_Timer:
    """Convenience wrapper for the common "run once a day at HH:MM" schedule."""
    cron = f"0 {minute} {hour} * * ?"
    return await cls.create(
        auth=auth,
        name=name,
        model_id=model_id,
        model_version=model_version,
        cron=cron,
        time_zone=time_zone,
        entity_id=entity_id,
        debug_api=debug_api,
        session=session,
        context=context,
        **context_kwargs,
    )

delete async

delete(
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> bool

Soft-delete this trigger (stops future firings).

Parameters:

Name Type Description Default
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
bool

True when Domo accepted the delete.

Source code in src/crew_dcs/classes/DomoWorkflow/trigger.py
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
391
392
393
394
async def delete(
    self,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> bool:
    """Soft-delete this trigger (stops future firings).

    Args:
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        True when Domo accepted the delete.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    res = await workflow_routes.delete_workflow_trigger(
        auth=self.auth,
        trigger_id=self.id,
        context=context,
    )

    return res.is_success

from_dict classmethod

from_dict(
    auth: DomoAuth, obj: dict[str, Any]
) -> DomoWorkflow_Trigger_Timer

Build a timer trigger from a /v2/triggers API payload.

Parameters:

Name Type Description Default
auth DomoAuth

Authenticated Domo session.

required
obj dict[str, Any]

Raw trigger dict.

required

Returns:

Type Description
DomoWorkflow_Trigger_Timer

The populated trigger.

Source code in src/crew_dcs/classes/DomoWorkflow/trigger.py
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
@classmethod
def from_dict(
    cls, auth: DomoAuth, obj: dict[str, Any]
) -> DomoWorkflow_Trigger_Timer:
    """Build a timer trigger from a ``/v2/triggers`` API payload.

    Args:
        auth: Authenticated Domo session.
        obj: Raw trigger dict.

    Returns:
        The populated trigger.
    """
    metadata = obj.get("metadata") or {}
    return cls(
        id=obj.get("id", ""),
        auth=auth,
        name=obj.get("name", ""),
        model_id=obj.get("modelId", ""),
        model_version=obj.get("modelVersion", ""),
        cron=metadata.get("cron", ""),
        time_zone=metadata.get("timeZone", "UTC"),
        entity_id=obj.get("entityId"),
        active=obj.get("active", True),
        created_by=obj.get("createdBy"),
        created_dt=convert_string_to_datetime(obj.get("createdAt")),
        updated_by=obj.get("updatedBy"),
        updated_dt=convert_string_to_datetime(obj.get("updatedAt")),
        deleted_dt=convert_string_to_datetime(obj.get("deletedAt")),
        raw=obj,
    )

get_by_id async classmethod

get_by_id(
    auth: DomoAuth,
    trigger_id: str,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> DomoWorkflow_Trigger_Timer

Fetch a trigger by ID.

Parameters:

Name Type Description Default
auth DomoAuth

Authenticated Domo session.

required
trigger_id str

Trigger UUID.

required
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
DomoWorkflow_Trigger_Timer

The trigger.

Source code in src/crew_dcs/classes/DomoWorkflow/trigger.py
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
@classmethod
async def get_by_id(
    cls,
    auth: DomoAuth,
    trigger_id: str,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> DomoWorkflow_Trigger_Timer:
    """Fetch a trigger by ID.

    Args:
        auth: Authenticated Domo session.
        trigger_id: Trigger UUID.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The trigger.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    res = await workflow_routes.get_workflow_trigger_by_id(
        auth=auth,
        trigger_id=trigger_id,
        context=context,
    )

    return cls.from_dict(auth=auth, obj=res.response)

list_by_entity async classmethod

list_by_entity(
    auth: DomoAuth,
    entity_type: str,
    entity_id: str,
    entity_versions: str | None = None,
    return_raw: bool = False,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

List triggers attached to an entity.

Returns the raw response: the listing spans every trigger type, not only TIMER, so it is not converted into DomoWorkflow_Trigger_Timer.

Parameters:

Name Type Description Default
auth DomoAuth

Authenticated Domo session.

required
entity_type str

Entity type path segment.

required
entity_id str

Entity UUID.

required
entity_versions str | None

Optional entity versions to filter by.

None
return_raw bool

If True, return the response without raising on failure.

False
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
ResponseGetData

The API response.

Raises:

Type Description
Workflow_GET_Error

If the response is not successful and return_raw is False.

Source code in src/crew_dcs/classes/DomoWorkflow/trigger.py
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
@classmethod
async def list_by_entity(
    cls,
    auth: DomoAuth,
    entity_type: str,
    entity_id: str,
    entity_versions: str | None = None,
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """List triggers attached to an entity.

    Returns the raw response: the listing spans every trigger type, not
    only TIMER, so it is not converted into ``DomoWorkflow_Trigger_Timer``.

    Args:
        auth: Authenticated Domo session.
        entity_type: Entity type path segment.
        entity_id: Entity UUID.
        entity_versions: Optional entity versions to filter by.
        return_raw: If True, return the response without raising on failure.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The API response.

    Raises:
        Workflow_GET_Error: If the response is not successful and
            ``return_raw`` is False.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    return await workflow_routes.list_workflow_triggers_by_entity(
        auth=auth,
        entity_type=entity_type,
        entity_id=entity_id,
        entity_versions=entity_versions,
        return_raw=return_raw,
        context=context,
    )

list_by_model async classmethod

list_by_model(
    auth: DomoAuth,
    workflow_id: str,
    return_raw: bool = False,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

List the triggers of a workflow model.

Returns the raw response: the listing spans every trigger type, not only TIMER, so it is not converted into DomoWorkflow_Trigger_Timer.

Parameters:

Name Type Description Default
auth DomoAuth

Authenticated Domo session.

required
workflow_id str

Workflow UUID.

required
return_raw bool

If True, return the response without raising on failure.

False
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
ResponseGetData

The API response.

Raises:

Type Description
Workflow_GET_Error

If the response is not successful and return_raw is False.

Source code in src/crew_dcs/classes/DomoWorkflow/trigger.py
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
@classmethod
async def list_by_model(
    cls,
    auth: DomoAuth,
    workflow_id: str,
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """List the triggers of a workflow model.

    Returns the raw response: the listing spans every trigger type, not
    only TIMER, so it is not converted into ``DomoWorkflow_Trigger_Timer``.

    Args:
        auth: Authenticated Domo session.
        workflow_id: Workflow UUID.
        return_raw: If True, return the response without raising on failure.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The API response.

    Raises:
        Workflow_GET_Error: If the response is not successful and
            ``return_raw`` is False.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    return await workflow_routes.list_workflow_triggers_by_model(
        auth=auth,
        workflow_id=workflow_id,
        return_raw=return_raw,
        context=context,
    )

list_trigger_types async classmethod

list_trigger_types(
    auth: DomoAuth,
    return_raw: bool = False,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

List the trigger types Domo offers for workflows.

Returns the raw response: the listing spans every trigger type, not only TIMER, so it is not converted into DomoWorkflow_Trigger_Timer.

Parameters:

Name Type Description Default
auth DomoAuth

Authenticated Domo session.

required
return_raw bool

If True, return the response without raising on failure.

False
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
ResponseGetData

The API response.

Raises:

Type Description
Workflow_GET_Error

If the response is not successful and return_raw is False.

Source code in src/crew_dcs/classes/DomoWorkflow/trigger.py
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
@classmethod
async def list_trigger_types(
    cls,
    auth: DomoAuth,
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """List the trigger types Domo offers for workflows.

    Returns the raw response: the listing spans every trigger type, not
    only TIMER, so it is not converted into ``DomoWorkflow_Trigger_Timer``.

    Args:
        auth: Authenticated Domo session.
        return_raw: If True, return the response without raising on failure.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The API response.

    Raises:
        Workflow_GET_Error: If the response is not successful and
            ``return_raw`` is False.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    return await workflow_routes.list_workflow_trigger_types(
        auth=auth,
        return_raw=return_raw,
        context=context,
    )

DomoWorkflow_Version dataclass

DomoWorkflow_Version(
    schema_version: int = 2,
    tiles: list[DomoWorkflow_Tile] = list(),
    edges: list[DomoWorkflow_Edge] = list(),
    layouts: list[dict[str, Any]] = list(),
    inputs: dict[str, DomoWorkflow_Parameter] = dict(),
    outputs: dict[str, DomoWorkflow_Parameter] = dict(),
    *,
    raw: dict = dict(),
    model_id: str = "",
    version: str = "",
    owner_id: str | None = None,
    created_by: str | None = None,
    created_dt: datetime | None = None,
    updated_by: str | None = None,
    updated_dt: datetime | None = None,
    active: bool | None = None,
    deployed_on: datetime | None = None,
    saved_json: str | None = None,
    parent: Any | None = None
)

Bases: DomoWorkflow_Design

A workflow version that merges semver metadata with design behavior.

Attributes:

Name Type Description
model_id str

Parent workflow model UUID.

version str

Semver string, e.g. "1.0.0".

owner_id str | None

User ID of the version owner.

created_by str | None

User ID who created this version.

created_dt datetime | None

Creation timestamp.

updated_by str | None

User ID who last updated this version.

updated_dt datetime | None

Last-update timestamp.

parent Any | None

Optional parent DomoWorkflow reference.

raw dict

Raw version API response dict.

is_deployed property

is_deployed: bool

Return True when the workflow version appears to be deployed/active.

from_dict classmethod

from_dict(
    obj: dict[str, Any], parent: Any | None = None
) -> DomoWorkflow_Version

Build a version from a workflow version API payload.

Parameters:

Name Type Description Default
obj dict[str, Any]

Raw version dict (savedJson is parsed when well-formed).

required
parent Any | None

Optional parent DomoWorkflow.

None

Returns:

Type Description
DomoWorkflow_Version

The populated version; the design is empty when savedJson is

DomoWorkflow_Version

missing or malformed.

Source code in src/crew_dcs/classes/DomoWorkflow/version.py
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
@classmethod
def from_dict(
    cls,
    obj: dict[str, Any],
    parent: Any | None = None,  # DomoWorkflow — avoid circular import
) -> DomoWorkflow_Version:
    """Build a version from a workflow version API payload.

    Args:
        obj: Raw version dict (``savedJson`` is parsed when well-formed).
        parent: Optional parent ``DomoWorkflow``.

    Returns:
        The populated version; the design is empty when ``savedJson`` is
        missing or malformed.
    """
    saved_json_str = obj.get("savedJson")
    design = DomoWorkflow_Design.empty()
    if saved_json_str:
        # Malformed savedJson — keep definition as None
        with contextlib.suppress(ValueError, KeyError, TypeError, AttributeError):
            design = DomoWorkflow_Design.from_raw(json.loads(saved_json_str))

    return cls(
        schema_version=design.schema_version,
        tiles=design.tiles,
        edges=design.edges,
        layouts=design.layouts,
        inputs=design.inputs,
        outputs=design.outputs,
        model_id=obj.get("modelId", ""),
        version=obj.get("version", ""),
        owner_id=obj.get("owner"),
        created_by=obj.get("createdBy"),
        created_dt=convert_string_to_datetime(obj.get("createdOn")),
        updated_by=obj.get("updatedBy"),
        updated_dt=convert_string_to_datetime(obj.get("updatedOn")),
        active=obj.get("active"),
        deployed_on=convert_string_to_datetime(obj.get("deployedOn")),
        saved_json=saved_json_str,
        parent=parent,
        raw=obj,
    )

get_definition async

get_definition(
    *,
    auth: DomoAuth | None = None,
    return_raw: bool = False,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

Get the v2 definition of this workflow version.

Parameters:

Name Type Description Default
auth DomoAuth | None

Session to use; defaults to the parent workflow's auth.

None
return_raw bool

If True, return the response without raising on failure.

False
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
ResponseGetData

The API response.

Raises:

Type Description
ValueError

If no auth is given and the version has no parent auth.

Workflow_GET_Error

If the response is not successful and return_raw is False.

Source code in src/crew_dcs/classes/DomoWorkflow/version.py
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
async def get_definition(
    self,
    *,
    auth: DomoAuth | None = None,
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """Get the v2 definition of this workflow version.

    Args:
        auth: Session to use; defaults to the parent workflow's auth.
        return_raw: If True, return the response without raising on failure.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The API response.

    Raises:
        ValueError: If no auth is given and the version has no parent auth.
        Workflow_GET_Error: If the response is not successful and
            ``return_raw`` is False.
    """
    resolved_auth = auth or getattr(self.parent, "auth", None)
    if resolved_auth is None:
        raise ValueError("auth is required when version parent has no auth")

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

    return await workflow_routes.get_workflow_version_definition(
        auth=resolved_auth,
        workflow_id=self.model_id,
        version_number=self.version,
        return_raw=return_raw,
        context=context,
    )

get_trigger_form async

get_trigger_form(
    *,
    auth: DomoAuth | None = None,
    trigger_id: str | None = None,
    deployment_id: str | None = None,
    return_raw: bool = False,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

Get the trigger form of this workflow version.

Parameters:

Name Type Description Default
auth DomoAuth | None

Session to use; defaults to the parent workflow's auth.

None
trigger_id str | None

Optional trigger to render the form for.

None
deployment_id str | None

Optional deployment to render the form for.

None
return_raw bool

If True, return the response without raising on failure.

False
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
ResponseGetData

The API response.

Raises:

Type Description
ValueError

If no auth is given and the version has no parent auth.

Workflow_GET_Error

If the response is not successful and return_raw is False.

Source code in src/crew_dcs/classes/DomoWorkflow/version.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
229
230
231
232
233
234
235
236
237
238
239
240
async def get_trigger_form(
    self,
    *,
    auth: DomoAuth | None = None,
    trigger_id: str | None = None,
    deployment_id: str | None = None,
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """Get the trigger form of this workflow version.

    Args:
        auth: Session to use; defaults to the parent workflow's auth.
        trigger_id: Optional trigger to render the form for.
        deployment_id: Optional deployment to render the form for.
        return_raw: If True, return the response without raising on failure.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The API response.

    Raises:
        ValueError: If no auth is given and the version has no parent auth.
        Workflow_GET_Error: If the response is not successful and
            ``return_raw`` is False.
    """
    resolved_auth = auth or getattr(self.parent, "auth", None)
    if resolved_auth is None:
        raise ValueError("auth is required when version parent has no auth")

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

    return await workflow_routes.get_workflow_trigger_form(
        auth=resolved_auth,
        model_id=self.model_id,
        version=self.version,
        trigger_id=trigger_id,
        deployment_id=deployment_id,
        return_raw=return_raw,
        context=context,
    )

release async

release(
    *,
    auth: DomoAuth | None = None,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

Release (deploy/activate) this specific workflow version.

Source code in src/crew_dcs/classes/DomoWorkflow/version.py
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 release(
    self,
    *,
    auth: DomoAuth | None = None,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """Release (deploy/activate) this specific workflow version."""
    resolved_auth = auth or getattr(self.parent, "auth", None)
    if resolved_auth is None:
        raise ValueError("auth is required when version parent has no auth")

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

    return await workflow_routes.release_workflow_version(
        auth=resolved_auth,
        model_id=self.model_id,
        version=self.version,
        context=context,
    )

to_dict

to_dict() -> dict[str, Any]

Serialize to the API shape (suitable for PUT /versions/{version}).

Source code in src/crew_dcs/classes/DomoWorkflow/version.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
def to_dict(self) -> dict[str, Any]:
    """Serialize to the API shape (suitable for PUT /versions/{version})."""
    payload: dict[str, Any] = {
        "modelId": self.model_id,
        "version": self.version,
        "owner": self.owner_id,
        "createdBy": self.created_by,
        "createdOn": self.created_dt.isoformat() if self.created_dt else None,
        "updatedBy": self.updated_by,
        "updatedOn": self.updated_dt.isoformat() if self.updated_dt else None,
        "active": self.active,
        "deployedOn": self.deployed_on.isoformat() if self.deployed_on else None,
    }
    payload["savedJson"] = json.dumps(super().to_dict())
    return payload

validate_definition

validate_definition(
    *,
    require_true_false_labels: bool = True,
    logic_tile_types: tuple[str, ...] = (
        "conditionalGatewayNode",
    ),
    allow_dangling_tile_types: tuple[str, ...] = (
        "endNode",
    )
) -> tuple[bool, list[str]]

Validate this version's design topology and branching rules.

Source code in src/crew_dcs/classes/DomoWorkflow/version.py
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
def validate_definition(
    self,
    *,
    require_true_false_labels: bool = True,
    logic_tile_types: tuple[str, ...] = ("conditionalGatewayNode",),
    allow_dangling_tile_types: tuple[str, ...] = ("endNode",),
) -> tuple[bool, list[str]]:
    """Validate this version's design topology and branching rules."""
    if not self.tiles:
        return False, [f"missing definition for workflow version '{self.version}'"]

    errors: list[str] = []
    if require_true_false_labels:
        try:
            self.validate_boolean_conditionals(required_labels=("True", "False"))
        except ValueError as exc:
            errors.append(str(exc))

    errors.extend(
        self.get_connection_validation_errors(
            logic_tile_types=logic_tile_types,
            allow_dangling_tile_types=allow_dangling_tile_types,
            required_logic_outputs=2,
        )
    )
    return len(errors) == 0, errors

validate_with_domo async

validate_with_domo(
    *,
    auth: DomoAuth | None = None,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

Validate this version's design with Domo's official validator API.

Source code in src/crew_dcs/classes/DomoWorkflow/version.py
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
async def validate_with_domo(
    self,
    *,
    auth: DomoAuth | None = None,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """Validate this version's design with Domo's official validator API."""
    if not self.tiles:
        raise ValueError(f"missing design for workflow version '{self.version}'")

    resolved_auth = auth or getattr(self.parent, "auth", None)
    if resolved_auth is None:
        raise ValueError("auth is required when version parent has no auth")

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

    return await workflow_routes.validate_workflow_model(
        auth=resolved_auth,
        body=DomoWorkflow_Design.to_dict(self),
        context=context,
    )

DomoWorkflow_Versions dataclass

DomoWorkflow_Versions(
    auth: DomoAuth,
    model_id: str,
    parent: Any | None = None,
    items: list[DomoWorkflow_Version] | None = None,
    current_version: DomoWorkflow_Version | None = None,
)

Bases: DomoManager

Manager for loading workflow versions where each version is a design object.

get async

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

List all versions for a workflow model.

Source code in src/crew_dcs/classes/DomoWorkflow/version.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
async def get(
    self,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> list[DomoWorkflow_Version]:
    """List all versions for a workflow model."""
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    res = await workflow_routes.list_workflow_versions(
        auth=self.auth,
        model_id=self.model_id,
        context=context,
    )

    versions_raw = res.response if res.is_success else []
    self.items = [
        DomoWorkflow_Version.from_dict(v, parent=self.parent)
        for v in (versions_raw or [])
    ]
    return self.items

get_by_version async

get_by_version(
    version: str,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> DomoWorkflow_Version | None

Fetch one specific version for this workflow model.

Source code in src/crew_dcs/classes/DomoWorkflow/version.py
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
async def get_by_version(
    self,
    version: str,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> DomoWorkflow_Version | None:
    """Fetch one specific version for this workflow model."""
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    res = await workflow_routes.get_workflow(
        auth=self.auth,
        model_id=self.model_id,
        version_id=version,
        context=context,
    )

    if not res.is_success:
        return None

    self.current_version = DomoWorkflow_Version.from_dict(
        res.response,
        parent=self.parent,
    )
    return self.current_version

DomoWorkflows dataclass

DomoWorkflows(
    auth: DomoAuth,
    workflows: list[DomoWorkflow] | None = None,
)

Manager for listing and searching workflow models.

Attributes:

Name Type Description
auth DomoAuth

Authenticated Domo session.

workflows list[DomoWorkflow] | None

Cached list from the last get call, if any.

get async

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

List all workflow models and cache them on self.workflows.

Parameters:

Name Type Description Default
return_raw bool

If True, return the raw route response instead of entities.

False
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
list[DomoWorkflow] | ResponseGetData

The workflows, or the raw response when return_raw is True.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
async def get(
    self,
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> list[DomoWorkflow] | rgd.ResponseGetData:
    """List all workflow models and cache them on ``self.workflows``.

    Args:
        return_raw: If True, return the raw route response instead of entities.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The workflows, or the raw response when ``return_raw`` is True.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    res = await workflow_routes.list_workflows(
        auth=self.auth,
        context=context,
    )

    if return_raw:
        return res

    models_raw = res.response if res.is_success else []
    self.workflows = [
        DomoWorkflow.from_dict(auth=self.auth, obj=obj)
        for obj in (models_raw or [])
    ]
    return self.workflows

get_models_usage async

get_models_usage(
    entity_type: str | None = None,
    entity_id: str | None = None,
    parts: str | None = None,
    return_raw: bool = False,
    debug_api: bool = False,
    session: AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any
) -> ResponseGetData

List workflow usage of an entity (for example a Code Engine package).

Parameters:

Name Type Description Default
entity_type str | None

Entity type to look up usage for.

None
entity_id str | None

Entity ID to look up usage for.

None
parts str | None

Optional response parts selector.

None
return_raw bool

If True, return the response without raising on failure.

False
debug_api bool

If True, enable API debug output.

False
session AsyncClient | None

Optional shared HTTP client.

None
context RouteContext | None

Optional pre-built route context.

None
**context_kwargs Any

Extra context overrides.

{}

Returns:

Type Description
ResponseGetData

The API response.

Raises:

Type Description
Workflow_GET_Error

If the response is not successful and return_raw is False.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
async def get_models_usage(
    self,
    entity_type: str | None = None,
    entity_id: str | None = None,
    parts: str | None = None,
    return_raw: bool = False,
    debug_api: bool = False,
    session: httpx.AsyncClient | None = None,
    *,
    context: RouteContext | None = None,
    **context_kwargs: Any,
) -> rgd.ResponseGetData:
    """List workflow usage of an entity (for example a Code Engine package).

    Args:
        entity_type: Entity type to look up usage for.
        entity_id: Entity ID to look up usage for.
        parts: Optional response parts selector.
        return_raw: If True, return the response without raising on failure.
        debug_api: If True, enable API debug output.
        session: Optional shared HTTP client.
        context: Optional pre-built route context.
        **context_kwargs: Extra context overrides.

    Returns:
        The API response.

    Raises:
        Workflow_GET_Error: If the response is not successful and
            ``return_raw`` is False.
    """
    context = RouteContext.build_context(
        context=context,
        session=session,
        debug_api=debug_api,
        **context_kwargs,
    )

    return await workflow_routes.get_workflow_models_usage(
        auth=self.auth,
        entity_type=entity_type,
        entity_id=entity_id,
        parts=parts,
        return_raw=return_raw,
        context=context,
    )

search_by_name

search_by_name(name: str) -> list[DomoWorkflow]

Filter the cached workflow list by name (case-insensitive substring).

Parameters:

Name Type Description Default
name str

Substring to match against workflow names.

required

Returns:

Type Description
list[DomoWorkflow]

Matching workflows.

Raises:

Type Description
RuntimeError

If get has not populated the list yet.

Source code in src/crew_dcs/classes/DomoWorkflow/core.py
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
def search_by_name(self, name: str) -> list[DomoWorkflow]:
    """Filter the cached workflow list by name (case-insensitive substring).

    Args:
        name: Substring to match against workflow names.

    Returns:
        Matching workflows.

    Raises:
        RuntimeError: If ``get`` has not populated the list yet.
    """
    if self.workflows is None:
        raise RuntimeError("Call get() first to populate the workflow list.")
    name_lower = name.lower()
    return [w for w in self.workflows if name_lower in (w.name or "").lower()]

Modules