Skip to main content

Build Gem UI

In Prophecy, the dialog() function defines the visual configuration interface for your gem. This is what users see and interact with when setting up or editing a gem in the UI.

To build an effective dialog, you'll use layout containers, UI controls (like checkboxes, text boxes, expression editors), and bindings to connect UI elements to your gem's configuration. All of these elements are imported into your custom gem from uispec.py.

By examining uispec.py, you can:

  • Discover the full library of UI elements.
  • Understand what each component does and what methods it supports.
  • See how to configure visual behavior, bind properties, add hints/tooltips, and more.
info

To see an example dialog() function, visit Gem Builder reference.

tip

To reference the complete code of a certain gem, search for the gem using the left sidebar of the project editor. Click the gem to open its code, then look for the dialog() function. This function contains the code that determines how UI components are configured.

Atoms

Atoms are basic UI components defined in uispec.py that you can use in the gem dialog configuration. Below are the most commonly used atoms, with example code and images for reference.


Checkbox

Boolean checkbox element that a user can select or unselect.

Checkbox("First row is header")
Checkbox

Switch

Boolean toggle that a user can enable or disable.

Switch("")
Switch

TextBox

Allows the user to type a single line of input.

TextBox("Column delimiter")
TextBox

ExpressionBox

Input for code expressions.

ExpressionBox("Pivot Column")
.makeFieldOptional()
.withSchemaSuggestions()
.bindPlaceholders(
{{
"scala": "col(\"col_name\")",
"python": "col(\"col_name\")",
"sql": "col_name"
}}
)
.bindProperty("pivotColumn")
ExpressionBox

ExpTable

Dynamic table with target and expression columns.

ExpTable("Aggregate Expressions").bindProperty("aggregate").withCopilotEnabledExpressions().allowCopilotExpressionsFix()
ExpTable

BasicTable

Configurable table with editable cells and optional footers.

BasicTable(
"Unique Values",
height="400px",
columns=[
Column(
"Unique Values",
"colName",
(
TextBox("").bindPlaceholder(
"Enter value present in pivot column"
)
),
)
],
).bindProperty("pivotValues")
BasicTable

TitleElement

Static heading element, rendered as title text with optional heading level.

TitleElement("Where Clause")
TitleElement

TextArea

Multi-line text input area for longer descriptions.

TextArea("Description", 2, placeholder="Dataset description...")
TextArea

SchemaTable

Information about the dataset schema, including column names, data type, and metadata.

SchemaTable("").bindProperty("schema")
SchemaTable

Ports

Component for displaying and editing gem input or output ports.

Ports(minInputPorts=2,
selectedFieldsProperty=("columnsSelector"),
singleColumnClickCallback=self.onClickFunc,
allColumnsSelectionCallback=self.allColumnsSelectionFunc).editableInput(True)
Ports

SchemaColumnsDropdown

Dropdown that lets the user select columns defined in the dataset schema.

SchemaColumnsDropdown("Partition Columns")
.withMultipleSelection()
.bindSchema("schema")
.bindProperty("partitionColumns")
SchemaColumnsDropdown

SelectBox

Dropdown menu with search and other configuration options.

SelectBox("")
.addOption("Inner", "inner")
.addOption("Outer", "outer")
.addOption("Left Outer", "left_outer")
.addOption("Right Outer", "right_outer")
.addOption("Left Semi", "left_semi")
.addOption("Left Anti", "left_anti")
.addOption("Cross Join", "cross")
SelectBox

AlertBox

Message box for alerts/info/warnings.

AlertBox(
variant="success",
_children=[
Markdown(
...
)
]
)
AlertBox

Markdown

Displays static Markdown-formatted content.

Markdown(
"**Column Split Delimiter Examples:**"
"\n"
"- **Tab-separated values:**"
"\n"
" **Delimiter:** \\t"
"\n"
" Example: Value1<tab>Value2<tab>Value3"
"\n"
"- **Newline-separated values:**"
"\n"
" **Delimiter:** \\n"
"\n"
" Example: Value1\\nValue2\\nValue3"
"\n"
"- **Pipe-separated values:**"
"\n"
" **Delimiter:** |"
"\n"
" Example: Value1|Value2|nValue3"
)
Markdown

RadioGroup

A group of radio buttons that can have labels, values, icons, and descriptions.

RadioGroup("Operation Type")
.addOption(
"Union",
"unionAll",
("UnionAll"),
("Returns a dataset containing rows in any one of the input Datasets, while preserving duplicates.")
)
Editor

Editor

Code editor with expression builder support and schema suggestions.

Editor(height=("100%")).withSchemaSuggestions().bindProperty("condition.expression")
Editor

Layout

The layout components let you organize your gems with columns, tabs, and more.

StackLayout

This arranges its child elements in a vertical stack to place elements one below the other.

ColumnLayout

This component arranges its child elements in horizontal columns, allowing for a side-by-side layout.

FieldPicker

This is a form-like component that contains labeled input fields. Each field you add call adds a new form control (like text box, checkbox, select box) associated with a specific property.

Explore uispec.py

import copy
import enum
from abc import ABC, abstractmethod
from dataclasses import dataclass, field, replace
from typing import TypeVar, Generic, Optional, List, Any, Dict
from typing import Union

from pyspark.sql.column import Column as sparkColumn
from pyspark.sql.functions import col

from prophecy.cb.decorator import singleton
from prophecy.cb.util.StringUtils import isBlank
from enum import Enum

''' ---------------------------- BASE ----------------------------'''


@singleton
class UISpec:
currentId: int = 0

def defaultLanguages(self) -> set:
return {'scala', 'python', 'sql'}

def getId(self) -> int:
self.currentId = self.currentId + 1
return self.currentId


@dataclass(frozen=True)
class PropertyContext:
contextName: str
prefix: str


def propertyPath(property: str) -> str:
if property.startswith("$.metadata"):
return f"${{{property}}}"
elif property.startswith("record."):
return f"${{{property}}}"
elif not property.startswith("component."):
return f'${{{".".join([prop for prop in ["component", "properties", property] if prop])}}}'
else:
return f"${{{property}}}"


class Element(ABC):
id: str = f"{UISpec().getId()}"

def __post_init__(self):
self.id = f"{UISpec().getId()}"

@abstractmethod
def kind(self) -> str:
pass

@abstractmethod
def json(self) -> dict:
pass

def propertyPath(self, property: str) -> str:
return propertyPath(property)



class Atom(Element, ABC):
@abstractmethod
def title(self) -> str:
pass

@abstractmethod
def property(self) -> Optional[str]:
pass

@abstractmethod
def bindProperty(self, property: str):
pass

def propertyKey(self) -> str:
return "value"

def getTemplateElements(self) -> List[Element]:
return []

def jsonProperties(self) -> dict:
return {"title": self.title()}

def json(self) -> dict:
properties = self.jsonProperties()
localProperty = self.property()
if localProperty is not None:
properties[self.propertyKey()] = self.propertyPath(localProperty)
return {"id": self.id, "kind": self.kind(), "properties": properties}


ElementType = TypeVar("ElementType", bound=Element)


class Container(Generic[ElementType], Element, ABC):
@abstractmethod
def children(self) -> list:
pass

def jsonProperties(self) -> dict:
return {}

def json(self) -> dict:
tmpList = []
childs = self.children()
if childs is not None:
reversedChilds = reversed(childs)
for child in reversedChilds:
tmpList.append(child.json())
return {
"id": self.id,
"kind": self.kind(),
"properties": self.jsonProperties(),
"contains": tmpList
}


@enum.unique
class CopilotType(enum.Enum):
button = "button"
prompt = "prompt"

@staticmethod
def get_enum(value):
if value == "button":
return CopilotType.button
elif value == "prompt":
return CopilotType.prompt

def to_json(self):
return self.value

@enum.unique
class AutoSuggestType(enum.Enum):
none = "none"
update = "update"
placeholder = "placeholder"

@staticmethod
def get_enum(value):
if value == "none":
return AutoSuggestType.none
elif value == "update":
return AutoSuggestType.update
elif value == "placeholder":
return AutoSuggestType.placeholder

def to_json(self):
return self.value

@dataclass
class CopilotSpecProps:
"""
The copilot spec props
..........
Attributes
----------
buttonLabel : str
The button label for the copilot button
align : str
Left (start) or right (end) alignment of the copilot button
alignOffset : int
Horizontal difference between the start points of the atom and copilot button
gap : int
Vertical difference between copilot button and atom
"""
buttonLabel: str = ""
align: str = "start" # or end
alignOffset: int = 0
gap: int = 0

@abstractmethod
def json(self) -> dict:
pass

@abstractmethod
def copilot_type(self) -> CopilotType:
pass


class CopilotButtonTypeProps(CopilotSpecProps):
"""
Properties for the copilot button type
"""

def json(self) -> dict:
props: dict = {
"buttonLabel": self.buttonLabel,
"align": self.align,
"alignOffset": self.alignOffset,
"gap": self.gap
}

return props

def copilot_type(self) -> CopilotType:
return CopilotType.button


class CopilotPromptTypeProps(CopilotSpecProps):
"""
Properties for the copilot prompt type
..........
Attributes
----------
promptPlaceholder : Optional[str]
Placeholder text inside the prompt input box
"""
promptPlaceholder: Optional[str] = None

def json(self) -> dict:
props: dict = {
"buttonLabel": self.buttonLabel,
"align": self.align,
"alignOffset": self.alignOffset,
"gap": self.gap
}

if self.promptPlaceholder is not None:
props["promptPlaceholder"] = self.promptPlaceholder

return props

def copilot_type(self) -> CopilotType:
return CopilotType.prompt


@dataclass
class CopilotSpec:
"""
The copilot properties for various atoms inside gems
..........
Attributes
----------
copilotProps: CopilotSpecProps
The copilot spec properties - button, prompt etc.
method : str
The websocket method to call
methodType : Optional[str]
The type of the message body (optional) (see usages for examples)
"""
copilotProps: CopilotSpecProps
method: str
methodType: Optional[str] = None
autoSuggest: Optional[str] = None
loadingMessage: Optional[str] = None

# Copilot type is inferred from copilot props
def json(self):
props = dict()
props["method"] = self.method
if self.methodType is not None:
props["methodType"] = self.methodType
props["copilotType"] = self.copilotProps.copilot_type().to_json()
props["copilotProps"] = self.copilotProps.json()

return props

def withDescribeColumn(self):
self.method = "copilot/describe"
self.methodType = "CopilotDescribeColumnRequest"
self.copilotProps = CopilotButtonTypeProps(buttonLabel="Auto-description", align="end", gap=4)
return self

def withDescribeDataSource(self):
self.method = "copilot/describe"
self.methodType = "CopilotDescribeDataSourceRequest"
self.copilotProps = CopilotButtonTypeProps(buttonLabel="Auto-description", align="end", gap=4)
return self

def withGetScript(self):
self.method = "copilot/getScript"
self.copilotProps = CopilotPromptTypeProps(buttonLabel="Ask AI", align="end", alignOffset=10)
return self

def withGetExpression(self):
self.method = "copilot/getExpression"
self.methodType = "CopilotProjectionExpressionRequest"
self.copilotProps = CopilotPromptTypeProps(buttonLabel="Ask AI")
return self

def withSuggestGemProperties(self):
self.method = "copilot/suggestGemProperties"
self.copilotProps = CopilotButtonTypeProps(buttonLabel="Ask-AI")
self.loadingMessage = "Writing Transformations..."
return self

def withAutoSuggestUpdate(self):
self.autoSuggest = "update"
return self

def withAutoSuggestPlaceholder(self):
self.autoSuggest = "placeholder"
return self


class ExpressionBuilderType(Enum):
VALUE_EXPRESSION = "ValueExpression"
COLUMN_EXPRESSION = "ColumnExpression"
FUNCTION_EXPRESSION = "FunctionExpression"
CASE_EXPRESSION = "CaseExpression"
CONFIG_EXPRESSION = "ConfigExpression"
INCREMENTAL_EXPRESSION = "IncrementalExpression"
SQL_FUNCTION = "SQLFunction"
MACRO_FUNCTION = "MacroFunction"
CUSTOM_EXPRESSION = "CustomExpression"
JINJA_CONCAT_EXPRESSION = "JinjaConcatExpression"
CAST_AS_EXPRESSION = "CastAsExpression"


class ExpressionBlockType(Enum):
INSIDE_JINJA = "insideJinja"
INSIDE_CONFIG_UI = "insideConfigUI"


class ExpressionValueType(Enum):
NUMBER = "number"
STRING = "string"
BOOLEAN = "boolean"


class ExpressionFunctionType(Enum):
SQL_FUNCTION = "SQLFunction"
MACRO_FUNCTION = "MacroFunction"


class ExpressionConfigType(Enum):
PIPELINE = "pipeline"
SQL_PROJECT = "sql_project"
MODEL = "model"


class GroupBuilderType(Enum):
GROUP = "group"
EXPRESSION = "expression"
CONDITIONAL = "conditional"

class RowLabel(Enum):
Column = "Column"

class SelectMode(Enum):
toggle = "toggle"
always = "always"
never = "never"

@dataclass
class VisualBuilderSpec:
supportedTypes: List[ExpressionBuilderType] = field(default_factory=list)
builderType: GroupBuilderType = GroupBuilderType.EXPRESSION
unsupportedTypes: List[ExpressionBuilderType] = field(default_factory=list)
supportedBlockType: Optional[ExpressionBlockType] = None
supportedValueType: Optional[ExpressionValueType] = None
supportedFunctionTypes: List[ExpressionFunctionType] = field(default_factory=list)
supportedConfigTypes: List[ExpressionConfigType] = field(default_factory=list)
border: Optional[bool] = None
placeholder: Optional[str] = None

def json(self) -> Dict[str, Any]:
props: Dict[str, Any] = {}

if self.unsupportedTypes is not None:
props["unsupportedTypes"] = [t.value for t in self.unsupportedTypes]
else:
props["supportedTypes"] = [t.value for t in self.supportedTypes]

props["builderType"] = self.builderType.value

if self.supportedBlockType is not None:
props["blockType"] = self.supportedBlockType.value

if self.supportedValueType is not None:
props["supportedValueType"] = self.supportedValueType.value

if self.supportedFunctionTypes is not None:
props["supportedFunctionTypes"] = [t.value for t in self.supportedFunctionTypes]

if self.supportedConfigTypes is not None:
props["supportedConfigTypes"] = [t.value for t in self.supportedConfigTypes]

if self.border is not None:
props["border"] = self.border

if self.placeholder is not None:
props["placeholder"] = self.placeholder

return props

''' ------------------------------------ ATOMS ----------------------------'''


@dataclass
class AlertBox(Container[Element]):
variant: str
banner: Optional[bool] = False
propertyVar: Optional[str] = None
_children: list = None

def title(self) -> str:
return "Alert"

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def addElement(self, element: Element):
if self._children is None:
return replace(self, _children=[element])
else:
self._children.insert(0, element)
return replace(self, _children=self._children)

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return "Atoms.Alert"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def jsonProperties(self) -> dict:
properties = super(AlertBox, self).jsonProperties()
if not isBlank(self.variant):
properties['variant'] = self.variant
if self.banner is not None:
properties['banner'] = self.banner
return properties


@dataclass
class Checkbox(Atom):
titleVar: str
propertyVar: Optional[str] = None
hint: Optional[str] = None
isChecked: Optional[bool] = False
helpText: Optional[str] = None

def title(self) -> str:
return self.titleVar

def withIsChecked(self, isChecked: bool):
return replace(self, isChecked=isChecked)

def withHint(self, hint: str):
return replace(self, hint=hint)

def withHelpText(self, helpText: str):
return replace(self, helpText=helpText)

def property(self) -> Optional[str]:
return self.propertyVar

def propertyKey(self) -> str:
return "checked"

def kind(self) -> str:
return "Atoms.CheckBox"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def jsonProperties(self) -> dict:
properties = super(Checkbox, self).jsonProperties()
properties["label"] = self.title()
if self.hint is not None:
properties["hint"] = self.hint
if self.isChecked is not None:
properties["checked"] = self.isChecked
if self.helpText is not None:
properties["helpText"] = self.helpText

return properties



@dataclass
class Switch(Atom):
titleVar: str
propertyVar: Optional[str] = None
hint: Optional[str] = None
disabled: Optional[bool] = False
checked: Optional[bool] = False

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def withChecked(self, checked: bool):
return replace(self, checked=checked)

def propertyKey(self) -> str:
return "checked"

