I can load and save Jira and Table Processor details
This commit is contained in:
@@ -41,6 +41,7 @@ def post(session, _id: str, component_type: str, x: int, y: int):
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.add_component(component_type, x, y)
|
||||
|
||||
|
||||
@rt(Routes.MoveComponent)
|
||||
def post(session, _id: str, component_id: str, x: int, y: int):
|
||||
logger.debug(
|
||||
@@ -56,6 +57,7 @@ def post(session, _id: str, component_id: str):
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.delete_component(component_id)
|
||||
|
||||
|
||||
@rt(Routes.AddConnection)
|
||||
def post(session, _id: str, from_id: str, to_id: str):
|
||||
logger.debug(
|
||||
@@ -63,9 +65,55 @@ def post(session, _id: str, from_id: str, to_id: str):
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.add_connection(from_id, to_id)
|
||||
|
||||
|
||||
@rt(Routes.ResizeDesigner)
|
||||
def post(session, _id: str, designer_height: int):
|
||||
logger.debug(
|
||||
f"Entering {Routes.ResizeDesigner} with args {debug_session(session)}, {_id=}, {designer_height=}")
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.set_designer_height(designer_height)
|
||||
|
||||
|
||||
@rt(Routes.SelectComponent)
|
||||
def post(session, _id: str, component_id: str):
|
||||
logger.debug(
|
||||
f"Entering {Routes.SelectComponent} with args {debug_session(session)}, {_id=}, {component_id=}")
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.select_component(component_id)
|
||||
|
||||
|
||||
@rt(Routes.SaveProperties)
|
||||
def post(session, _id: str, component_id: str, details: dict):
|
||||
logger.debug(
|
||||
f"Entering {Routes.SaveProperties} with args {debug_session(session)}, {_id=}, {component_id=}, {details=}")
|
||||
instance = InstanceManager.get(session, _id)
|
||||
details.pop("_id")
|
||||
details.pop("component_id")
|
||||
return instance.save_properties(component_id, details)
|
||||
|
||||
|
||||
@rt(Routes.CancelProperties)
|
||||
def post(session, _id: str, component_id: str):
|
||||
logger.debug(
|
||||
f"Entering {Routes.CancelProperties} with args {debug_session(session)}, {_id=}, {component_id=}")
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.cancel_properties(component_id)
|
||||
|
||||
|
||||
@rt(Routes.SelectProcessor)
|
||||
def post(session, _id: str, component_id: str, processor_name: str):
|
||||
logger.debug(
|
||||
f"Entering {Routes.SelectProcessor} with args {debug_session(session)}, {_id=}, {component_id=}, {processor_name=}")
|
||||
instance = InstanceManager.get(session, _id)
|
||||
return instance.set_selected_processor(component_id, processor_name)
|
||||
|
||||
|
||||
@rt(Routes.OnProcessorDetailsEvent)
|
||||
def post(session, _id: str, component_id: str, event_name: str, details: dict):
|
||||
logger.debug(
|
||||
f"Entering {Routes.OnProcessorDetailsEvent} with args {debug_session(session)}, {_id=}, {component_id=}, {event_name=}, {details=}")
|
||||
instance = InstanceManager.get(session, _id)
|
||||
details.pop("_id")
|
||||
details.pop("component_id")
|
||||
details.pop("event_name")
|
||||
return instance.on_processor_details_event(component_id, event_name, details)
|
||||
@@ -1,6 +1,5 @@
|
||||
.wkf-toolbox-item {
|
||||
cursor: grab;
|
||||
color: var(--color-neutral);
|
||||
}
|
||||
|
||||
.wkf-toolbox-item:active {
|
||||
@@ -74,7 +73,6 @@
|
||||
border: 2px solid transparent;
|
||||
transition: all 0.2s;
|
||||
height: 64px;
|
||||
color: var(--color-neutral);
|
||||
}
|
||||
|
||||
.wkf-workflow-component:hover {
|
||||
|
||||
@@ -217,10 +217,10 @@ function bindWorkflowDesignerToolbox(elementId) {
|
||||
if (!component) return;
|
||||
|
||||
componentId = component.dataset.componentId
|
||||
htmx.ajax('POST', '/workflows/select-connection', {
|
||||
target: `#c_${elementId}`,
|
||||
htmx.ajax('POST', '/workflows/select-component', {
|
||||
target: `#p_${elementId}`,
|
||||
headers: {"Content-Type": "application/x-www-form-urlencoded"},
|
||||
swap: "innerHTML",
|
||||
swap: "outerHTML",
|
||||
values: {
|
||||
_id: elementId,
|
||||
component_id: componentId
|
||||
|
||||
@@ -28,3 +28,42 @@ class WorkflowsCommandManager(BaseCommandManager):
|
||||
"hx-swap": "outerHTML",
|
||||
"hx-vals": f'js:{{"_id": "{self._id}", "name": "{workflow_name}", "tab_boundaries": getTabContentBoundaries("{self._owner.tabs_manager.get_id()}")}}',
|
||||
}
|
||||
|
||||
|
||||
class WorkflowDesignerCommandManager(BaseCommandManager):
|
||||
def __init__(self, owner):
|
||||
super().__init__(owner)
|
||||
|
||||
def select_processor(self, component_id :str):
|
||||
return {
|
||||
"hx_post": f"{ROUTE_ROOT}{Routes.SelectProcessor}",
|
||||
"hx-target": f"#p_{self._id}",
|
||||
"hx-swap": "outerHTML",
|
||||
"hx-trigger": "change",
|
||||
"hx-vals": f'js:{{"_id": "{self._id}", "component_id": "{component_id}"}}',
|
||||
}
|
||||
|
||||
def save_properties(self, component_id: str):
|
||||
return {
|
||||
"hx_post": f"{ROUTE_ROOT}{Routes.SaveProperties}",
|
||||
"hx-target": f"#p_{self._id}",
|
||||
"hx-swap": "outerHTML",
|
||||
"hx-vals": f'js:{{"_id": "{self._id}", "component_id": "{component_id}"}}',
|
||||
}
|
||||
|
||||
def cancel_properties(self, component_id: str):
|
||||
return {
|
||||
"hx_post": f"{ROUTE_ROOT}{Routes.CancelProperties}",
|
||||
"hx-target": f"#p_{self._id}",
|
||||
"hx-swap": "outerHTML",
|
||||
"hx-vals": f'js:{{"_id": "{self._id}", "component_id": "{component_id}"}}',
|
||||
}
|
||||
|
||||
def on_processor_details_event(self, component_id: str, event_name: str):
|
||||
return {
|
||||
"hx_post": f"{ROUTE_ROOT}{Routes.OnProcessorDetailsEvent}",
|
||||
"hx-target": f"#p_{self._id}",
|
||||
"hx-trigger": "change",
|
||||
"hx-swap": "outerHTML",
|
||||
"hx-vals": f'js:{{"_id": "{self._id}", "component_id": "{component_id}", "event_name": "{event_name}"}}',
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import logging
|
||||
|
||||
from fastcore.basics import NotStr
|
||||
from fasthtml.components import *
|
||||
from fasthtml.xtend import Script
|
||||
|
||||
from components.BaseComponent import BaseComponent
|
||||
from components.workflows.commands import WorkflowDesignerCommandManager
|
||||
from components.workflows.constants import WORKFLOW_DESIGNER_INSTANCE_ID
|
||||
from components.workflows.db_management import WorkflowsDesignerSettings, WorkflowComponent, \
|
||||
Connection, WorkflowsDesignerDbManager
|
||||
from components_helpers import apply_boundaries, mk_tooltip
|
||||
from core.utils import get_unique_id
|
||||
from components_helpers import apply_boundaries, mk_tooltip, mk_dialog_buttons
|
||||
from core.utils import get_unique_id, make_safe_id
|
||||
from utils.DbManagementHelper import DbManagementHelper
|
||||
|
||||
logger = logging.getLogger("WorkflowDesigner")
|
||||
|
||||
# Component templates
|
||||
COMPONENT_TYPES = {
|
||||
@@ -15,22 +21,27 @@ COMPONENT_TYPES = {
|
||||
"title": "Data Producer",
|
||||
"description": "Generates or loads data",
|
||||
"icon": "📊",
|
||||
"color": "bg-green-100 border-green-300"
|
||||
"color": "bg-green-100 border-green-300 text-neutral"
|
||||
},
|
||||
"filter": {
|
||||
"title": "Data Filter",
|
||||
"description": "Filters and transforms data",
|
||||
"icon": "🔍",
|
||||
"color": "bg-blue-100 border-blue-300"
|
||||
"color": "bg-blue-100 border-blue-300 text-neutral"
|
||||
},
|
||||
"presenter": {
|
||||
"title": "Data Presenter",
|
||||
"description": "Displays or exports data",
|
||||
"icon": "📋",
|
||||
"color": "bg-purple-100 border-purple-300"
|
||||
"color": "bg-purple-100 border-purple-300 text-neutral"
|
||||
}
|
||||
}
|
||||
|
||||
PROCESSOR_TYPES = {
|
||||
"producer": ["Repository", "Jira"],
|
||||
"filter": ["Default"],
|
||||
"presenter": ["Default"]}
|
||||
|
||||
|
||||
class WorkflowDesigner(BaseComponent):
|
||||
def __init__(self, session,
|
||||
@@ -46,13 +57,17 @@ class WorkflowDesigner(BaseComponent):
|
||||
self._db = WorkflowsDesignerDbManager(session, settings_manager)
|
||||
self._state = self._db.load_state(key)
|
||||
self._boundaries = boundaries
|
||||
self.commands = WorkflowDesignerCommandManager(self)
|
||||
|
||||
def set_boundaries(self, boundaries: dict):
|
||||
self._boundaries = boundaries
|
||||
|
||||
def refresh(self, oob=False):
|
||||
def refresh_designer(self):
|
||||
return self._mk_elements()
|
||||
|
||||
def refresh_properties(self):
|
||||
return self._mk_properties()
|
||||
|
||||
def add_component(self, component_type, x, y):
|
||||
self._state.component_counter += 1
|
||||
|
||||
@@ -70,7 +85,7 @@ class WorkflowDesigner(BaseComponent):
|
||||
|
||||
self._state.components[component_id] = component
|
||||
self._db.save_state(self._key, self._state) # update db
|
||||
return self.refresh()
|
||||
return self.refresh_designer()
|
||||
|
||||
def move_component(self, component_id, x, y):
|
||||
if component_id in self._state.components:
|
||||
@@ -78,7 +93,7 @@ class WorkflowDesigner(BaseComponent):
|
||||
self._state.components[component_id].y = int(y)
|
||||
self._db.save_state(self._key, self._state) # update db
|
||||
|
||||
return self.refresh()
|
||||
return self.refresh_designer()
|
||||
|
||||
def delete_component(self, component_id):
|
||||
# Remove component
|
||||
@@ -91,13 +106,13 @@ class WorkflowDesigner(BaseComponent):
|
||||
# update db
|
||||
self._db.save_state(self._key, self._state)
|
||||
|
||||
return self.refresh()
|
||||
return self.refresh_designer()
|
||||
|
||||
def add_connection(self, from_id, to_id):
|
||||
# Check if connection already exists
|
||||
for connection in self._state.connections:
|
||||
if connection.from_id == from_id and connection.to_id == to_id:
|
||||
return self.refresh() # , self.error_message("Connection already exists")
|
||||
return self.refresh_designer() # , self.error_message("Connection already exists")
|
||||
|
||||
connection_id = f"conn_{len(self._state.connections) + 1}"
|
||||
connection = Connection(id=connection_id, from_id=from_id, to_id=to_id)
|
||||
@@ -106,13 +121,49 @@ class WorkflowDesigner(BaseComponent):
|
||||
# update db
|
||||
self._db.save_state(self._key, self._state)
|
||||
|
||||
return self.refresh()
|
||||
return self.refresh_designer()
|
||||
|
||||
def set_designer_height(self, height):
|
||||
self._state.designer_height = height
|
||||
self._db.save_state(self._key, self._state)
|
||||
return self.__ft__() # refresh the whole component
|
||||
|
||||
def select_component(self, component_id):
|
||||
if component_id in self._state.components:
|
||||
self._state.selected_component_id = component_id
|
||||
self._db.save_state(self._key, self._state)
|
||||
|
||||
return self.refresh_properties()
|
||||
|
||||
def save_properties(self, component_id: str, details: dict):
|
||||
if component_id in self._state.components:
|
||||
component = self._state.components[component_id]
|
||||
component.properties = details
|
||||
self._db.save_state(self._key, self._state)
|
||||
logger.debug(f"Saved properties for component {component_id}: {details}")
|
||||
|
||||
return self.refresh_properties()
|
||||
|
||||
def cancel_properties(self, component_id: str):
|
||||
if component_id in self._state.components:
|
||||
logger.debug(f"Cancel saving properties for component {component_id}")
|
||||
|
||||
return self.refresh_properties()
|
||||
|
||||
def set_selected_processor(self, component_id: str, processor_name: str):
|
||||
self._state.selected_processor_name[component_id] = processor_name
|
||||
self._db.save_state(self._key, self._state)
|
||||
return self.refresh_properties()
|
||||
|
||||
def on_processor_details_event(self, component_id: str, event_name: str, details: dict):
|
||||
if component_id in self._state.components:
|
||||
component = self._state.components[component_id]
|
||||
if event_name == "OnRepositoryChanged":
|
||||
component.properties["repository"] = details["repository"]
|
||||
component.properties["table"] = None
|
||||
|
||||
return self.refresh_properties()
|
||||
|
||||
def __ft__(self):
|
||||
return Div(
|
||||
H1(f"{self._designer_settings.workflow_name}", cls="text-xl font-bold"),
|
||||
@@ -125,56 +176,6 @@ class WorkflowDesigner(BaseComponent):
|
||||
id=f"{self._id}",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def create_component_id(session, suffix=None):
|
||||
prefix = f"{WORKFLOW_DESIGNER_INSTANCE_ID}{session['user_id']}"
|
||||
if suffix is None:
|
||||
suffix = get_unique_id()
|
||||
|
||||
return f"{prefix}{suffix}"
|
||||
|
||||
@staticmethod
|
||||
def _mk_toolbox_item(component_type: str, info: dict):
|
||||
return Div(
|
||||
mk_tooltip(
|
||||
Div(
|
||||
Span(info["icon"], cls="mb-2"),
|
||||
H4(info["title"], cls="font-semibold text-xs"),
|
||||
cls=f"p-2 rounded-lg border-2 {info['color']} flex text-center"
|
||||
),
|
||||
tooltip=info["description"]),
|
||||
cls="wkf-toolbox-item p-2",
|
||||
draggable="true",
|
||||
data_type=component_type
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _mk_workflow_component(component: WorkflowComponent):
|
||||
info = COMPONENT_TYPES[component.type]
|
||||
return Div(
|
||||
# Input connection point
|
||||
Div(cls="wkf-connection-point wkf-input-point",
|
||||
data_component_id=component.id,
|
||||
data_point_type="input"),
|
||||
|
||||
# Component content
|
||||
Div(
|
||||
Span(info["icon"], cls="text-xl mb-1"),
|
||||
H4(component.title, cls="font-semibold text-xs"),
|
||||
cls=f"p-3 rounded-lg border-2 {info['color']} bg-white shadow-lg flex items-center"
|
||||
),
|
||||
|
||||
# Output connection point
|
||||
Div(cls="wkf-connection-point wkf-output-point",
|
||||
data_component_id=component.id,
|
||||
data_point_type="output"),
|
||||
|
||||
cls="wkf-workflow-component w-32",
|
||||
style=f"left: {component.x}px; top: {component.y}px;",
|
||||
data_component_id=component.id,
|
||||
draggable="true"
|
||||
)
|
||||
|
||||
def _mk_connection_svg(self, conn: Connection):
|
||||
if conn.from_id not in self._state.components or conn.to_id not in self._state.components:
|
||||
return ""
|
||||
@@ -241,22 +242,158 @@ class WorkflowDesigner(BaseComponent):
|
||||
style=f"height:{self._state.designer_height}px;"
|
||||
)
|
||||
|
||||
def _mk_properties(self):
|
||||
def _mk_processor_properties(self, component, processor_name):
|
||||
if processor_name == "Jira":
|
||||
return self._mk_jira_processor_details(component)
|
||||
elif processor_name == "Repository":
|
||||
return self._mk_repository_processor_details(component)
|
||||
|
||||
return Div('Not defined yet !')
|
||||
|
||||
def _mk_properties_details(self):
|
||||
def _mk_header():
|
||||
return Div(
|
||||
Div(
|
||||
H4("Properties"),
|
||||
Div(
|
||||
P("Workflow name:", cls="text-sm"),
|
||||
Div(self._designer_settings.workflow_name, cls="text-base"),
|
||||
cls="flex flex-col gap-2"
|
||||
),
|
||||
Span(icon),
|
||||
H4(component.title, cls="font-semibold text-xs"),
|
||||
cls=f"rounded-lg border-2 {color} flex text-center px-2"
|
||||
),
|
||||
H1(component_id, cls="ml-4"),
|
||||
cls="flex mb-2"
|
||||
)
|
||||
|
||||
def _mk_select():
|
||||
return Select(
|
||||
*[Option(processor_name, selected="selected" if processor_name == selected_processor_name else None)
|
||||
for processor_name in PROCESSOR_TYPES[component.type]],
|
||||
cls="select select-sm w-64 mb-2",
|
||||
id="processor_name",
|
||||
name="processor_name",
|
||||
**self.commands.select_processor(component_id)
|
||||
)
|
||||
|
||||
if self._state.selected_component_id is None or self._state.selected_component_id not in self._state.components:
|
||||
return None
|
||||
else:
|
||||
component_id = self._state.selected_component_id
|
||||
component = self._state.components[component_id]
|
||||
selected_processor_name = self._state.selected_processor_name.get(component_id, None)
|
||||
icon = COMPONENT_TYPES[component.type]["icon"]
|
||||
color = COMPONENT_TYPES[component.type]["color"]
|
||||
return Form(
|
||||
_mk_header(),
|
||||
_mk_select(),
|
||||
self._mk_processor_properties(component, selected_processor_name),
|
||||
mk_dialog_buttons(cls="mt-4",
|
||||
on_ok=self.commands.save_properties(component_id),
|
||||
on_cancel=self.commands.cancel_properties(component_id)),
|
||||
cls="font-mono text-sm",
|
||||
)
|
||||
|
||||
def _mk_properties(self):
|
||||
return Div(
|
||||
self._mk_properties_details(),
|
||||
cls="p-2 bg-base-100 rounded-lg border",
|
||||
style=f"height:{self._get_properties_height()}px;",
|
||||
id=f"p_{self._id}",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _mk_jira_processor_details(component):
|
||||
return Div(
|
||||
Fieldset(
|
||||
Legend("JQL", cls="fieldset-legend"),
|
||||
Input(type="text",
|
||||
name="jira_jql",
|
||||
value=component.properties.get("jira_jql", ""),
|
||||
placeholder="Enter JQL",
|
||||
cls="input w-full"),
|
||||
P("Write your jsl code"),
|
||||
cls="fieldset bg-base-200 border-base-300 rounded-box border p-4"
|
||||
),
|
||||
)
|
||||
|
||||
def _mk_repository_processor_details(self, component):
|
||||
selected_repo = component.properties.get("repository", None)
|
||||
selected_table = component.properties.get("table", None)
|
||||
|
||||
return Div(
|
||||
Fieldset(
|
||||
Legend("Repository", cls="fieldset-legend"),
|
||||
Div(
|
||||
Select(
|
||||
*[Option(repo.name, selected="selected" if repo.name == selected_repo else None)
|
||||
for repo in DbManagementHelper.list_repositories(self._session)],
|
||||
cls="select w-64",
|
||||
id=f"repository_{self._id}",
|
||||
name="repository",
|
||||
**self.commands.on_processor_details_event(component.id, "OnRepositoryChanged"),
|
||||
),
|
||||
Select(
|
||||
*[Option(table, selected="selected" if table == selected_table else None)
|
||||
for table in DbManagementHelper.list_tables(self._session, selected_repo)],
|
||||
cls="select w-64 ml-4",
|
||||
id=f"table_{self._id}",
|
||||
name="table",
|
||||
),
|
||||
cls="flex",
|
||||
),
|
||||
P("Select the source table"),
|
||||
cls="fieldset bg-base-200 border-base-300 rounded-box border p-4"
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
def _get_properties_height(self):
|
||||
print(f"height: {self._boundaries['height']}")
|
||||
return self._boundaries["height"] - self._state.designer_height - 86
|
||||
|
||||
@staticmethod
|
||||
def create_component_id(session, suffix=None):
|
||||
prefix = f"{WORKFLOW_DESIGNER_INSTANCE_ID}{session['user_id']}"
|
||||
if suffix is None:
|
||||
suffix = get_unique_id()
|
||||
|
||||
return make_safe_id(f"{prefix}{suffix}")
|
||||
|
||||
@staticmethod
|
||||
def _mk_toolbox_item(component_type: str, info: dict):
|
||||
return Div(
|
||||
mk_tooltip(
|
||||
Div(
|
||||
Span(info["icon"], cls="mb-2"),
|
||||
H4(info["title"], cls="font-semibold text-xs"),
|
||||
cls=f"p-2 rounded-lg border-2 {info['color']} flex text-center"
|
||||
),
|
||||
tooltip=info["description"]),
|
||||
cls="wkf-toolbox-item p-2",
|
||||
draggable="true",
|
||||
data_type=component_type
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _mk_workflow_component(component: WorkflowComponent):
|
||||
info = COMPONENT_TYPES[component.type]
|
||||
return Div(
|
||||
# Input connection point
|
||||
Div(cls="wkf-connection-point wkf-input-point",
|
||||
data_component_id=component.id,
|
||||
data_point_type="input"),
|
||||
|
||||
# Component content
|
||||
Div(
|
||||
Span(info["icon"], cls="text-xl mb-1"),
|
||||
H4(component.title, cls="font-semibold text-xs"),
|
||||
cls=f"p-3 rounded-lg border-2 {info['color']} bg-white shadow-lg flex items-center"
|
||||
),
|
||||
|
||||
# Output connection point
|
||||
Div(cls="wkf-connection-point wkf-output-point",
|
||||
data_component_id=component.id,
|
||||
data_point_type="output"),
|
||||
|
||||
cls="wkf-workflow-component w-32",
|
||||
style=f"left: {component.x}px; top: {component.y}px;",
|
||||
data_component_id=component.id,
|
||||
draggable="true"
|
||||
)
|
||||
|
||||
@@ -18,3 +18,7 @@ class Routes:
|
||||
DeleteComponent = "/delete-component"
|
||||
AddConnection = "/add-connection"
|
||||
ResizeDesigner = "/resize-designer"
|
||||
SaveProperties = "/save-properties"
|
||||
CancelProperties = "/cancel-properties"
|
||||
SelectProcessor = "/select-processor"
|
||||
OnProcessorDetailsEvent = "/on-processor-details-event"
|
||||
@@ -17,6 +17,7 @@ class WorkflowComponent:
|
||||
y: int
|
||||
title: str
|
||||
description: str
|
||||
properties: dict = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -37,6 +38,8 @@ class WorkflowsDesignerState:
|
||||
connections: list[Connection] = field(default_factory=list)
|
||||
component_counter = 0
|
||||
designer_height = 230
|
||||
selected_component_id = None
|
||||
selected_processor_name: dict = field(default_factory=dict) # selected processor for each component
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
14
src/utils/DbManagementHelper.py
Normal file
14
src/utils/DbManagementHelper.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from utils.ComponentsInstancesHelper import ComponentsInstancesHelper
|
||||
|
||||
|
||||
class DbManagementHelper:
|
||||
@staticmethod
|
||||
def list_repositories(session):
|
||||
return ComponentsInstancesHelper.get_repositories(session).db.get_repositories()
|
||||
|
||||
@staticmethod
|
||||
def list_tables(session, repository_name):
|
||||
if not repository_name:
|
||||
return []
|
||||
repository = ComponentsInstancesHelper.get_repositories(session).db.get_repository(repository_name)
|
||||
return repository.tables
|
||||
Reference in New Issue
Block a user