def withDisabled(self, disabled: bool):
return replace(self, disabled=disabled)

def withHint(self, hint: str):
return replace(self, hint=hint)

def kind(self) -> str:
return "Atoms.Switch"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def jsonProperties(self) -> dict:
properties = super(Switch, self).jsonProperties()
properties["label"] = self.title()
if self.hint is not None:
properties["hint"] = self.hint
if self.disabled is not None:
properties["disabled"] = self.disabled
if self.checked is not None:
properties["checked"] = self.checked
return properties


@dataclass
class ExpressionBox(Atom):
title: str = ""
language: Optional[str] = "${record.expression.format}"
placeholder: dict = dict(),
propertyVar: Optional[str] = None
selectedFields: Optional[str] = None
ports: Optional[str] = None
ignoreTitle: bool = False
readOnly: Optional[bool] = None
_kind: str = "ExpressionBox"
fieldType: Optional[str] = None
copilot: Optional[CopilotSpec] = None
fixWithCopilot: Optional[bool] = None
visualBuilder: Optional[VisualBuilderSpec] = None

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return self._kind

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindLanguage(self, language: str):
return replace(self, language=language)

def bindSelectedFieldProperty(self, selectedFields: str):
return replace(self, selectedFields=selectedFields)

def bindPlaceholders(self, languagePlaceholders=None):
if languagePlaceholders is None:
languagePlaceholders = {
"scala": """concat(col("source_column"), col("other_column"))""",
"python": """concat(col("source_column"), col("other_column"))""",
"sql": """concat(source_column, other_column)"""}
return replace(self, placeholder=languagePlaceholders.copy())

def bindPlaceholder(self, placeHolder: str):
languagePlaceholders = {
"scala": placeHolder,
"python": placeHolder,
"sql": placeHolder}
return replace(self, placeholder=languagePlaceholders.copy())

def withFrontEndLanguage(self):
return replace(self, language="${$.workflow.metainfo.frontEndLanguage}")

def bindPorts(self, ports: str):
return replace(self, ports=ports)

def withSchemaSuggestions(self):
return self.bindPorts("component.ports.inputs")

def makeFieldOptional(self):
return replace(self, _kind="SColumn", fieldType="ExpressionBox")

def disabled(self):
return replace(self, readOnly=True)

# TODO: This is a temporary backup method. Will be removed later
def withCopilot(self, copilot: CopilotSpec):
return replace(self, copilot=copilot)
def withCopilotEnabledExpression(self):
copilot = CopilotSpec(method="copilot/getExpression", copilotProps=CopilotSpecProps()).withGetExpression()
return replace(self, copilot=copilot)


def allowFixWithCopilot(self):
return replace(self, fixWithCopilot=True)

def withVisualBuilderSpec(self, visualBuilder: VisualBuilderSpec):
return replace(self, visualBuilder=visualBuilder)

def withExpressionBuilder(self, visualBuilderType: List[ExpressionBuilderType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedTypes=visualBuilderType))

def withUnsupportedExpressionBuilderTypes(self, unsupportedTypes: List[ExpressionBuilderType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
unsupportedTypes=unsupportedTypes))

def withGroupBuilder(self, groupBuilderType: GroupBuilderType):
return replace(self,
visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), builderType=groupBuilderType))

def withBlockType(self, expressionBlockType: ExpressionBlockType):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedBlockType=expressionBlockType))

def withValueType(self, valueType: ExpressionValueType):
return replace(self,
visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), supportedValueType=valueType))

def withFunctionTypes(self, functionTypes: List[ExpressionFunctionType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedFunctionTypes=functionTypes))

def withConfigTypes(self, configTypes: List[ExpressionConfigType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedConfigTypes=configTypes))

def withBorder(self, border: bool):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), border=border))

def withVisualPlaceholder(self, placeholder: str):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), placeholder=placeholder))

def jsonProperties(self) -> dict:
props = dict()
if not self.ignoreTitle:
props["title"] = self.title
if self.language is not None:
props["language"] = self.language
if self.selectedFields is not None:
props["selectedFields"] = self.propertyPath(self.selectedFields)
if self.ports is not None:
props["ports"] = self.propertyPath(self.ports)
props["placeholder"] = self.placeholder
if self.readOnly is not None:
props["readOnly"] = self.readOnly
if self.fieldType is not None:
props["fieldType"] = self.fieldType
if self.copilot is not None:
props["copilot"] = self.copilot.json()
if self.fixWithCopilot is not None:
props["fixWithCopilot"] = self.fixWithCopilot
if self.visualBuilder is not None:
props["visualBuilder"] = self.visualBuilder.json()
return props

@dataclass
class BusinessRuleBox(Atom):
title: str = ""
language: Optional[str] = "${record.expression.format}"
placeholder: dict = dict(),
propertyVar: Optional[str] = None
selectedFields: Optional[str] = None
ports: Optional[str] = None
ignoreTitle: bool = False
readOnly: Optional[bool] = None
_kind: str = "BusinessRule"
fieldType: Optional[str] = None
copilot: Optional[CopilotSpec] = None
fixWithCopilot: Optional[bool] = None
options: Optional[str] = None
paramErrors: Optional[str] = None

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return self._kind

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindLanguage(self, language: str):
return replace(self, language=language)

def bindOptions(self, options: str):
return replace(self, options=options)

def bindParamErrors(self, paramErrors: str):
return replace(self, paramErrors=paramErrors)
def bindSelectedFieldProperty(self, selectedFields: str):
return replace(self, selectedFields=selectedFields)

def bindPlaceholders(self, languagePlaceholders=None):
if languagePlaceholders is None:
languagePlaceholders = {
"scala": """concat(col("source_column"), col("other_column"))""",
"python": """concat(col("source_column"), col("other_column"))""",
"sql": """concat(source_column, other_column)"""}
return replace(self, placeholder=languagePlaceholders.copy())

def bindPlaceholder(self, placeHolder: str):
languagePlaceholders = {
"scala": placeHolder,
"python": placeHolder,
"sql": placeHolder}
return replace(self, placeholder=languagePlaceholders.copy())

def withFrontEndLanguage(self):
return replace(self, language="${$.workflow.metainfo.frontEndLanguage}")

def bindPorts(self, ports: str):
return replace(self, ports=ports)

def withSchemaSuggestions(self):
return self.bindPorts("component.ports.inputs")

def makeFieldOptional(self):
return replace(self, _kind="SColumn", fieldType="ExpressionBox")

def disabled(self):
return replace(self, readOnly=True)

# TODO: This is a temporary backup method. Will be removed later
def withCopilot(self, copilot: CopilotSpec):
return replace(self, copilot=copilot)
def withCopilotEnabledExpression(self):
copilotSpec = CopilotSpec(method="copilot/getExpression", copilotProps=CopilotSpecProps()).withGetExpression()
return replace(self, copilot=copilotSpec)

def allowFixWithCopilot(self):
return replace(self, fixWithCopilot=True)

def jsonProperties(self) -> dict:
props = dict()
if not self.ignoreTitle:
props["title"] = self.title
if self.language is not None:
props["language"] = self.language
if self.selectedFields is not None:
props["selectedFields"] = self.propertyPath(self.selectedFields)
if self.ports is not None:
props["ports"] = self.propertyPath(self.ports)
props["placeholder"] = self.placeholder
if self.readOnly is not None:
props["readOnly"] = self.readOnly
if self.fieldType is not None:
props["fieldType"] = self.fieldType
if self.copilot is not None:
props["copilot"] = self.copilot.json()
if self.fixWithCopilot is not None:
props["fixWithCopilot"] = self.fixWithCopilot
if self.options is not None:
props["options"] = self.options
if self.paramErrors is not None:
props["paramErrors"] = self.paramErrors
return props


@dataclass
class SelectBoxOption:
label: str
value: str


@dataclass
class SelectBox(Atom):
titleVar: str
creatable: Optional[bool] = None
propertyVar: Optional[str] = None
options: list = None
optionProperty: Optional[str] = None
disabled: Optional[bool] = None
placeholder: Optional[str] = None
mode: Optional[str] = None
notFoundContent: Optional[str] = None
showSearch: Optional[bool] = None
footer: List[Element] = field(default_factory=list)
allowConfig: Optional[bool] = None
style: Optional[dict] = None
optionFilterProp: Optional[str] = None
_identifier: Optional[str] = None
valueKey: Optional[str] = None
labelKey: Optional[str] = None
optionCTA: Optional[Element] = None
helpText: Optional[str] = None
defaultValue: Optional[str] = None

def kind(self) -> str:
return "Atoms.SelectBox"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def withCreatable(self, key:bool):
return replace(self, creatable=key)

def withDefault(self, value: str):
return replace(self, defaultValue=value)

def withValueKey(self, key:str):
return replace(self, valueKey=key)

def withOptionCTA(self, cta: Element):
return replace(self, optionCTA=cta)

def withLabelKey(self, key:str):
return replace(self, labelKey=key)

def withDisabled(self):
return replace(self, disabled=True)

def withStyle(self, style: dict):
return replace(self, style=style)

def withAllowConfig(self):
return replace(self, allowConfig=True)

def identifier(self) -> Optional[str]:
return self._identifier

def withIdentifier(self, identifier: str):
return replace(self, _identifier=identifier)

def withFilterProp(self, filterProp: str):
return replace(self, optionFilterProp=filterProp)

def withNoContentMessage(self, msg: str):
return replace(self, notFoundContent=msg)

def bindOptionProperty(self, property: str):
return replace(self, optionProperty=property)

def withSearchEnabled(self):
return replace(self, showSearch=True)

def addFooter(self, element: Element):
if len(self.footer) == 0:
return replace(self, footer=[element])
else:
self.footer.append(element)
return replace(self, footer=self.footer)

def getTemplateElements(self):
return self.footer

def addOption(self, label: str, value: str):
if self.options is not None:
self.options.append(SelectBoxOption(label, value))
return replace(self, options=self.options)
else:
return replace(self, options=[SelectBoxOption(label, value)])

def withHelpText(self, helpText: str):
return replace(self, helpText=helpText)

def jsonProperties(self) -> dict:
properties = super(SelectBox, self).jsonProperties()
optionsJsonArray = []

if self.identifier() is not None:
properties["identifier"] = self.identifier()
if self.disabled is not None:
properties["disabled"] = self.disabled
if self.placeholder is not None:
properties["placeholder"] = self.placeholder
if self.mode is not None:
properties["mode"] = self.mode
if self.notFoundContent is not None:
properties["notFoundContent"] = self.notFoundContent
if self.style is not None:
properties["style"] = self.style
if self.allowConfig is not None:
properties["allowConfig"] = self.allowConfig
if self.showSearch is not None:
properties["showSearch"] = self.showSearch
if self.optionFilterProp is not None:
properties["optionFilterProp"] = self.optionFilterProp
if self.helpText is not None:
properties["helpText"] = self.helpText
if self.optionProperty is not None:
properties["options"] = self.optionProperty
if self.creatable is not None:
properties["creatable"] = self.creatable
elif self.options is not None:
for curOpt in self.options:
optionsJsonArray.append({"label": curOpt.label, "value": curOpt.value})
properties["options"] = optionsJsonArray

if self.valueKey is not None:
properties["valueKey"] = self.valueKey

if self.labelKey is not None:
properties["labelKey"] = self.labelKey

footerJsons = []
for footer in self.footer:
footerJsons.append(footer.json())

if self.optionCTA is not None:
properties["optionCTA"] = self.optionCTA.json()

if self.defaultValue is not None:
properties["defaultValue"] = self.defaultValue

properties["footer"] = footerJsons
return properties


@dataclass
class NumberBox(Atom):
titleVar: str
propertyVar: Optional[str] = None
minValueVar: Optional[int] = None
maxValueVar: Optional[int] = None
ignoreTitle: bool = False
placeholder: str = ""
disabledView: Optional[bool] = None
textType: Optional[str] = None
allowEscapeSequence: Optional[bool] = None
_kind: str = "Atoms.NumberBox"
fieldType: Optional[str] = None
helpText: Optional[str] = None
requiredMin: bool = True
min: int = 0

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def minValue(self) -> Optional[int]:
return self.minValueVar

def maxValue(self) -> Optional[int]:
return self.maxValueVar

def kind(self) -> str:
return self._kind

def bindProperty(self, property_input: str):
return replace(self, propertyVar=property_input)

def bindPlaceholder(self, placeholder_input: str):
return replace(self, placeholder=placeholder_input)

def disabled(self):
return replace(self, disabledView=True)

def enableEscapeSequence(self):
return replace(self, allowEscapeSequence=True)

def makeFieldOptional(self):
return replace(self, _kind="SColumn", fieldType="Atoms.NumberBox")

def withHelpText(self, helpText: str):
return replace(self, helpText=helpText)

def withRequiredMin(self, requiredMin: int):
return replace(self, requiredMin=requiredMin)

def withMin(self, min: int):
return replace(self, min=min)


def jsonProperties(self) -> dict:
props = super(NumberBox, self).jsonProperties()
if not self.ignoreTitle:
props["title"] = self.title()
if self.minValue is not None:
props["min"] = self.minValue()
if self.maxValue is not None:
props["max"] = self.maxValue()
if self.disabledView is not None:
props["disabled"] = self.disabledView
if self.fieldType is not None:
props["fieldType"] = self.fieldType
if self.textType is not None:
props["type"] = self.textType
if self.allowEscapeSequence is not None:
props["allowEscapeSequence"] = self.allowEscapeSequence
if self.helpText is not None:
props["helpText"] = self.helpText

props["requiredMin"] = self.requiredMin
props["min"] = self.min
props["placeholder"] = self.placeholder
return props


@dataclass
class Markdown(Atom):
valueVar: str
propertyVar: Optional[str] = None
_kind: str = "Atoms.Markdown"

def title(self) -> str:
return ""

def value(self) -> str:
return self.valueVar

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return self._kind

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def jsonProperties(self) -> dict:
props = super(Markdown, self).jsonProperties()
if self.valueVar is not None:
props["value"] = self.value()
return props


@dataclass
class FileUploadBox(Atom):
titleVar: str
_kind: str = "Atoms.FileUploadBox"
placeholder: str = "Upload file"
ignoreTitle: bool = False
propertyVar: Optional[str] = None

def title(self) -> str:
return self.titleVar

def kind(self) -> str:
return self._kind

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindPlaceholder(self, placeHolder: str):
return replace(self, placeholder=placeHolder)

def property(self) -> Optional[str]:
return self.propertyVar

def jsonProperties(self) -> dict:
props = super(FileUploadBox, self).jsonProperties()
if not self.ignoreTitle:
props["title"] = self.title()
props["placeholder"] = self.placeholder
return props


@dataclass
class ConfigText(Atom):
titleVar: str
propertyVar: Optional[str] = None
ignoreTitle: bool = False
_kind: str = "Atoms.ConfigText"
placeholder: str = "target_column"
disabledView: Optional[Union[list, bool]] = None
textType: Optional[str] = None
allowEscapeSequence: Optional[bool] = None
allowConfig: Optional[bool] = None
fieldType: Optional[str] = None
allowComposite: Optional[bool] = None
rows: Optional[int] = None
resetTrigger: Optional[str] = None
width: Optional[str] = None
helpText: Optional[str] = None

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return self._kind

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def withWidth(self, width: str):
return replace(self, width=width)

def withResetTrigger(self, property: str):
return replace(self, resetTrigger=property)

def bindPlaceholder(self, placeHolder: str):
return replace(self, placeholder=placeHolder)

def disabled(self):
return replace(self, disabledView=True)

def enableEscapeSequence(self):
return replace(self, allowEscapeSequence=True)

def isPassword(self):
return replace(self, textType="password")

def withRows(self, rows: int):
return replace(self, rows=rows)

def makeFieldOptional(self):
return replace(self, _kind="SColumn", fieldType="Atoms.ConfigText")

def withAllowConfig(self):
return replace(self, allowConfig=True)

def withAllowComposite(self, flag: bool = True):
return replace(self, allowComposite=flag)

def withHelpText(self, helpText: str):
return replace(self, helpText=helpText)


def jsonProperties(self) -> dict:
props = super(ConfigText, self).jsonProperties()
if not self.ignoreTitle:
props["title"] = self.title()
if self.disabledView is not None:
props["disabled"] = self.disabledView
if self.fieldType is not None:
props["fieldType"] = self.fieldType
if self.textType is not None:
props["type"] = self.textType
if self.allowEscapeSequence is not None:
props["allowEscapeSequence"] = self.allowEscapeSequence
if self.allowConfig is not None:
props["allowConfig"] = self.allowConfig
if self.allowComposite is not None:
props["allowComposite"] = self.allowComposite
if self.rows is not None:
props["rows"] = self.rows
if self.helpText is not None:
props["helpText"] = self.helpText
if self.resetTrigger is not None:
props["resetTrigger"] = self.resetTrigger
if self.width is not None:
props["width"] = self.width
props["placeholder"] = self.placeholder
return props



@dataclass
class SqlSecretSelector(Atom):
titleVar: str
_kind: str = "Atoms.SqlSecretSelector"
placeholder: str = "Upload file"
ignoreTitle: bool = False
propertyVar: Optional[str] = None

def title(self) -> str:
return self.titleVar

def kind(self) -> str:
return self._kind

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindPlaceholder(self, placeHolder: str):
return replace(self, placeholder=placeHolder)

def property(self) -> Optional[str]:
return self.propertyVar

def jsonProperties(self) -> dict:
props = super(SqlSecretSelector, self).jsonProperties()
if not self.ignoreTitle:
props["title"] = self.title()
props["placeholder"] = self.placeholder
return props

@dataclass
class SqlFileSystemUpload(Atom):
titleVar: str
_kind: str = "Atoms.SqlFileSystemUpload"
formats: list = ("xlsx")
placeholder: str = "Upload formatted XLSX"
ignoreTitle: bool = False
propertyVar: Optional[str] = None

def title(self) -> str:
return self.titleVar

def kind(self) -> str:
return self._kind

def propertyKey(self) -> str:
return "path"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindPlaceholder(self, placeHolder: str):
return replace(self, placeholder=placeHolder)

def property(self) -> Optional[str]:
return self.propertyVar

def jsonProperties(self) -> dict:
props = super(SqlFileSystemUpload, self).jsonProperties()
if not self.ignoreTitle:
props["title"] = self.title()
props["placeholder"] = self.placeholder
props["formats"] = self.formats
return props

@dataclass
class ConnectionDropdown(Atom):
connectionKind: str
titleVar: str
_kind: str = "Atoms.ConnectionDropdown"
placeholder: str = "Select Connection"
ignoreTitle: bool = False
propertyVar: Optional[str] = None

def title(self) -> str:
return self.titleVar

def kind(self) -> str:
return self._kind

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindPlaceholder(self, placeHolder: str):
return replace(self, placeholder=placeHolder)

def bindConnectionKind(self, connectionKind: str):
return replace(self, connectionKind=connectionKind)

def property(self) -> Optional[str]:
return self.propertyVar

def jsonProperties(self) -> dict:
props = super(ConnectionDropdown, self).jsonProperties()
if not self.ignoreTitle:
props["title"] = self.title()
props["placeholder"] = self.placeholder
props["connectionKind"] = self.connectionKind
return props

@enum.unique
class AddOnPlacement(enum.Enum):
left = "left"
right = "right"

def getEnum(value):
if value == "left":
return AddOnPlacement.left
elif value == "right":
return AddOnPlacement.right
else:
return AddOnPlacement.left

@dataclass
class TextBox(Atom):
titleVar: str
propertyVar: Optional[str] = None
ignoreTitle: bool = False
_kind: str = "Atoms.TextBox"
placeholder: str = "target_column"
disabledView: Optional[Union[list, bool]] = None
textType: Optional[str] = None
allowEscapeSequence: Optional[bool] = None
allowConfig: Optional[bool] = None
fieldType: Optional[str] = None
allowComposite: Optional[bool] = None
rows: Optional[int] = None
resetTrigger: Optional[str] = None
width: Optional[str] = None
helpText: Optional[str] = None
addOn: Optional[str] = None
addOnPlacement: Optional[AddOnPlacement] = None
required: Optional[bool] = None

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return self._kind

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def withWidth(self, width: str):
return replace(self, width=width)

def withResetTrigger(self, property: str):
return replace(self, resetTrigger=property)

def bindPlaceholder(self, placeHolder: str):
return replace(self, placeholder=placeHolder)

def disabled(self):
return replace(self, disabledView=True)

def enableEscapeSequence(self):
return replace(self, allowEscapeSequence=True)

def isPassword(self):
return replace(self, textType="password")

def withRows(self, rows: int):
return replace(self, rows=rows)

def makeFieldOptional(self):
return replace(self, _kind="SColumn", fieldType="Atoms.TextBox")

def withAllowConfig(self):
return replace(self, allowConfig=True)

def withAllowComposite(self, flag: bool = True):
return replace(self, allowComposite=flag)

def withHelpText(self, helpText: str):
return replace(self, helpText=helpText)

def withAddOn(self, addOn: str):
return replace(self, addOn=addOn)

# must be either 'left' or 'right'
def withAddOnPlacement(self, addOnPlacement: AddOnPlacement):
return replace(self, addOnPlacement=addOnPlacement.value)

def withRequired(self, required: bool):
return replace(self, required=required)

def jsonProperties(self) -> dict:
props = super(TextBox, self).jsonProperties()
if not self.ignoreTitle:
props["title"] = self.title()
if self.disabledView is not None:
props["disabled"] = self.disabledView
if self.fieldType is not None:
props["fieldType"] = self.fieldType
if self.textType is not None:
props["type"] = self.textType
if self.allowEscapeSequence is not None:
props["allowEscapeSequence"] = self.allowEscapeSequence
if self.allowConfig is not None:
props["allowConfig"] = self.allowConfig
if self.allowComposite is not None:
props["allowComposite"] = self.allowComposite
if self.rows is not None:
props["rows"] = self.rows
if self.helpText is not None:
props["helpText"] = self.helpText
if self.resetTrigger is not None:
props["resetTrigger"] = self.resetTrigger
if self.width is not None:
props["width"] = self.width
if self.addOn is not None:
props["addOn"] = self.addOn
if self.addOnPlacement is not None:
props["addOnPlacement"] = self.addOnPlacement
if self.required is not None:
props["required"] = self.required
props["placeholder"] = self.placeholder
return props


@dataclass
class TextArea(Atom):
titleVar: str
rows: int
propertyVar: Optional[str] = None
_kind: str = "Atoms.TextArea"
placeholder: str = ""
allowEscapeSequence: Optional[bool] = None
readOnly: bool = False
copilot: Optional[CopilotSpec] = None

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return self._kind

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindPlaceholder(self, placeHolder: str):
return replace(self, placeholder=placeHolder)

def enableEscapeSequence(self):
return replace(self, allowEscapeSequence=True)

# TODO: This is a temporary backup method. Will be removed later
def withCopilot(self, copilot: CopilotSpec):
return replace(self, copilot=copilot)
def withCopilotEnabledDescribeColumn(self):
copilotSpec = CopilotSpec(method="", copilotProps=CopilotSpecProps()).withDescribeColumn()
return replace(self, copilot=copilotSpec)

def withCopilotEnabledDescribeDataSource(self):
copilotSpec = CopilotSpec(method="", copilotProps=CopilotSpecProps()).withDescribeDataSource()
return replace(self, copilot=copilotSpec)

def jsonProperties(self) -> dict:
props = super(TextArea, self).jsonProperties()
props["title"] = self.title()
props["rows"] = self.rows
if self.allowEscapeSequence is not None:
props["allowEscapeSequence"] = self.allowEscapeSequence
props["placeholder"] = self.placeholder
if self.readOnly:
props["readOnly"] = self.readOnly
if self.copilot is not None:
props["copilot"] = self.copilot.json()
return props


@dataclass(frozen=True)
class RadioOption:
label: str
value: str
icon: Optional[str] = None
description: Optional[str] = None


@dataclass
class RadioGroup(Atom):
_title: str
propertyVar: Optional[str] = None
optionProperty: Optional[str] = None
optionType: Optional[str] = None
options: list = None
gap: Optional[str] = "1rem"
variant: Optional[str] = None
buttonStyle: Optional[str] = None
buttonSize: Optional[str] = None
iconSize: Optional[str] = None
style: Optional[dict] = None
defaultValue: Optional[str] = None
orientation: Optional[str] = None

def kind(self) -> str:
return "Atoms.RadioGroup"

def title(self) -> str:
return self._title

def property(self) -> Optional[str]:
return self.propertyVar

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindOptionProperty(self, property: str):
return replace(self, optionProperty=property)

def addOption(self, label: str, value: str, icon: Optional[str] = None, description: Optional[str] = None
):
if self.options is not None:
self.options.append(RadioOption(label, value, icon, description))
return replace(self, options=self.options)
else:
return replace(self, options=[RadioOption(label, value, icon, description)])

def setOptionType(self, optionType: str):
return replace(self, optionType=optionType)

def setVariant(self, variant: str):
return replace(self, variant=variant)

def setButtonStyle(self, buttonStyle: str):
return replace(self, buttonStyle=buttonStyle)

def setButtonSize(self, buttonSize: str):
return replace(self, buttonSize=buttonSize)

def jsonProperties(self) -> dict:
properties = super(RadioGroup, self).jsonProperties()
if self.optionProperty is not None:
properties["options"] = self.optionProperty
elif self.options is not None:
optionsJsonArray = []
for opt in self.options:
optionsJsonArray.append({
"label": opt.label, "value": opt.value, "icon": opt.icon, "description": opt.description
})
properties["options"] = optionsJsonArray
if self.gap is not None:
properties["gap"] = self.gap
if self.variant is not None:
properties["variant"] = self.variant
if self.optionType is not None:
properties["optionType"] = self.optionType
if self.buttonStyle is not None:
properties["buttonStyle"] = self.buttonStyle

if self.buttonSize is not None:
properties["buttonSize"] = self.buttonSize
if self.iconSize is not None:
properties["iconSize"] = self.iconSize
if self.style is not None:
properties["style"] = self.style
if self.defaultValue is not None:
properties["defaultValue"] = self.defaultValue
if self.orientation is not None:
properties["orientation"] = self.orientation

return properties


@dataclass
class NativeText(Atom):
titleVar: str
propertyVar: Optional[str] = None

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return "Atoms.NativeText"

def bindProperty(self, property: str):
return self

def jsonProperties(self) -> dict:
properties = super(NativeText, self).jsonProperties()
properties["value"] = self.title()
return properties

@dataclass
class Editor(Atom):
height: Optional[str] = "100%"
language: Optional[str] = "${$.workflow.metainfo.frontEndLanguage}"
_kind: str = "Atoms.Editor"
ports: Optional[str] = None
propertyVar: Optional[str] = None
fieldType: Optional[str] = None
visualBuilder: Optional[VisualBuilderSpec] = None


def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return self._kind

def title(self) -> str:
return "Editor"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindLanguage(self, lang: str):
return replace(self, language=lang)

def withSchemaSuggestions(self):
return replace(self, ports="component.ports.inputs")

def makeFieldOptional(self):
return replace(self, _kind="SColumn", fieldType="Atoms.Editor")

def withVisualBuilderSpec(self, visualBuilder: VisualBuilderSpec):
return replace(self, visualBuilder=visualBuilder)

def withExpressionBuilder(self, visualBuilderType: List[ExpressionBuilderType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedTypes=visualBuilderType))

def withUnsupportedExpressionBuilderTypes(self, unsupportedTypes: List[ExpressionBuilderType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
unsupportedTypes=unsupportedTypes))

def withGroupBuilder(self, groupBuilderType: GroupBuilderType):
return replace(self,
visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), builderType=groupBuilderType))

def withBlockType(self, expressionBlockType: ExpressionBlockType):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedBlockType=expressionBlockType))

def withValueType(self, valueType: ExpressionValueType):
return replace(self,
visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), supportedValueType=valueType))

def withFunctionTypes(self, functionTypes: List[ExpressionFunctionType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedFunctionTypes=functionTypes))

def withConfigTypes(self, configTypes: List[ExpressionConfigType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedConfigTypes=configTypes))

def withBorder(self, border: bool):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), border=border))

def withVisualPlaceholder(self, placeholder: str):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), placeholder=placeholder))

def jsonProperties(self) -> dict:
props = {"title": self.title(), "language": self.language}
if self.height is not None:
props["height"] = self.height
else:
props["height"] = "100px"
if self.fieldType is not None:
props["fieldType"] = self.fieldType
if self.ports is not None:
props["ports"] = self.propertyPath(self.ports)
if self.visualBuilder is not None:
props["visualBuilder"] = self.visualBuilder.json()
return props


@enum.unique
class Gem(enum.Enum):
DiamondPurple = "DiamondPurple"
TrillionOrange = "TrillionOrange"

@staticmethod
def get_enum(value):
if value == "DiamondPurple":
return Gem.DiamondPurple
elif value == "TrillionOrange":
return Gem.TrillionOrange


@enum.unique
class ButtonVariant(enum.Enum):
primary = "primary"
secondary = "secondary"
secondaryGrey = "secondaryGrey"
tertiary = "tertiary"
tertiaryGrey = "tertiaryGrey"
link = "link"
linkGrey = "linkGrey"
plain = "plain"

def getEnum(value):
if value == "primary":
return ButtonVariant.primary
elif value == "secondary":
return ButtonVariant.secondary
elif value == "secondaryGrey":
return ButtonVariant.secondaryGrey
elif value == "tertiary":
return ButtonVariant.tertiary
elif value == "tertiaryGrey":
return ButtonVariant.tertiaryGrey
elif value == "link":
return ButtonVariant.link
elif value == "linkGrey":
return ButtonVariant.linkGrey
else:
return ButtonVariant.plain


@enum.unique
class ButtonSize(enum.Enum):
xs = "xs"
s = "s"
m = "m"
l = "l"
xl = "xl"

def getEnum(value):
if value == "xs":
return ButtonSize.xs
elif value == "s":
return ButtonSize.s
elif value == "m":
return ButtonSize.m
elif value == "l":
return ButtonSize.l
else:
return ButtonSize.xl


@enum.unique
class ButtonShape(enum.Enum):
default = "default"
circle = "circle"

def getEnum(value):
if value == "default":
return ButtonShape.default
else:
return ButtonShape.circle


@enum.unique
class FontLevel(enum.Enum):
xl = "xl"
lg = "lg"
md = "md"
sm15 = "sm15"
sm = "sm"
sm13 = "sm13"
xs = "xs"
xxs = "xxs"

def getEnum(value):
if value == "xs":
return FontLevel.xs
elif value == "lg":
return FontLevel.lg
elif value == "md":
return FontLevel.md
elif value == "sm15":
return FontLevel.sm15
elif value == "sm":
return FontLevel.sm
elif value == "sm13":
return FontLevel.sm13
elif value == "xxs":
return FontLevel.xxs
else:
return FontLevel.xl


@dataclass
class Button(Container[Element]):
titleVar: str
variant: Optional[ButtonVariant] = ButtonVariant.secondaryGrey
shape: Optional[ButtonShape] = None
size: Optional[ButtonSize] = None
style: Optional[dict] = None
_children: list = None
onClick: Optional = None
danger: Optional[bool] = None
block: Optional[bool] = None
href: Optional[str] = None

def jsonProperties(self) -> dict:
properties = super(Button, self).jsonProperties()
if self.variant is not None:
properties["variant"] = self.variant.value
if self.shape is not None:
properties["shape"] = self.shape.value
if self.size is not None:
properties["size"] = self.size.value
if self.style is not None:
properties["style"] = self.style
if self.danger is not None:
properties["danger"] = self.danger
if self.block is not None:
properties["block"] = self.block
if self.onClick is not None:
properties["actions"] = ["onButtonClick"]
if self.href is not None:
properties["href"] = self.href
return properties

def bindOnClick(self, onClick):
return replace(self, onClick=onClick)

def kind(self) -> str:
return "Atoms.Button"

def title(self):
return self.titleVar

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def withHref(self, href: str):
return replace(self, href=href)

def addElement(self, child: Element):
if self._children is None:
return replace(self, _children=[child])
else:
self._children.insert(0, child)
return replace(self, _children=self._children)


@dataclass
class Step(Container[Element]):
_children: List[Element] = field(default_factory=list)
grow: Optional[int] = 0
shrink: Optional[int] = 1
basis: Optional[str] = "auto"
style: Optional[Dict[str, str]] = None
align: Optional[str] = None
padding: Optional[str] = None

def kind(self) -> str:
return "Layouts.Step"

def addElement(self, child: Element):
self._children.append(child)
return self

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def jsonProperties(self) -> dict:
properties = super(Step, self).jsonProperties()
if self.grow is not None:
properties["grow"] = self.grow
if self.shrink is not None:
properties["shrink"] = self.shrink
if self.basis is not None:
properties["basis"] = self.basis
if self.style is not None:
properties["style"] = self.style
if self.align is not None:
properties["align"] = self.align
if self.padding is not None:
properties["padding"] = self.padding

return properties

@dataclass
class StepContainer(Container[Element]):
gap: Optional[str] = "1rem"
direction: Optional[str] = None
align: Optional[str] = None
width: Optional[str] = None
alignY: Optional[str] = None
height: Optional[str] = None
_children: List[Element] = field(default_factory=list)
padding: Optional[str] = None
style: Optional[Dict[str, str]] = None
template: Optional[Element] = None

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def kind(self) -> str:
return "Layouts.StepContainer"

def addElement(self, child: Element):
return replace(self, _children=self._children + [child])

def addStackItem(self, child: Element):
return replace(self, _children=self._children + [child])

def addTemplate(self, template: Element):
return replace(self, template=template)

def jsonProperties(self) -> dict:
properties = super(StepContainer, self).jsonProperties()
if self.gap is not None:
properties["gap"] = self.gap
if self.direction is not None:
properties["direction"] = self.direction
if self.align is not None:
properties["align"] = self.align
if self.width is not None:
properties["width"] = self.width
if self.alignY is not None:
properties["alignY"] = self.alignY
if self.height is not None:
properties["height"] = self.height
if self.template is not None:
properties["template"] = self.template.json()
if self.style is not None:
properties["style"] = self.style
if self.padding is not None:
properties["padding"] = self.padding

return properties

class JoinIconEnum(Enum):
INNER_JOIN = "InnerJoin"
LEFT_OUTER_JOIN = "LeftOuterJoin"
RIGHT_OUTER_JOIN = "RightOuterJoin"
FULL_OUTER = "FullOuter"
CROSS_JOIN = "CrossJoin"
DEFAULT = "Default"
NATURAL_INNER_JOIN = "NaturalInnerJoin"
NATURAL_LEFT_OUTER_JOIN = "NaturalLeftOuterJoin"
NATURAL_RIGHT_OUTER_JOIN = "NaturalRightOuterJoin"
NATURAL_FULL_OUTER = "NaturalFullOuter"


@dataclass
class JoinType():
displayName: str
name: str
description: str
icon: JoinIconEnum = JoinIconEnum.DEFAULT,
tooltip: Optional[str] = None

def json(self):
properties = {}
properties["displayName"] = self.displayName
properties["name"] = self.name
properties["description"] = self.description
properties["icon"] = self.icon.value
if self.tooltip is not None:
properties["tooltip"] = self.tooltip
return properties


@dataclass
class JoinConditionsAtom(Atom):
value: str
headAlias: str
joinTypes: List[JoinType] = field(default_factory=list)
propertyVar: Optional[str] = None

def title(self) -> str:
return ""

def kind(self) -> str:
return "Atoms.JoinConditions"

def property(self) -> Optional[str]:
return self.propertyVar

def bindValue(self, value: str):
return replace(self, value=value)

def bindProperty(self, value: str):
return replace(self, value=value)

def bindHeadAlias(self, headAlias: str):
return replace(self, headAlias=headAlias)

def bindJoinTypes(self, joinTypes: List[JoinType]):
return replace(joinTypes=joinTypes)

def addJoinType(self, joinType: JoinType):
return replace(self, joinTypes=self.joinTypes + [joinType])

def jsonProperties(self) -> dict:
properties = super(JoinConditionsAtom, self).jsonProperties()
properties["value"] = self.value
properties["headAlias"] = self.headAlias
properties["joinTypes"] = [jt.json() for jt in self.joinTypes]
return properties

@dataclass
class IDELinkButton(Button):
ide:Optional[str] = None
projectId:Optional[str] = None
entityId:Optional[str] = None
action:Optional[str] = None
entityType:Optional[str] = None
openInNewTab:Optional[bool] = None
navigatingTo:Optional[str] = None

def kind(self) -> str:
return "Atoms.IDELinkButton"

def withOpenInNewTab(self, openInNewTab: bool):
return replace(self, openInNewTab=openInNewTab)

def withIDE(self, ide: str):
return replace(self, ide=ide)

def withEntityType(self, entityType: str):
return replace(self, entityType=entityType)

def withProjectId(self, projectId: str):
return replace(self, projectId=projectId)

def withEntityId(self, entityId: str):
return replace(self, entityId=entityId)

def withAction(self, action: str):
return replace(self, action=action)

def withNavigateTo(self, msg: str):
return replace(self, navigatingTo=msg)


def jsonProperties(self) -> dict:
properties = super(IDELinkButton, self).jsonProperties()
if self.ide is not None:
properties["ide"] = self.ide
if self.projectId is not None:
properties["projectId"] = self.projectId
if self.entityId is not None:
properties["entityId"] = self.entityId
if self.action is not None:
properties["action"] = self.action
if self.entityType is not None:
properties["entityType"] = self.entityType
if self.openInNewTab is not None:
properties["openInNewTab"] = self.openInNewTab
if self.navigatingTo is not None:
properties["navigatingTo"] = self.navigatingTo

return properties


@dataclass
class ProphecyIcon(Atom):
type: Optional[str] = None
iconName: Optional[str] = None
color: Optional[str] = None

def title(self) -> str:
return ""

def property(self) -> Optional[str]:
pass

def bindProperty(self, property: str):
pass

def kind(self) -> str:
return "Atoms.ProphecyIcon"

def withType(self, type: str):
return replace(self, type=type)

def withIconName(self, iconName: str):
return replace(self, iconName=iconName)

def withColor(self, color: str):
return replace(self, color=color)

def jsonProperties(self) -> dict:
properties = super(ProphecyIcon, self).jsonProperties()
if self.type is not None:
properties["type"] = self.type
if self.iconName is not None:
properties["iconName"] = self.iconName
if self.color is not None:
properties["color"] = self.color
return properties

@dataclass
class FabricIcon(Atom):
providerType: Optional[str] = None
provider: Optional[str] = None

def title(self) -> str:
return ""

def property(self) -> Optional[str]:
pass

def bindProperty(self, property: str):
pass

def kind(self) -> str:
return "Atoms.FabricIcon"


def withProviderType(self, providerType: str):
return replace(self, providerType=providerType)

def withProvider(self, provider: str):
return replace(self, provider=provider)

def jsonProperties(self) -> dict:
properties = super(FabricIcon, self).jsonProperties()
if self.providerType is not None:
properties["providerType"] = self.providerType
if self.provider is not None:
properties["provider"] = self.provider
return properties
@dataclass
class Code(Atom):
titleVar: str
propertyVar: Optional[str] = None

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return "Atoms.Code"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

@dataclass
class CodeBlock(Atom):
titleVar: str
propertyVar: Optional[str] = None
languageVar: Optional[str] = None

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return "Atoms.CodeBlock"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindCodeLanguage(self, language: str):
return replace(self, languageVar=language)

def propertyKey(self) -> str:
return "code"

def jsonProperties(self) -> dict:
properties = super(CodeBlock, self).jsonProperties()
if self.languageVar is not None:
properties["language"] = self.languageVar
return properties


@dataclass
class PortSchemaType:
nameVar: str = ""

def name(self) -> str:
return self.nameVar


@enum.unique
class PortSchemaTypeEnum(enum.Enum):
InputSchema = "in"
OutputSchema = "out"
AnySchema = "Any"

def getEnum(value):
if value == "Input":
return PortSchemaTypeEnum.InputSchema
elif value == "Output":
return PortSchemaTypeEnum.OutputSchema
else:
return PortSchemaTypeEnum.AnySchema


# This represents the new Ports Atom which encapsulates both input & output tabs.
# This is so that UI can have better control on rendering them.
@dataclass
class Ports(Atom):
propertyVar: Optional[str] = None
singleColumnClickCallback: Optional = None
allColumnsSelectionCallback: Optional = None
minInputPorts: Union[int, str] = 0
minOutputPorts: Union[int, str] = 0
allowInputRename: Union[bool, str] = False
allowOutputRename: Union[bool, str] = False
selectedFieldsProperty: Optional[str] = None
inputPorts: str = "${component.ports.inputs}"
outputPorts: str = "${component.ports.outputs}"
inputNoFieldsMessage: str = "Please connect input ports and fix upstream gems to see schema"
outputNoFieldsMessage: str = "Please fix gem errors and upstream gems to see schema"
allowInputAddOrDelete: Union[bool, str] = False
allowOutputAddOrDelete: Union[bool, str] = False
allowCustomOutputSchema: bool = True
defaultCustomOutputSchema: bool = False
allowInputSelection: Optional[bool] = None
allowInputSelectionProperty: Optional[str] = None

def property(self) -> Optional[str]:
return self.propertyVar

def title(self) -> str:
return "Schema"

def kind(self) -> str:
return "Ports"

def bindSelectedFieldsProperty(self, property: str):
return replace(self, selectedFieldsProperty=property)

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def allowColumnClickBasedOn(self, propertyPath: str):
return replace(self, allowInputSelectionProperty=(propertyPath))

def editableInput(self, editableFlag: bool):
return replace(self, allowInputAddOrDelete=editableFlag, allowInputRename=editableFlag)

def jsonProperties(self) -> dict:
properties = super(Ports, self).jsonProperties()
properties["id"] = self.id

actions = list()
if self.singleColumnClickCallback is not None:
actions.append("onColumnClick")
if self.allColumnsSelectionCallback is not None:
actions.append("onSelectAllColumns")
if len(actions) > 0:
properties["actions"] = actions

properties["minInputPorts"] = self.minInputPorts
properties["minOutputPorts"] = self.minOutputPorts
properties["allowInputRename"] = self.allowInputRename
properties["allowOutputRename"] = self.allowOutputRename

if self.selectedFieldsProperty is not None:
properties["selectedFields"] = self.propertyPath(self.selectedFieldsProperty)

properties["inputPorts"] = self.inputPorts
properties["outputPorts"] = self.outputPorts
properties["inputNoFieldsMessage"] = self.inputNoFieldsMessage
properties["outputNoFieldsMessage"] = self.outputNoFieldsMessage
properties["allowInputAddOrDelete"] = self.allowInputAddOrDelete
properties["allowOutputAddOrDelete"] = self.allowOutputAddOrDelete
properties["allowCustomOutputSchema"] = self.allowCustomOutputSchema
properties["defaultCustomOutputSchema"] = self.defaultCustomOutputSchema
properties["isCustomOutputSchema"] = self.propertyPath("component.ports.isCustomOutputSchema")

if self.allowInputSelection is not None:
properties["allowInputSelection"] = self.allowInputSelection
elif self.allowInputSelectionProperty is not None:
properties["allowInputSelection"] = self.propertyPath(self.allowInputSelectionProperty)
elif (self.singleColumnClickCallback is not None) or (self.allColumnsSelectionCallback is not None):
properties["allowInputSelection"] = True

return properties


@dataclass
class PortSchema(Atom):
schemaType: PortSchemaTypeEnum = PortSchemaTypeEnum.AnySchema
selectedFieldsProperty: Optional[str] = None
propertyVar: Optional[str] = None
ports: Optional[str] = None
minPorts: Union[int, str] = 0
allowSelection: Optional[bool] = False
onColumnClicked: Optional = None
allowRename: Union[bool, str] = False
onAllColumnsClicked: Optional = None
allowAddOrDelete: Union[bool, str] = False
noSchemaMessage: str = "Schema not found"
selectionProperty: Optional[str] = None
isCustomOutputSchema: Optional[str] = None
allowCustomOutputSchema: bool = True
defaultCustomOutputSchema: bool = False

def property(self) -> Optional[str]:
return self.propertyVar

def title(self) -> str:
return "Schema"

def kind(self) -> str:
return "PortSchema"

def bindSelectedFieldsProperty(self, property: str):
return replace(self, selectedFieldsProperty=property)

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindOnColumnClicked(self, callbackFunc):
return replace(self, onColumnClicked=callbackFunc)

def bindOnAllColumnsClicked(self, callbackFunc):
return replace(self, onAllColumnsClicked=callbackFunc)

def withRenamePortsEnabled(self, allowRename: Union[bool, str] = True):
return replace(self, allowRename=allowRename)

def withMinimumPorts(self, minNumberOfPorts: Union[int, str] = 0):
return replace(self, minPorts=minNumberOfPorts)

def withAddOrDeletePortsEnabled(self, allowAddDelete: Union[bool, str] = True):
return replace(self, allowAddOrDelete=allowAddDelete)

# The idea is that column-clicking can be allowed by
# either setting the allowSelectionFlag to true/false
# OR by setting the allowColumnSelectionProperty to a Property (a jsonpath in the component) which evaluates to
# either true or false
# eg, in simple components, you'd know if you want to allow user to click or not on the column name in portschema
# but for something like aggregate, you may want to only allow it when the active tab is a specific tab
# The key idea is that only one of these should have a bool value, the other should be none
def asInput(self, allowSelectionFlag: Optional[bool] = None, allowColumnSelectionProperty: Optional[str] = None):
return replace(self,
schemaType=PortSchemaTypeEnum.InputSchema,
ports="component.ports.inputs",
allowSelection=allowSelectionFlag,
selectionProperty=allowColumnSelectionProperty,
selectedFieldsProperty="component.ports.selectedInputFields",
noSchemaMessage="Please connect input ports and fix upstream gems to see schema")

def asOutput(self):
return replace(self, schemaType=PortSchemaTypeEnum.OutputSchema, ports="component.ports.outputs",
noSchemaMessage="Please fix gem errors and upstream gems to see schema",
isCustomOutputSchema="component.ports.isCustomOutputSchema")

def jsonProperties(self) -> dict:
properties = super(PortSchema, self).jsonProperties()
properties["type"] = self.schemaType.value
properties["id"] = self.id
properties["noFieldsMessage"] = "Please connect port to see columns"
if (self.selectionProperty is not None):
properties["allowSelection"] = self.propertyPath(self.selectionProperty)
elif (self.allowSelection is not None):
properties["allowSelection"] = self.allowSelection

if isinstance(self.allowRename, bool):
properties["allowRename"] = self.allowRename
elif isinstance(self.allowRename, str):
properties["allowRename"] = self.propertyPath(self.allowRename)

if isinstance(self.minPorts, int):
properties["minPorts"] = self.minPorts
elif isinstance(self.minPorts, str):
properties["minPorts"] = self.propertyPath(self.minPorts)

if isinstance(self.allowAddOrDelete, bool):
properties["allowAddOrDelete"] = self.allowAddOrDelete
elif isinstance(self.allowAddOrDelete, str):
properties["allowAddOrDelete"] = self.propertyPath(self.allowAddOrDelete)

properties["noFieldsMessage"] = self.noSchemaMessage
if self.selectedFieldsProperty is not None:
properties["selectedFields"] = self.propertyPath(self.selectedFieldsProperty)
if self.ports is not None:
properties["ports"] = self.propertyPath(self.ports)
actions = list()
if self.onColumnClicked is not None:
actions.append("onColumnClick")
if self.onAllColumnsClicked is not None:
actions.append("onSelectAllColumns")
if self.isCustomOutputSchema is not None:
properties["isCustomOutputSchema"] = self.propertyPath(self.isCustomOutputSchema)
if len(actions) > 0:
properties["actions"] = actions
properties["allowCustomOutputSchema"] = self.allowCustomOutputSchema
properties["defaultCustomOutputSchema"] = self.defaultCustomOutputSchema
return properties


@dataclass
class FileEditor(Atom):
newFileLanguage: Optional[str] = None
height: Optional[str] = "100%"
newFilePrefix: Optional[str] = "out"
propertyVar: Optional[str] = None
files: Optional[str] = None
minFiles: int = 0
allowRename: bool = False
allowAddOrDelete: bool = False
placeholder: dict = dict(),
ports: Optional[str] = None
mode: Optional[str] = "Normal"

def property(self) -> Optional[str]:
return self.propertyVar

def title(self) -> str:
return "FileEditor"

def kind(self) -> str:
return "FileEditor"

def propertyKey(self) -> str:
return "files"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def withMinFiles(self, minFiles: int):
return replace(self, minFiles=minFiles)

def allowFileRenames(self):
return replace(self, allowRename=True)

def allowFileAddDelete(self):
return replace(self, allowAddOrDelete=True)

def withExpressionMode(self):
return replace(self, mode="Expression")

def bindPlaceholders(self, languagePlaceholders=None):
if languagePlaceholders is None:
languagePlaceholders = {
"scala": """concat(col("source_column"), col("other_column"))""",
"python": """concat(col("source_column"), col("other_column"))""",
"sql": """concat(source_column, other_column)"""}
return replace(self, placeholder=languagePlaceholders.copy())

def bindPorts(self, ports: str):
return replace(self, ports=ports)

def withSchemaSuggestions(self):
return self.bindPorts("component.ports.inputs")

def jsonProperties(self) -> dict:
properties = super(FileEditor, self).jsonProperties()

if self.newFilePrefix is not None:
properties["newFilePrefix"] = self.newFilePrefix

if self.newFileLanguage is not None:
properties["newFileLanguage"] = self.newFileLanguage

if self.height is not None:
properties["height"] = self.height

if self.minFiles is not None:
properties["minFiles"] = self.minFiles

if self.allowRename is not None:
properties["allowRename"] = self.allowRename

if self.allowAddOrDelete is not None:
properties["allowAddOrDelete"] = self.allowAddOrDelete

properties["placeholder"] = self.placeholder

if self.ports is not None:
properties["ports"] = self.propertyPath(self.ports)

if self.mode is not None:
properties["mode"] = self.mode

return properties


@dataclass
class FileBrowser(Atom):
propertyVar: Optional[str] = None
showExecutionError: bool = True

def property(self) -> Optional[str]:
return self.propertyVar

def title(self) -> str:
return "FileBrowser"

def kind(self) -> str:
return "FileBrowser"

def propertyKey(self) -> str:
return "path"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def hideExecutionErrors(self):
return replace(self, showExecutionError=False)

def jsonProperties(self) -> dict:
prop = super(FileBrowser, self).jsonProperties()
prop["showExecutionError"] = self.showExecutionError
return prop


@dataclass
class HeaderText(Atom):
text: str

def kind(self) -> str:
return "Atoms.HeaderText"

def title(self) -> str:
return "HeaderText"

def property(self) -> Optional[str]:
return None

def bindProperty(self, property: str):
return self


@dataclass
class SchemaEditor(Atom):
propertyVar: Optional[str] = None

def title(self) -> str:
return "SchemaEditor"

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return "Atoms.SchemaEditor"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)


@dataclass
class InferSchemaButton(Atom):
def title(self) -> str:
return "InferSchemaButton"

def kind(self) -> str:
return "Atoms.InferSchemaButton"

def property(self) -> Optional[str]:
return None

def bindProperty(self, property: str):
return self


@dataclass
class PreviewDataButton(Atom):
def title(self) -> str:
return "PreviewDataButton"

def kind(self) -> str:
return "Atoms.PreviewDataButton"

def property(self) -> Optional[str]:
return None

def bindProperty(self, property: str):
return self


@dataclass
class PreviewTable(Atom):
titleVar: str = "PreviewTable"
propertyVar: Optional[str] = None

def title(self) -> str:
return self.titleVar

def propertyKey(self) -> str:
return "schema"

def kind(self) -> str:
return "PreviewTable"

def property(self) -> Optional[str]:
return self.propertyVar

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def jsonProperties(self) -> dict:
return super(PreviewTable, self).jsonProperties()

@dataclass
class Section(Atom):
borderColor: Optional[str] = None
thickness: Optional[str] = None
propertyVar: Optional[str] = None

def title(self) -> str:
return ""

def property(self) -> Optional[str]:
return self.propertyVar

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def kind(self) -> str:
return "Atoms.Section"

def withBorderColor(self, borderColor: str):
return replace(self, borderColor=borderColor)

def withThickness(self, thickness: str):
return replace(self, thickness=thickness)

def jsonProperties(self) -> dict:
properties = super(Section, self).jsonProperties()
if self.borderColor is not None:
properties["borderColor"] = self.borderColor
if self.thickness is not None:
properties["thickness"] = self.thickness
return properties

@dataclass
class ConnectionResourceBrowser(Atom):
connectionId: str
databaseLabel: Optional[str] = None
schemaLabel: Optional[str] = None
tableLabel: Optional[str] = None
filePathLabel: Optional[str] = None
database: Optional[ConfigText] = None
schema: Optional[ConfigText] = None
table: Optional[ConfigText] = None
filePath: Optional[ConfigText] = None
propertyVar: Optional[str] = None

def title(self) -> str:
return "ConnectionResourceBrowser"

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def kind(self) -> str:
return "Atoms.ConnectionResourceBrowser"

def property(self) -> Optional[str]:
return self.propertyVar

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def withDatabase(self, element: ConfigText):
return replace(self, database=element)

def withSchema(self, element: ConfigText):
return replace(self, schema=element)

def withTable(self, element: ConfigText):
return replace(self, table=element)

def withFilePath(self, element: ConfigText):
return replace(self, filePath=element)

def jsonProperties(self) -> dict:
properties = super(ConnectionResourceBrowser, self).jsonProperties()
properties["connectionId"] = self.connectionId
if self.databaseLabel is not None:
properties["databaseLabel"] = self.databaseLabel
if self.schemaLabel is not None:
properties["schemaLabel"] = self.schemaLabel
if self.tableLabel is not None:
properties["tableLabel"] = self.tableLabel
if self.filePathLabel is not None:
properties["filePathLabel"] = self.filePathLabel
if self.database is not None:
properties["database"] = self.database.json()
if self.schema is not None:
properties["schema"] = self.schema.json()
if self.table is not None:
properties["table"] = self.table.json()
if self.filePath is not None:
properties["filePath"] = self.filePath.json()

return properties

''' ------------------------------------ DIALOG -------------------------------------'''


class DialogElement(Element, ABC):
pass


@dataclass
class DialogTitle(DialogElement, Atom):
titleVar: str
propertyVar: Optional[str] = None

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return "Dialogs.Title"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def jsonProperties(self) -> dict:
properties = super(DialogTitle, self).jsonProperties()
properties["title"] = self.propertyPath("component.metadata.label")
return properties


@dataclass
class DialogContent(DialogElement, Container[Element]):
_children: list

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def kind(self) -> str:
return "Dialogs.Content"


@dataclass
class DialogFooter(DialogElement, Container[Element]):
_children: list = None

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def kind(self) -> str:
return "Dialogs.Footer"


@dataclass
class SubgraphDialogFooter(DialogElement, Container[Element]):
_children: list = None

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def kind(self) -> str:
return "Atoms.SubgraphDialogFooter"


@dataclass
class Dialog(Container[DialogElement]):
title: str
contentChildren: list = None
footer: Element = field(default_factory=DialogFooter)
copilot: Optional[CopilotSpec] = None

def kind(self) -> str:
return "Dialogs.Container"

def title(self) -> str:
return self.title

def children(self) -> list:
return [
self.footer,
DialogContent(self.contentChildren),
DialogTitle(self.title)
]

def addElement(self, element: Element):
if self.contentChildren is None:
return replace(self, contentChildren=[element])
else:
self.contentChildren.insert(0, element)
return replace(self, contentChildren=self.contentChildren)

def jsonProperties(self) -> dict:
props = dict()
if self.copilot is not None:
props["copilot"] = self.copilot.json()
return props

def withCopilotEnabledAutoSuggestionProperties(self):
copilotSpec = CopilotSpec(method="copilot/suggestGemProperties", copilotProps=CopilotSpecProps()).withSuggestGemProperties().withAutoSuggestUpdate()
return replace(self, copilot=copilotSpec)


''' ------------------------------------ DATASET DIALOG -----------------------------'''


@dataclass
class DatasetDialogSection(Container[Element]):
title: str
spec: Optional[Element] = None

def kind(self) -> str:
return "DatasetDialogs.Section"

def children(self) -> list:
if self.spec is not None:
return [self.spec]
else:
return []

def jsonProperties(self) -> dict:
properties = super(DatasetDialogSection, self).jsonProperties()
if self.spec is not None:
properties["spec"] = self.spec.json()
return properties

def json(self) -> dict:
properties = self.jsonProperties()
properties["id"] = self.id
properties["title"] = self.title
return properties


@dataclass
class DatasetDialog(Container[DatasetDialogSection]):
title: str
sections: list = None

def kind(self) -> str:
return "DatasetDialogs"

def children(self) -> list:
if self.sections is None:
return []
else:
return self.sections

def addSection(self, title: str, element: Element):
if self.sections is not None:
self.sections.append(DatasetDialogSection(title, element))
return replace(self, sections=self.sections)
else:
return replace(self, sections=[DatasetDialogSection(title, element)])


''' ------------------------------------ TABS ----------------------------'''


@dataclass
class TabPane(Container[Element]):
label: str
key: str
propertyVar: Optional[str] = None
_children: list = None

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def kind(self) -> str:
return "Atoms.Tabs.TabPane"

def addElement(self, element: Element):
if self._children is None:
return replace(self, _children=[element])
else:
self._children.insert(0, element)
return replace(self, _children=self._children)

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def jsonProperties(self) -> dict:
return {"tab": self.label, "key": self.key}


@dataclass
class Tab(Container[Element]):
title: str
contentChildren: list = None

def children(self) -> list:
if self.contentChildren is None:
return []
else:
return self.contentChildren

def kind(self) -> str:
return "Tabs.Tab"

def addElement(self, element: Element):
if self.contentChildren is None:
return replace(self, contentChildren=[element])
else:
self.contentChildren.insert(0, element)
return replace(self, contentChildren=self.contentChildren)


@dataclass
class SubgraphConfigurationTabs(Container[TabPane]):
propertyVar: Optional[str] = None
tabs: list = None
childrenList: list = None

def property(self) -> Optional[str]:
return self.propertyVar

def children(self) -> list:
if self.childrenList is None:
return []
else:
return self.childrenList

def addTabPane(self, tab: TabPane):
if self.tabs is None:
return replace(self, tabs=[tab])
else:
self.tabs.insert(0, tab)
return replace(self, tabs=self.tabs)

def kind(self) -> str:
return "Atoms.SubgraphConfigurationTabs"

def jsonProperties(self) -> dict:
tab_jsons = []
tabs = self.tabs if self.tabs is not None else []
for tab in reversed(tabs):
tab_jsons.append(tab.json())
return {"tabs": tab_jsons}


@dataclass
class Tabs(Container[TabPane]):
propertyVar: Optional[str] = None
childrenList: list = None

def property(self) -> Optional[str]:
return self.propertyVar

def children(self) -> list:
if self.childrenList is None:
return []
else:
return self.childrenList

def kind(self) -> str:
return "Atoms.Tabs"

def addTabPane(self, tab: TabPane):
if self.childrenList is None:
return replace(self, childrenList=[tab])
else:
self.childrenList.insert(0, tab)
return replace(self, childrenList=self.childrenList)

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def jsonProperties(self) -> dict:
if self.propertyVar is None:
return {}
else:
return {"activeKey": self.propertyPath(self.propertyVar)}


@dataclass
class PortSchemaTabs(Container[TabPane]):
selectedFieldsProperty: Optional[str] = None
property: Optional[str] = None
minNumberOfPorts: Union[int, str] = 0
editableInput: Union[Optional[bool], str] = None
allowSelection: bool = False
singleColumnClickCallback: Optional = None
childrenList: list = None
allowInportRename: Union[bool, str] = False
allColumnsSelectionCallback: Optional = None
allowInportAddDelete: Union[bool, str] = False
allowOutportRename: Union[bool, str] = False
editableOutput: Union[Optional[bool], str] = None
allowOutportAddDelete: Union[bool, str] = False
selectionProperty: Optional[str] = None
minNumberOfOutPorts: Union[str, Optional[int]] = None

def allowColumnClickBasedOn(self, propertyPath: str):
return replace(self, selectionProperty=(propertyPath))

def getPortSchema(self) -> PortSchema:
# propertyPath should bind to a bool property.
# If the value at propertyPath is true, then user would be able to click on column names,
# else this decision would be taken based on whether callbacks are defined.
# If yes, allow clicking, else no
if (self.selectionProperty is not None):
ip0 = PortSchema(propertyVar=self.property).asInput(
allowColumnSelectionProperty=self.selectionProperty).withMinimumPorts(self.minNumberOfPorts)
else:
isSelectionAllowed = (self.singleColumnClickCallback is not None) or (
self.allColumnsSelectionCallback is not None)
ip0 = PortSchema(propertyVar=self.property).asInput(isSelectionAllowed).withMinimumPorts(
self.minNumberOfPorts)

if self.editableInput is None:
ip1 = ip0 \
.withRenamePortsEnabled(self.allowInportRename) \
.withAddOrDeletePortsEnabled(self.allowInportAddDelete)
else:
ip1 = ip0.withRenamePortsEnabled(self.editableInput).withAddOrDeletePortsEnabled(self.editableInput)

ip2 = ip1.bindSelectedFieldsProperty(
self.selectedFieldsProperty) if self.selectedFieldsProperty is not None else ip1
ip3 = ip2.bindOnColumnClicked(
self.singleColumnClickCallback) if self.singleColumnClickCallback is not None else ip2
inputPortSchema = ip3.bindOnAllColumnsClicked(
self.allColumnsSelectionCallback) if self.allColumnsSelectionCallback is not None else ip3
return inputPortSchema

def getOutputPortSchema(self) -> PortSchema:
op0: PortSchema = PortSchema(allowSelection=self.allowSelection, propertyVar=self.property).asOutput()
if self.editableOutput is None:
op = op0 \
.withRenamePortsEnabled(self.allowOutportRename) \
.withAddOrDeletePortsEnabled(self.allowOutportAddDelete)
else:
op = op0.withRenamePortsEnabled(self.editableOutput).withAddOrDeletePortsEnabled(self.editableOutput)
if self.minNumberOfOutPorts is not None:
op = op.withMinimumPorts(self.minNumberOfOutPorts)
return op

def kind(self) -> str:
return "Atoms.Tabs"

def children(self) -> list:
if self.childrenList is None:
return []
else:
return self.childrenList

def importSchema(self) -> Tabs:
inputPortSchema = self.getPortSchema()
outputPortSchema = self.getOutputPortSchema()
return Tabs(). \
addTabPane(TabPane("Input", "Input").addElement(inputPortSchema)). \
addTabPane(TabPane("Output", "Output").addElement(outputPortSchema))


''' ------------------------------------ TABLE ----------------------------'''


@dataclass
class TableColumn(Container[Element]):
title: str
element: Element

def kind(self) -> str:
return "Tables.Column"

def children(self) -> list:
return [self.element]

def jsonProperties(self) -> dict:
return {"title": self.title}


''' ------------------------------ EXPRESSION TABLE ---------------------------------'''


@dataclass
class Column:
label: str
key: str
component: Optional[Element] = None
width: str = "1fr"
id: str = ""
align: Optional[str] = None

def __post_init__(self):
self.id = f"{UISpec().getId()}"

def json(self):
props = dict()
props["label"] = self.label
props["key"] = self.key
props["width"] = self.width
if self.component is not None:
props["component"] = self.component.json()
if self.align is not None:
props["align"] = self.align
return props


@dataclass(frozen=True)
class SColumn:
rawExpression: str
format: str = "python"
expression: Optional[sparkColumn] = None
usedColumns: List[str] = field(default_factory=list)
diagnosticMessages: Optional[List[str]] = None

@staticmethod
def getSColumn(column: str, format: str = "python"):
return SColumn(f'col("{column}")', format, col(column), [column])

def isExpressionPresent(self) -> bool:
return len(self.rawExpression.strip()) != 0

def isValidSparkExpression(self) -> bool:
return self.expression is not None

def column(self) -> sparkColumn:
return self.expression

def columnName(self) -> str:
return self.expression._jc.toString()

def jsonProperties(self) -> dict:
props = dict()
props["format"] = format
props["expression"] = self.rawExpression if self.isExpressionPresent() else ""
return props

def __eq__(self, other) -> bool:
return self.jsonProperties() == other.jsonProperties()


@dataclass(frozen=True)
class SColumnExpression:
target: str
expression: SColumn
description: str = ""
_row_id: Optional[str] = None

@staticmethod
def getSColumnExpression(column: str):
# todo @ank sanitize the column for backticks here
return SColumnExpression(column, SColumn.getSColumn(column), "")

@staticmethod
def getColumnsFromColumnExpressionList(columnExpressions: list) -> List[sparkColumn]:
columnList = []
for expression in columnExpressions:
columnList.append(expression.expression.expression)
return columnList

def withRowId(self):
if self._row_id is not None and len(self._row_id.strip()) > 0:
return self
else:
from datetime import datetime
import random
return replace(self, _row_id=(
datetime.now().isoformat() + "_" + str(random.getrandbits(32))).__hash__().__abs__().__str__())

def column(self) -> sparkColumn:
return self.expression.expression.alias(self.target)

def isExpressionPresent(self) -> bool:
return self.expression.isExpressionPresent()

def isValidSparkExpression(self) -> bool:
return self.expression.isValidSparkExpression()

def jsonProperties(self) -> dict:
props = dict()
props["target"] = self.target
expression = dict()
# The format key is not present in the Python world's implementation of SColumn.
# However, Scala implementation uses it, so setting it here. It'll be used for understanding of the newRowData by ui.
# Hardcoding its value to scala for letting dialog-jsons be the same and therefore tests to pass.
expression["format"] = "scala"
expression["expression"] = self.expression.rawExpression if self.isExpressionPresent() else ""
props["expression"] = expression
props["description"] = self.description
return props


class SecretValuePart(ABC):
@abstractmethod
def jsonProperties(self):
pass

@staticmethod
def fromJson(obj):
type = obj["type"]
if type == "pipelineConfiguration":
return ConfigSecret(obj['value'])
elif type == "literal":
return TextSecret(obj['value'])
elif type == "vaultSecret":
value = obj['value']
return VaultSecret(value['providerType'], value['providerName'], value['providerId'],
value.get('secretScope'), value['secretKey'])
else:
raise Exception("Invalid type of SecretValue: " + type)

@staticmethod
def convertTextToSecret(value: str) -> list:
def convertTextToSecretInternal(value: str) -> list:
import re, itertools
configPattern = "\\$\\{[^\\{\\}]*\\}"
configParts = [ConfigSecret(x[2:-1].split(".")) for x in re.findall(configPattern, value)]
textParts = [TextSecret(x) if x else None for x in re.split(configPattern, value)]
return [item for tup in itertools.zip_longest(textParts, configParts) for item in tup if item]

return [x for v in value.split("$$") for x in convertTextToSecretInternal(v) + [TextSecret("$")]][:-1]


@dataclass(frozen=True)
class SecretValue:
parts: list

def jsonProperties(self) -> list:
return [part.jsonProperties() for part in self.parts]

@staticmethod
def fromJson(obj):
return SecretValue([SecretValuePart.fromJson(part) for part in obj])


@dataclass(frozen=True)
class TextSecret(SecretValuePart):
value: str

def jsonProperties(self) -> dict:
props = dict()
props["type"] = "literal"
props["value"] = self.value
return props


@dataclass(frozen=True)
class ConfigSecret(SecretValuePart):
value: list

def jsonProperties(self) -> dict:
props = dict()
props["type"] = "pipelineConfiguration"
props["value"] = self.value
return props


@dataclass(frozen=True)
class VaultSecret(SecretValuePart):
providerType: str
providerName: str
providerId: str
secretScope: Optional[str]
secretKey: str

def jsonProperties(self) -> dict:
props = dict()
props["type"] = "vaultSecret"
value = dict()
value["providerType"] = self.providerType
value["providerName"] = self.providerName
value["providerId"] = self.providerId
if self.secretScope is not None:
value["secretScope"] = self.secretScope
value["secretKey"] = self.secretKey
props["value"] = value
return props


@dataclass
class BasicTable(Atom):
titleVar: str
targetColumnKey: Optional[str] = None
propertyVar: Optional[str] = None
columns: List[Column] = None
delete: bool = True
height: Optional[str] = None
footer: List[Element] = field(default_factory=list)
appendNewRow: bool = True
emptyText: Optional[str] = None
newRowData: Optional[dict] = None

def property(self) -> Optional[str]:
return self.propertyVar

def title(self) -> str:
return self.titleVar

def propertyKey(self) -> str:
return "data"

def kind(self) -> str:
return "Atoms.Table"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def addFooter(self, element: Element):
if self.footer is None:
return replace(self, footer=[element])
else:
self.footer.insert(0, element)
return replace(self, footer=self.footer)

def getTemplateElements(self):
return self.footer

def setEmptyContainerText(self, text: str):
return replace(self, emptyText=text)

def addColumn(self, col: Column):
if self.columns is None:
return replace(self, columns=[col])
else:
self.columns.insert(0, col)
return replace(self, columns=self.columns)

def setTargetColumn(self, columnName):
self.targetColumnKey = columnName

def jsonProperties(self) -> dict:
props = super(BasicTable, self).jsonProperties()
colJsons = []
for col in self.columns:
colJsons.append(col.json())
if len(self.columns) > 0 and self.delete:
colJsons.append({"type": "delete"})
if self.height is not None:
props["height"] = self.height
footerJsons = []
for footer in self.footer:
footerJsons.append(footer.json())
if self.emptyText is not None:
props["emptyText"] = self.emptyText
props["newRowData"] = self.newRowData
props["appendNewRow"] = self.appendNewRow
props["footer"] = footerJsons
props["columns"] = colJsons
if self.targetColumnKey is not None:
props["targetColumnKey"] = self.targetColumnKey
return props

@dataclass
class CodeTable(Atom):
titleVar: str
targetColumn: Optional[Column] = None
expressionColumn: Column = field(default_factory=lambda: Column("Expression", "expression.expression", ExpressionBox(ignoreTitle=True).bindPlaceholders().withSchemaSuggestions()))
propertyVar: Optional[str] = None
delete: bool = False
height: Optional[str] = None
footer: List[Element] = field(default_factory=list)
newRowData: Any = None
appendNewRow: bool = True
virtualize: Optional[bool] = None
targetColumnKey: Optional[str] = None
delay: Optional[int] = None
newRowLabel: Optional[RowLabel] = None
visualBuilder: Optional[VisualBuilderSpec] = None
placeholderRows: Optional[int] = None
fixedHeader: Optional[bool] = None
selectMode: Optional[SelectMode] = SelectMode.always
collapsible: Optional[bool] = None
rowDetailsTemplate: List[Element] = field(default_factory=list)
allowConditions: Optional[bool] = None
allowLoops: Optional[bool] = None

def property(self) -> Optional[str]:
return self.propertyVar

def title(self) -> str:
return self.titleVar

def kind(self) -> str:
return "Atoms.CodeTable"

def propertyKey(self) -> str:
return "data"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def setTargetColumnKey(self, columnName: str):
return replace(self, targetColumnKey=columnName)

def withSchemaSuggestions(self):
currentExpressionBox = self.expressionColumn.component.withSchemaSuggestions()
return replace(self, expressionColumn=replace(self.expressionColumn, component=currentExpressionBox))

def withRowId(self):
return replace(self, virtualize=True)

def withNewRowData(self, data: Any):
return replace(self, newRowData=data)

def columns(self) -> List[Column]:
return [col for col in [self.targetColumn, self.expressionColumn] if col is not None]

def addFooter(self, element: Any):
return replace(self, footer=[element] + self.footer)

def getTemplateElements(self) -> List[Any]:
return self.footer + self.rowDetailsTemplate

def addRowDetail(self, element: Any):
return replace(self, rowDetailsTemplate=[element] + self.rowDetailsTemplate)

def withDelay(self, delay: int):
return replace(self, delay=delay)

def withPlaceholderRows(self, placeholderRows: int):
return replace(self, placeholderRows=placeholderRows)

def withFixedHeader(self, fixedHeader: bool):
return replace(self, fixedHeader=fixedHeader)

def withLabel(self, label: RowLabel):
return replace(self, newRowLabel=label)

def withSelectMode(self, mode: SelectMode):
return replace(self, selectMode=mode)

def withCollapsible(self, value: bool):
return replace(self, collapsible=value)

def withAllowLoops(self, value: bool):
return replace(self, allowLoops=value)

def withAllowConditions(self, value: bool):
return replace(self, allowConditions=value)

def withVisualBuilderSpec(self, visualBuilder: VisualBuilderSpec):
return replace(self, visualBuilder=visualBuilder)

def withExpressionBuilder(self, visualBuilderType: List[ExpressionBuilderType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedTypes=visualBuilderType))

def withUnsupportedExpressionBuilderTypes(self, unsupportedTypes: List[ExpressionBuilderType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
unsupportedTypes=unsupportedTypes))

def withGroupBuilder(self, groupBuilderType: GroupBuilderType):
return replace(self,
visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), builderType=groupBuilderType))

def withBlockType(self, expressionBlockType: ExpressionBlockType):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedBlockType=expressionBlockType))

def withValueType(self, valueType: ExpressionValueType):
return replace(self,
visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), supportedValueType=valueType))

def withFunctionTypes(self, functionTypes: List[ExpressionFunctionType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedFunctionTypes=functionTypes))

def withConfigTypes(self, configTypes: List[ExpressionConfigType]):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(),
supportedConfigTypes=configTypes))

def withBorder(self, border: bool):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), border=border))

def withVisualPlaceholder(self, placeholder: str):
return replace(self, visualBuilder=replace(self.visualBuilder or VisualBuilderSpec(), placeholder=placeholder))

def jsonProperties(self) -> Dict[str, Any]:
props = super().jsonProperties()
if hasattr(super(), "jsonProperties"):
props.update(super().jsonProperties())
props["newRowData"] = self.newRowData
props["appendNewRow"] = self.appendNewRow
colJsons = [column.json() for column in self.columns()]
colsWithDelete = colJsons
if len(colJsons) > 0 and self.delete:
colsWithDelete = colJsons.append({"type": "delete"})
props["columns"] = colsWithDelete
props["footer"] = [footer.json() for footer in self.footer]
if not self.targetColumnKey:
if self.targetColumn:
props["targetColumnKey"] = self.targetColumn.key
else:
props["targetColumnKey"] = self.targetColumnKey
if self.newRowLabel is not None:
props["newRowLabel"] = self.newRowLabel.value
if self.selectMode is not None:
props["selectMode"] = self.selectMode.value
if self.collapsible is not None:
props["collapsible"] = self.collapsible
if self.height is not None:
props["height"] = self.height
if self.virtualize is not None:
props["virtualize"] = self.virtualize
if self.delay is not None:
props["delay"] = self.delay
if self.placeholderRows is not None:
props["placeholderRows"] = self.placeholderRows
if self.fixedHeader is not None:
props["fixedHeader"] = self.fixedHeader
if self.visualBuilder is not None:
props["visualBuilder"] = self.visualBuilder.json()
if self.allowConditions is not None:
props["allowConditions"] = self.allowConditions
if self.allowLoops is not None:
props["allowLoops"] = self.allowLoops
props["rowDetailsTemplate"] = [temp.json() for temp in self.rowDetailsTemplate]
return props


@dataclass
class ExpTable(Atom):
titleVar: str
targetColumn: Column = field(
default_factory=lambda: Column("Target Column", "target", TextBox("", ignoreTitle=True), width="30%"))
expressionColumn: Column = field(default_factory=lambda: Column("Expression", "expression.expression",
ExpressionBox(
ignoreTitle=True).bindPlaceholders().withSchemaSuggestions()))
propertyVar: Optional[str] = None
delete: bool = True
virtualize: Optional[bool] = None
newRowData: Optional[dict] = None
appendNewRow: bool = True
height: Optional[str] = None
footer: List[Element] = field(default_factory=list)
targetColumnKey: Optional[str] = "target"

def property(self) -> Optional[str]:
return self.propertyVar

def setTargetColumn(self, columnName):
self.targetColumnKey = columnName

def title(self) -> str:
return self.titleVar

def kind(self) -> str:
return "Atoms.Table"

def bindPropertyKey(self) -> str:
return "data"

def propertyKey(self) -> str:
return "data"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def enableVirtualization(self):
return replace(self, virtualize=True)

def columns(self) -> list:
return [self.targetColumn, self.expressionColumn]

def addFooter(self, element: Element):
if self.footer is None:
return replace(self, footer=[element])
else:
self.footer.insert(0, element)
return replace(self, footer=self.footer)


def withCopilotEnabledExpressions(self, copilot: CopilotSpec = None):
if self.expressionColumn.component is not None and isinstance(self.expressionColumn.component, ExpressionBox):
new_expression_col = replace(
self.expressionColumn,
component=self.expressionColumn.component.withCopilotEnabledExpression()
)
return replace(self, expressionColumn=new_expression_col)
else:
return self

def allowCopilotExpressionsFix(self):
if self.expressionColumn.component is not None and isinstance(self.expressionColumn.component, ExpressionBox):
new_expression_col = replace(
self.expressionColumn,
component=self.expressionColumn.component.allowFixWithCopilot()
)
return replace(self, expressionColumn=new_expression_col)
else:
return self

def jsonProperties(self) -> dict:
props = super().jsonProperties()
colJsons = []
for col in self.columns():
colJsons.append(col.json())
if len(self.columns()) > 0 and self.delete:
colJsons.append({"type": "delete"})
if self.virtualize is not None:
props["virtualize"] = self.virtualize
props["newRowData"] = self.newRowData
if self.height is not None:
props["height"] = self.height
footerJsons = []
for footer in self.footer:
footerJsons.append(footer.json())
props["footer"] = footerJsons
props["appendNewRow"] = self.appendNewRow
props["columns"] = colJsons
if self.targetColumnKey is not None:
props["targetColumnKey"] = self.targetColumnKey
else:
props["targetColumnKey"] = self.targetColumn.key
return props


@dataclass
class ConfigurationSelectorTable(Atom):
titleVar: str
propertyVar: Optional[str] = None
footer: List[Element] = field(default_factory=list)
availableConfigFieldNames: Optional[str] = None
availableColumnNames: Optional[str] = None
portIndex: Optional[str] = None
newRowData: Optional[dict] = None
appendNewRow: bool = True

def property(self) -> Optional[str]:
return self.propertyVar

def setTargetColumn(self, columnName):
self.targetColumnKey = columnName

def title(self) -> str:
return self.titleVar

def kind(self) -> str:
return "Atoms.ConfigurationSelectorTable"

def propertyKey(self) -> str:
return "data"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def addFooter(self, element: Element):
if self.footer is None:
return replace(self, footer=[element])
else:
self.footer.insert(0, element)
return replace(self, footer=self.footer)

def bindConfigFieldNames(self, property: str):
return replace(self, availableConfigFieldNames=property)

def bindColumnNames(self, property: str):
return replace(self, availableColumnNames=property)

def bindPortIndex(self, property: str):
return replace(self, portIndex=property)

def jsonProperties(self) -> dict:
props = super().jsonProperties()
props["newRowData"] = self.newRowData
footerJsons = []
for footer in self.footer:
footerJsons.append(footer.json())
props["footer"] = footerJsons
props["appendNewRow"] = self.appendNewRow
props["portIndex"] = self.portIndex
props["availableConfigFieldNames"] = self.availableConfigFieldNames
props["availableColumnNames"] = self.availableColumnNames
return props


@dataclass
class KeyValuePair(Atom):
key: str
value: str
propertyVar: Optional[str] = None

def title(self) -> str:
return ""

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return "Atoms.KeyValuePair"

def jsonProperties(self) -> dict:
props = super().jsonProperties()
props['key'] = self.key
props['value'] = self.value
return props


@dataclass
class KeyValuePairs(Atom):
titleVar: str = ""
propertyVar: Optional[str] = None
disabled: bool = False
readOnly: bool = False
placeholder: KeyValuePair = None

def property(self) -> Optional[str]:
return self.propertyVar

def title(self) -> str:
return self.titleVar

def kind(self) -> str:
return "Atoms.KeyValuePairs"

def propertyKey(self) -> str:
return "pairs"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def isReadOnly(self, readOnly: bool = True):
return replace(self, readOnly=readOnly)

def isDisabled(self, disabled: bool = True):
return replace(self, disabled=disabled)

def setPlaceholder(self, keyValuePair: KeyValuePair):
return replace(self, placeholder=keyValuePair)

def jsonProperties(self) -> dict:
props = super().jsonProperties()
props['disabled'] = self.disabled
props['readOnly'] = self.readOnly
props['placeholder'] = self.placeholder.jsonProperties()
return props


''' ------------------------------ LAYOUTS ---------------------------------'''


@dataclass
class StackLayout(Container[Element]):
gap: Optional[str] = "1rem"
direction: Optional[str] = None
align: Optional[str] = None
width: Optional[str] = None
alignY: Optional[str] = None
height: Optional[str] = None
_children: list = None
padding: Optional[str] = None
style: Optional[dict] = None
_template: Optional[Element] = None

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def kind(self) -> str:
return "Layouts.Stack"

def addElement(self, child: Element):
if child is None:
return self
elif self._children is None:
return replace(self, _children=[child])
else:
self._children.insert(0, child)
return replace(self, _children=self._children)

def addTemplate(self, template: Element):
return replace(self, _template=template)

def template(self) -> Optional[Element]:
return self._template

def jsonProperties(self) -> dict:
properties = dict()
if self.gap is not None:
properties["gap"] = self.gap
if self.direction is not None:
properties["direction"] = self.direction
if self.alignY is not None:
properties["alignY"] = self.alignY
if self.align is not None:
properties["align"] = self.align
if self.width is not None:
properties["width"] = self.width
if self.height is not None:
properties["height"] = self.height
if self.style is not None:
properties["style"] = self.style
if self.padding is not None:
properties["padding"] = self.padding
if self._template is not None:
properties["template"] = self.template().json()
return properties


@dataclass
class ColumnLayout(Container[Element]):
width: str = "1fr"
childrenList: list = None
overflow: Optional[str] = None
style: Optional[dict] = None
padding: Optional[str] = None

def children(self) -> list:
if self.childrenList is None:
return []
else:
return self.childrenList

def kind(self) -> str:
return "Layouts.Columns.Column"

def addElement(self, child: Element):
if self.childrenList is None:
return replace(self, childrenList=[child])
else:
self.childrenList.insert(0, child)
return replace(self, childrenList=self.childrenList)

def jsonProperties(self) -> dict:
properties = dict()
properties["width"] = self.width
if self.overflow is not None:
properties["overflow"] = self.overflow
if self.style is not None:
properties["style"] = self.style
if self.padding is not None:
properties["padding"] = self.padding
return properties


@dataclass
class ColumnsLayout(Container[Element]):
gap: Optional[str] = None
alignY: Optional[str] = None
height: Optional[str] = None
_children: list = None

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def kind(self) -> str:
return "Layouts.Columns"

def addColumn(self, column: Optional[Element] = None, width: str = "1fr", overflow: Optional[str] = None):
if column is not None:
return self.addElement(ColumnLayout(width, [column], overflow))
else:
return self.addElement(ColumnLayout(width, None, overflow))

def addElement(self, child: Element):
if self._children is None:
return replace(self, _children=[child])
else:
self._children.insert(0, child)
return replace(self, _children=self._children)

def jsonProperties(self) -> dict:
properties = dict()
if self.gap is not None:
properties["gap"] = self.gap
if self.alignY is not None:
properties["alignY"] = self.alignY
if self.height is not None:
properties["height"] = self.height
return properties


@dataclass
class SimpleButtonLayout(Container[Element]):
label: str
onClick: Optional = None
_children: list = None
stackLayout: Optional[StackLayout] = None

# @staticmethod
def __new__(cls, label: str, onClickFunc: Optional = None):
primaryButton: Button = Button(label, _children=[NativeText(label)])
buttonWithCB = replace(primaryButton, onClick=onClickFunc) if onClickFunc is not None else primaryButton
return StackLayout(alignY="start").addElement(buttonWithCB)

def kind(self) -> str:
return "Layouts.Stack"

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def jsonProperties(self) -> dict:
properties = super(SimpleButtonLayout, self).jsonProperties()
if self.onClick is not None:
properties["actions"] = ["onButtonClick"]
return properties


@dataclass
class Card(Container[Element]):
header: Optional[Element] = None
collapsible: Optional[bool] = None
collapsed: Optional[bool] = None
_children: list = None

def kind(self) -> str:
return "Atoms.Card"

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def addElement(self, child: Element):
if self._children is None:
return replace(self, _children=[child])
else:
self._children.insert(0, child)
return replace(self, _children=self._children)

def jsonProperties(self) -> dict:
properties = dict()
if self.header is not None:
properties["header"] = self.header.json()
if self.collapsible is not None:
properties["collapsible"] = self.collapsible
if self.collapsed is not None:
properties["collapsed"] = self.collapsed
properties["actions"] = list() # sending empty actions as these are derived from UI element
return properties


''' ------------------------------ LISTS ---------------------------------'''


@dataclass
class ListItemDelete(Atom):
titleVar: str
rowIdentifier: Optional[str] = "record"

def title(self) -> str:
return self.titleVar

def kind(self) -> str:
return "Atoms.ListRowDeleteButton"

def property(self) -> Optional[str]:
return None

def bindProperty(self, property: str):
return self

def jsonProperties(self) -> dict:
properties = super(ListItemDelete, self).jsonProperties()
if self.rowIdentifier is not None:
properties["rowIdentifier"] = self.rowIdentifier
return properties


@dataclass
class OrderedList(Atom):
titleVar: str
rowTemplate: list = None
propertyVar: Optional[str] = None
emptyText: Optional[str] = None
virtualize: Optional[bool] = None

def rowIdentifier(self):
return "record"

def propertyKey(self) -> str:
return "data"

def addElement(self, element: Element):
if self.rowTemplate is None:
return replace(self, rowTemplate=[element])
else:
self.rowTemplate.insert(0, element)
return replace(self, rowTemplate=self.rowTemplate)

def enableVirtualization(self):
return replace(self, virtualize=True)

def setEmptyContainerText(self, text: str):
return replace(self, emptyText=text)

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return "Atoms.List"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def jsonProperties(self) -> dict:
properties = super(OrderedList, self).jsonProperties()
properties["rowIdentifier"] = self.rowIdentifier()
properties["emptyText"] = self.emptyText
if self.virtualize is not None:
properties["virtualize"] = self.virtualize
rows = []
for row in self.rowTemplate:
rows.append(row.json())
properties["rowTemplate"] = rows
return properties


''' ------------------------------ ENUMS ---------------------------------'''


@dataclass
class EnumOption(Container[Element]):
title: str
element: Optional[Element] = None

def kind(self) -> str:
return "Enum.Option"

def withElement(self, element: Element):
return replace(self, element=element)

def children(self) -> list:
return [self.element]

def jsonProperties(self) -> dict:
return {"title": self.title}


@dataclass
class Enum(Container[EnumOption]):
options: list = None

def kind(self) -> str:
return "Enum.Container"

def addOption(self, option: EnumOption):
if self.options is None:
return replace(self, options=[option])
else:
self.options.insert(0, option)
return replace(self, options=self.options)

def children(self) -> list:
return self.options


''' ------------------------------ Others ---------------------------------'''


@dataclass
class Text(Container[Element]):
type: str = "default"
property: Optional[str] = None
_children: list = None
level: Optional[FontLevel] = None
tooltip: Optional[str] = None

def kind(self) -> str:
return "Atoms.Text"

def children(self) -> list:
return self._children

def addElement(self, child: Element):
if self._children is None:
return replace(self, _children=[child])
else:
self._children.insert(0, child)
return replace(self, _children=self._children)

def addToolTip(self, msg:str):
return replace(self, tooltip=msg)

def jsonProperties(self) -> dict:
properties = dict()
properties["type"] = self.type
if self.level is not None:
properties["level"] = self.level.value

if self.tooltip is not None:
properties["tooltip"] = self.tooltip

return properties


class Expr(ABC):
@abstractmethod
def json(self):
pass

def propertyPathExpr(self, context: PropertyContext):
return self.json()


@dataclass
class PropExpr(Expr):
value: str

def json(self):
return f"${{self.value}}"

def propertyPathExpr(self, context: PropertyContext):
property = self.value

if (property.startswith("component.properties") and (context.prefix != "")):
propName = property[:len("component.properties.")]
return (f"${{component.properties.{context.prefix}.{propName}}}")
else:
return (f"${{{property}}}")


@dataclass
class StringExpr(Expr):
value: str

def json(self):
return self.value


@dataclass
class BooleanExpr(Expr):
value: bool

def json(self):
return self.value


@dataclass
class Condition(Atom):
leftProp: Optional[Expr] = None
rightProp: Optional[Expr] = None
propertyVar: Optional[str] = None
consequent: List[Element] = field(default_factory=list)
alternate: List[Element] = field(default_factory=list)
condition: str = "Exists"

def property(self) -> Optional[str]:
return self.propertyVar

def title(self) -> str:
return "Condition"

def kind(self) -> str:
return "Condition"

def getTemplateElements(self):
return self.consequent + self.alternate

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

conditionSpecifics = {
"Exists": {"operator": "Unary", "leftKey": "test", "rightKey": ""},
"InList": {"operator": "PresentInList", "leftKey": "test_value", "rightKey": "test_list"},
"Equal": {"operator": "Equal", "leftKey": "test_left", "rightKey": "test_right"},
"NotEqual": {"operator": "NotEqual", "leftKey": "test_left", "rightKey": "test_right"}}

def ifExists(self, existProp: Expr):
return replace(self, leftProp=existProp, condition="Exists")

def ifEqual(self, leftProp: Expr, rightProp: Expr):
return replace(self, leftProp=leftProp, rightProp=rightProp, condition="Equal")

def ifNotEqual(self, leftProp: Expr, rightProp: Expr):
return replace(self, leftProp=leftProp, rightProp=rightProp, condition="NotEqual")

def then(self, elementsWhenConditionTrue: Element):
return replace(self, consequent=[elementsWhenConditionTrue])

def otherwise(self, elementsWhenConditionFalse: Element):
return replace(self, alternate=[elementsWhenConditionFalse])

def jsonProperties(self) -> dict:
properties = super(Condition, self).jsonProperties()
properties["type"] = "Ternary"
conditionSpecificName = self.conditionSpecifics[self.condition]
properties["operator"] = conditionSpecificName["operator"]
if self.leftProp is not None:
properties[conditionSpecificName["leftKey"]] = self.leftProp.propertyPathExpr(PropertyContext("", ""))
if self.rightProp is not None:
properties[conditionSpecificName["rightKey"]] = self.rightProp.propertyPathExpr(PropertyContext("", ""))

consequentArray = []
for element in self.consequent:
consequentArray.append(element.json())
properties["consequent"] = consequentArray

alternateElemsArray = []
for element in self.alternate:
alternateElemsArray.append(element.json())
properties["alternate"] = alternateElemsArray

return properties


'''---------------------------- Dataset -----------------------------------'''


@dataclass(frozen=True)
class DatasetTemplate:
type: str
format: str
tabs: list

def json(self) -> dict:
properties = dict()
properties["type"] = self.type
properties["format"] = self.format
properties["tabs"] = list(map(lambda x: x.json(), self.tabs))
return properties


@dataclass(frozen=True)
class Dataset(Container[Element]):
type: Optional[str]
format: Optional[str]
basicTemplate: Element
templates: List[DatasetTemplate] = field(default_factory=list)

def kind(self) -> str:
return "Dataset"

def children(self) -> list:
return []

def addTemplate(self, datasetTemplate: DatasetTemplate):
self.templates.append(datasetTemplate)
return replace(self, templates=self.templates)

def json(self) -> dict:
return super(Dataset, self).json()

def jsonProperties(self) -> dict:
properties = super(Dataset, self).jsonProperties()
if self.type is not None:
properties["type"] = self.type
if self.format is not None:
properties["format"] = self.format
properties["basicTemplate"] = self.basicTemplate.json()
properties["templates"] = list(map(lambda x: x.json(), self.templates))
return properties


@dataclass(frozen=True)
class NewDataset(Container[Element]):
type: Optional[str]
format: Optional[str]
basicTemplate: Element
templates: List[DatasetTemplate] = field(default_factory=list)
cancelNewDataset: Optional = None
createNewDataset: Optional = None

def kind(self) -> str:
return "NewDataset"

def children(self) -> list:
return []

def addTemplate(self, datasetTemplate: DatasetTemplate):
self.templates.append(datasetTemplate)
return replace(self, templates=self.templates)

def cancelNewDataset(self, action):
return replace(self, cancelNewDataset=action)

def createNewDataset(self, action):
return replace(self, createNewDataset=action)

def json(self):
return super(NewDataset, self).json()

def jsonProperties(self) -> dict:
properties = super(NewDataset, self).jsonProperties()
if self.type is not None:
properties["type"] = self.type
if self.format is not None:
properties["format"] = self.format
properties["basicTemplate"] = self.basicTemplate.json()
properties["templates"] = list(map(lambda x: x.json(), self.templates))
actions = []
if self.createNewDataset is not None:
actions.append("createNewDataset")
if self.cancelNewDataset is not None:
actions.append("cancelNewDataset")
properties["actions"] = actions
return properties


@dataclass
class HorizontalDivider(Container[Element]):
_children: list = None

def kind(self) -> str:
return "Atoms.Divider"

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def jsonProperties(self) -> dict:
properties = super(HorizontalDivider, self).jsonProperties()
properties["type"] = "horizontal"
return properties


@dataclass
class VerticalDivider(Container[Element]):
_children: list = None

def kind(self) -> str:
return "Atoms.Divider"

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def jsonProperties(self) -> dict:
properties = super(VerticalDivider, self).jsonProperties()
properties["type"] = "vertical"
return properties


@dataclass
class TitleElement(Container[NativeText]):
title: str
level: Optional[int] = None

def kind(self) -> str:
return "Atoms.Title"

def children(self) -> list:
return [NativeText(self.title)]

def setLevel(self, level: int):
return replace(self, level=level)

def jsonProperties(self) -> dict:
properties = super(TitleElement, self).jsonProperties()
if self.level is not None:
properties["level"] = self.level
return properties


@dataclass
class SchemaColumnsDropdown(Atom):
titleVar: str
schema: Optional[str] = None
propertyVar: Optional[str] = None
options: List[SelectBoxOption] = field(default_factory=list)
optionProperty: Optional[str] = None
disabled: Optional[bool] = None
placeholder: Optional[str] = None
mode: Optional[str] = None
notFoundContent: Optional[str] = None
errorBindings: List[str] = field(default_factory=list)
showSearch: Optional[bool] = None
allowClear: Optional[bool] = None
appearance: Optional[str] = None # minimal
value: Optional[str] = None # s{component.properties.value}

def kind(self) -> str:
return "SchemaSelectBox"

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def bindSchema(self, schemaPath: str):
return replace(self, schema=schemaPath)

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def withMultipleSelection(self):
return replace(self, mode="multiple")

def withDisabled(self):
return replace(self, disabled=True)

def withNoContentMessage(self, msg: str):
return replace(self, notFoundContent=msg)

def allowClearSelection(self):
return replace(self, allowClear=True)

def withSearchEnabled(self):
return replace(self, showSearch=True)

def showErrorsFor(self, *bindings):
return replace(self, errorBindings=bindings)

def jsonProperties(self):
properties = super(SchemaColumnsDropdown, self).jsonProperties()
if self.schema is not None:
properties["schema"] = self.propertyPath(self.schema)
if self.disabled is not None:
properties["disabled"] = self.disabled
if self.placeholder is not None:
properties["placeholder"] = self.placeholder
if self.mode is not None:
properties["mode"] = self.mode
if self.notFoundContent is not None:
properties["notFoundContent"] = self.notFoundContent
if self.showSearch is not None:
properties["showSearch"] = self.showSearch
if self.allowClear is not None:
properties["allowClear"] = self.allowClear
if self.appearance is not None:
properties["appearance"] = self.appearance
if self.value is not None:
properties["value"] = self.propertyPath(self.value)
if self.errorBindings is not None and len(self.errorBindings) > 0:
properties["errorBindings"] = list(
map(lambda bind: self.propertyPath(bind).replace("${", "").replace("}", ""), self.errorBindings))
return properties


def TargetLocation(pathProperty: str) -> StackLayout:
return StackLayout().addElement(
StackLayout(direction="vertical", gap="1rem", height="100bh")
.addElement(TextBox("Location")
.bindPlaceholder("Enter location here or navigate through the File Browser")
.bindProperty(pathProperty))
.addElement(FileBrowser().hideExecutionErrors().bindProperty(pathProperty)))


@dataclass
class ScrollBox(Container[Element]):
property: Optional[str] = None
height: Optional[str] = "100%"
width: Optional[str] = None
_children: list = None
propertyVar: Optional[str] = None

def kind(self) -> str:
return "Atoms.ScrollBox"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def addElement(self, element: Element):
if self._children is None:
return replace(self, _children=[element])
else:
self._children.insert(0, element)
return replace(self, _children=self._children)

def jsonProperties(self) -> dict:
properties = super(ScrollBox, self).jsonProperties()
if self.height is not None:
properties["height"] = self.height
if self.width is not None:
properties["width"] = self.width
return properties


@dataclass
class CatalogTableDB(Atom):
titleVar: str
propertyVar: Optional[str] = None
tableProperty: Optional[str] = None
isCatalogEnabled: Optional[str] = None
catalog: Optional[str] = None

def title(self) -> str:
return self.titleVar

def kind(self) -> str:
return "Database"

def property(self) -> Optional[str]:
return self.propertyVar

def propertyKey(self) -> str:
return "database"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def bindTableProperty(self, property: str):
return replace(self, tableProperty=property)

def bindIsCatalogEnabledProperty(self, property: str):
return replace(self, isCatalogEnabled=property)

def bindCatalogProperty(self, property: str):
return replace(self, catalog=property)

def jsonProperties(self) -> dict:
properties = super(CatalogTableDB, self).jsonProperties()
if self.tableProperty is not None:
properties["table"] = self.propertyPath(self.tableProperty)
if self.isCatalogEnabled is not None:
properties["isCatalogEnabled"] = self.propertyPath(self.isCatalogEnabled)
if self.catalog is not None:
properties["catalog"] = self.propertyPath(self.catalog)
return properties


@dataclass
class StackItem(Container[Element]):
_children: list = None
grow: Optional[int] = 0
shrink: Optional[int] = 1
basis: Optional[str] = "auto"

def kind(self) -> str:
return "Layouts.StackItem"

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def addElement(self, element: Element):
if self._children is None:
return replace(self, _children=[element])
else:
self._children.insert(0, element)
return replace(self, _children=self._children)

def jsonProperties(self) -> dict:
properties = super(StackItem, self).jsonProperties()
if self.grow is not None:
properties["grow"] = self.grow
if self.shrink is not None:
properties["shrink"] = self.shrink
if self.basis is not None:
properties["basis"] = self.basis
return properties


@dataclass
class OptionField:
key: str
field: Element
hideDelete: bool = False

@dataclass
class PickerTab:
label: str
value: str
isDefault: bool = False
propertyVar: Optional[str] = "component.properties"
fields: List[OptionField] = None

def addFields(self, fields: List[OptionField]):
if self.fields is None:
return replace(self, fields=fields)
else:
newFields = copy.deepcopy(self.fields)
newFields.extend(fields)
return replace(self, fields=newFields)

def addField(self, child: Atom, key: str, hideDelete: bool = False):
optionField = OptionField(key, child.bindProperty(f"{self.propertyVar}.{key}"), hideDelete)
if self.fields is None:
return replace(self, fields=[optionField])
else:
newFields = copy.deepcopy(self.fields)
newFields.append(optionField)
return replace(self, fields=newFields)

def json(self) -> dict:
ff = []
if self.fields is not None:
for field in self.fields:
entry = {"key": field.key, "field": field.field.json()}
if field.hideDelete:
entry["hideDelete"] = True
ff.append(entry)

return {
"label": self.label,
"value": self.value,
"isDefault": self.isDefault,
"dataset": propertyPath(self.propertyVar),
"fields": ff
}


@dataclass
class FieldPickerWithTabs(Container[Element]):
title: Optional[str] = None
height: Optional[str] = None
tabs: Optional[List[PickerTab]] = None
_children: list = None

def kind(self) -> str:
return "Atoms.FieldPickerWithTabs"

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def addTab(self, pickerTab: PickerTab):
if self.tabs is None:
return replace(self, tabs=[pickerTab])
else:
self.tabs.append(pickerTab)
return replace(self, tabs=self.tabs)

def jsonProperties(self) -> dict:
properties = super(FieldPickerWithTabs, self).jsonProperties()
if self.title is not None:
properties["title"] = self.title
if self.height is not None:
properties["height"] = self.height
if self.tabs is not None:
fields = []
for tab in self.tabs:
fields.append(tab.json())
properties["tabs"] = fields
return properties


@dataclass
class FieldPicker(Container[Element]):
_children: list = None
fields: List[OptionField] = None
height: Optional[str] = None
propertyVar: Optional[str] = "component.properties"
title: Optional[str] = None

def kind(self) -> str:
return "Atoms.FieldPicker"

def children(self) -> list:
if self._children is None:
return []
else:
return self._children

def addField(self, child: Atom, key: str, hideDelete: bool = False):
optionField = OptionField(key, child.bindProperty(f"{self.propertyVar}.{key}"), hideDelete)
if self.fields is None:
return replace(self, fields=[optionField])
else:
newFields = copy.deepcopy(self.fields)
newFields.insert(0, optionField)
return replace(self, fields=newFields)

def jsonProperties(self) -> dict:
properties = super(FieldPicker, self).jsonProperties()
properties["dataset"] = self.propertyPath(self.propertyVar)
fields = []
if self.fields is not None:
reversedFields = reversed(self.fields)
for field in reversedFields:
entry = {"key": field.key, "field": field.field.json()}
if (field.hideDelete):
entry["hideDelete"] = True
fields.append(entry)
properties["fields"] = fields
if self.height is not None:
properties["height"] = self.height
if self.title is not None:
properties["title"] = self.title
return properties


@dataclass
class SchemaTable(Atom):
titleVar: str
readOnly: bool = False
allowInferSchema: bool = True
propertyVar: Optional[str] = None

def kind(self) -> str:
return "SchemaTable"

def propertyKey(self) -> str:
return "schema"

def property(self) -> Optional[str]:
return self.propertyVar

def title(self) -> str:
return self.titleVar

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def isReadOnly(self, readOnly: bool = True):
return replace(self, readOnly=readOnly)

def withoutInferSchema(self):
return replace(self, allowInferSchema=False)

def jsonProperties(self):
properties = super(SchemaTable, self).jsonProperties()
properties["readOnly"] = self.readOnly
properties["allowInferSchema"] = self.allowInferSchema
return properties


@dataclass
class Credentials(Atom):
titleVar: str
propertyVar: Optional[str] = None

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return "Credentials"

def propertyKey(self) -> str:
return "scope"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def jsonProperties(self) -> dict:
properties = super(Credentials, self).jsonProperties()
return properties


@dataclass
class SecretBox(Atom):
titleVar: str
propertyVar: Optional[str] = None
placeholder: str = ""
allowPlainText: Optional[bool] = None

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def kind(self) -> str:
return "Atoms.Secret"

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def isPassword(self):
return replace(self, allowPlainText=False)

def bindPlaceholder(self, placeHolder: str):
return replace(self, placeholder=placeHolder)

def jsonProperties(self) -> dict:
props = super(SecretBox, self).jsonProperties()
props["title"] = self.title()
if self.allowPlainText is not None:
props["allowPlainText"] = self.allowPlainText
props["placeholder"] = self.placeholder
return props

# Button to sign in using OAuth in Orchetrator connector creation flow
@dataclass
class OAuthButton(Atom):
_title: str
host: Optional[str] = None
provider: Optional[str] = None
connectorKind: Optional[str] = None
_kind: str = "Atoms.OAuthButton"
propertyVar: Optional[str] = None

def title(self) -> str:
return self._title

def property(self) -> Optional[str]:
return self.propertyVar

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def withHost(self, host: str):
return replace(self, host=host)

def withProvider(self, provider: str):
return replace(self, provider=provider)

def withConnectorKind(self, connectorKind: str):
return replace(self, connectorKind=connectorKind)

def kind(self):
return self._kind

def jsonProperties(self) -> dict:
props = super(OAuthButton, self).jsonProperties()
if self.host is not None:
props["host"] = self.host
if self.provider is not None:
props["provider"] = self.provider
if self.connectorKind is not None:
props["connectorKind"] = self.connectorKind
return props

@dataclass
class TextBoxTemplate(Container[Element]):
titleVar: str
identifierVar: str
templateVar: StackLayout
_kind: str = "Layouts.Stack"
_children: List[Element] = field(default_factory=list)

def title(self) -> str:
return self.titleVar

def identifier(self) -> Optional[str]:
return self.identifierVar

def template(self) -> StackLayout:
return self.templateVar

def kind(self) -> str:
return self._kind

def children(self) -> list:
return self._children

def jsonProperties(self) -> dict:
props = super(TextBoxTemplate, self).jsonProperties()
props["identifier"] = self.identifier()
props["template"] = self.template().json()
return props

@staticmethod
def create_job_size_template(titleVar: str, identifier: str):
sub_template: StackLayout = StackLayout().addTemplate(
StackLayout(align="space-between", direction="horizontal").addElement(
child=Text(_children=[NativeText("${job_sizes.label}")])))

return TextBoxTemplate(titleVar, identifier, templateVar=sub_template,
_children=[NativeText("${record.label}")])

@staticmethod
def create_pipeline_template(titleVar: str, identifier: str):
sub_template: StackLayout = StackLayout().addTemplate(
StackLayout(align="space-between", direction="horizontal").addElement(
child=Text(_children=[NativeText("${pipelines.label}")])))

return TextBoxTemplate(titleVar, identifier, templateVar=sub_template,
_children=[NativeText("${record.label}")])

@staticmethod
def create_airflow_connection_template(titleVar: str, identifier: str):
sub_template: StackLayout = StackLayout().addTemplate(
StackLayout(align="space-between", direction="horizontal").addElement(
child=Text(_children=[NativeText("${record.label}")])))

return TextBoxTemplate(titleVar, identifier, templateVar=sub_template,
_children=[NativeText("${record.label}")])

@staticmethod
def create_airflow_dag_template(titleVar: str, identifier: str):
sub_template: StackLayout = StackLayout().addTemplate(
StackLayout(align="space-between", direction="horizontal").addElement(
child=Text(_children=[NativeText("${projects.git_branches_and_release_tags.label}")])))

return TextBoxTemplate(titleVar, identifier, templateVar=sub_template,
_children=[NativeText("${record.label}")])

@staticmethod
def create_model_template():
return NativeText("${record.label}")


@staticmethod
def create_project_template(titleVar: str, identifier: str):
sub_template: StackLayout = StackLayout().addTemplate(
StackLayout(align="space-between", direction="horizontal").addElement(
child=Condition().ifEqual(PropExpr("projects.disabled"), BooleanExpr(True))
.then(Text(_children=[NativeText("${projects.label}")],
tooltip="Prophecy Managed git projects cannot be scheduled"))
.otherwise(Text(_children=[NativeText("${projects.label}")]))
))

return TextBoxTemplate(titleVar, identifier, templateVar=sub_template,
_children=[NativeText("${record.label}")])

@staticmethod
def create_fabric_template(titleVar: str, identifier: str):
sub_template: StackLayout = StackLayout().addTemplate(
StackLayout(direction="horizontal").addElement(
StackLayout(direction="horizontal", width="100%", gap="8px")
.addElement(StackLayout().addElement(FabricIcon(provider="SQL", providerType="${fabrics.type}")))
.addElement(child=Text(_children=[NativeText("${fabrics.label}")]))))

return TextBoxTemplate(titleVar, identifier, templateVar=sub_template,
_children=[NativeText("${record.label}")])

@dataclass
class SelectBoxWithTemplate(Atom):
titleVar: str
propertyVar: Optional[str] = None
options: List[SelectBoxOption] = field(default_factory=list)
optionProperty: Optional[str] = None
disabled: Optional[bool] = None
allowConfig: Optional[bool] = None
style: Optional[dict] = None
placeholder: Optional[str] = None
mode: Optional[str] = None
notFoundContent: Optional[str] = None
showSearch: Optional[bool] = None
_identifier: Optional[str] = None
_template: Optional[TextBoxTemplate] = None
disableOption: Optional[bool] = None
optionFilterProp: Optional[str] = None
hasGroupName: Optional[bool] = None
_kind: str = "Atoms.SelectBox"
optionCTA: Optional[Element] = None

def title(self) -> str:
return self.titleVar

def property(self) -> Optional[str]:
return self.propertyVar

def identifier(self) -> Optional[str]:
return self._identifier

def template(self) -> Optional[TextBoxTemplate]:
return self._template

def kind(self) -> str:
return self._kind

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def withNoContentMessage(self, msg: str):
return replace(self, notFoundContent=msg)

def bindOptionProperty(self, property: str):
return replace(self, optionProperty=property)

def withStyle(self, style: dict):
return replace(self, style=style)

def withAllowConfig(self):
return replace(self, allowConfig=True)

def withSearchEnabled(self):
return replace(self, showSearch=True)

def withIdentifier(self, identifier: str):
return replace(self, _identifier=identifier)

def withFilterProp(self, filterProp: str):
return replace(self, optionFilterProp=filterProp)

def withGroupName(self):
return replace(self, hasGroupName=True)

def addTemplate(self, template: TextBoxTemplate):
return replace(self, _template=template)

def withOptionCTA(self, cta: Element):
return replace(self, optionCTA=cta)

def jsonProperties(self) -> dict:
props = super(SelectBoxWithTemplate, self).jsonProperties()
props["identifier"] = self.identifier()
props["template"] = self.template().json()
if self.disableOption is not None:
props["disableOption"] = self.disableOption
if self.showSearch is not None:
props["showSearch"] = self.showSearch
if self.allowConfig is not None:
props["allowConfig"] = self.allowConfig
if self.notFoundContent is not None:
props["notFoundContent"] = self.notFoundContent
if self.mode is not None:
props["mode"] = self.mode
if self.placeholder is not None:
props["placeholder"] = self.placeholder
if self.disabled is not None:
props["disabled"] = self.disabled
if self.style is not None:
props["style"] = self.style
if self.optionFilterProp is not None:
props["optionFilterProp"] = self.optionFilterProp
if self.hasGroupName is not None:
props["hasGroupName"] = self.hasGroupName
if self.optionCTA is not None:
props["optionCTA"] = self.optionCTA.json()

# Label -> value generation for options in absence of option property
if self.optionProperty is not None:
props["options"] = self.optionProperty
elif self.options is not None:
options_json_array = []
for opt in self.options:
options_json_array.append({"label": opt.label, "value": opt.value})
props["options"] = options_json_array
return props


@dataclass
class ImagePlaceholder(Atom):
gem: Gem
_title: str
icon: Optional[str] = None
propertyVar: Optional[str] = None
_kind: str = "Atoms.ImagePlaceholder"

def title(self) -> str:
return self._title

def kind(self) -> str:
return self._kind

def property(self) -> Optional[str]:
return self.propertyVar

def bindProperty(self, property: str):
return replace(self, propertyVar=property)

def jsonProperties(self) -> dict:
props = super(ImagePlaceholder, self).jsonProperties()
props["gem"] = self.gem.name
if self.icon is not None:
props["icon"] = self.icon
return props


@dataclass
class PipelineConfigurationTable(Atom):
_title: str = "Pipeline Configurations"
_schemaProperty: str = "${component.properties.configurations.schema}"
_selectedInstanceProperty: str = "${component.properties.configurations.selectedInstance}"
_instancesProperty: str = "${component.properties.configurations.instances}"
_overridesProperty: str = "${component.properties.configurations.overrides}"
_clusterSizeProperty: str = "${component.properties.clusterSize}"
_property: Optional[str] = None
_kind: str = "Atoms.PipelineConfiguration"

def title(self) -> str:
return self._title

def kind(self) -> str:
return self._kind

def property(self) -> Optional[str]:
return self._property

def schemaProperty(self) -> Optional[str]:
return self._schemaProperty

def selectedInstanceProperty(self) -> Optional[str]:
return self._selectedInstanceProperty

def instancesProperty(self) -> Optional[str]:
return self._instancesProperty

def overridesProperty(self) -> Optional[str]:
return self._overridesProperty

def clusterSizeProperty(self) -> Optional[str]:
return self._clusterSizeProperty

def bindProperty(self, property: str):
return replace(self, _property=property)

def jsonProperties(self) -> dict:
props = super(PipelineConfigurationTable, self).jsonProperties()
props["schema"] = self.schemaProperty()
props["selectedInstance"] = self.selectedInstanceProperty()
props["instances"] = self.instancesProperty()
props["overrides"] = self.overridesProperty()
props["clusterSize"] = self.clusterSizeProperty()
return props


# -------------------------------- MACRO TABLE --------------------------------
@dataclass
class MacroInstance(Atom):
_title: str
_property: Optional[str] = None
ports: Optional[str] = None
name: Optional[str] = None
projectName: Optional[str] = None

def title(self) -> str:
return self._title

def property(self) -> Optional[str]:
return self._property

def kind(self) -> str:
return "MacroInstance"

def bindProperty(self, property: str):
return replace(self, _property=property)

def withSchemaSuggestions(self):
return replace(self, ports="component.ports.inputs")

def jsonProperties(self) -> dict:
props = super().jsonProperties()
if self.ports is not None:
props['ports'] = self.propertyPath(self.ports)
if self.name is not None:
props['macro'] = self.propertyPath(self.name)
if self.projectName is not None:
props['projectName'] = self.propertyPath(self.projectName)
return props