diff --git a/docs/deployment_guide/dashboards/BUILD b/docs/deployment_guide/dashboards/BUILD new file mode 100644 index 0000000000..faad6d9fdd --- /dev/null +++ b/docs/deployment_guide/dashboards/BUILD @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +load("//bzl:py.bzl", "osmo_py_test") + +osmo_py_test( + name = "test_dashboards", + srcs = ["test_dashboards.py"], + data = ["workflow_resources_usage.json"], +) diff --git a/docs/deployment_guide/dashboards/test_dashboards.py b/docs/deployment_guide/dashboards/test_dashboards.py new file mode 100644 index 0000000000..285da04b54 --- /dev/null +++ b/docs/deployment_guide/dashboards/test_dashboards.py @@ -0,0 +1,73 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +"""Structural checks for the Grafana dashboard JSON definitions.""" + +import json +import pathlib +import unittest +from typing import Any + + +DASHBOARD_DIRECTORY = pathlib.Path(__file__).parent + + +def _load_dashboard(filename: str) -> dict[str, Any]: + with (DASHBOARD_DIRECTORY / filename).open(encoding='utf-8') as dashboard_file: + return json.load(dashboard_file) + + +def _variables(dashboard: dict[str, Any]) -> dict[str, dict[str, Any]]: + return { + variable['name']: variable + for variable in dashboard.get('templating', {}).get('list', []) + } + + +def _query(variable: dict[str, Any]) -> str: + # Grafana stores template-variable queries as either a raw string or a + # {query: ...} object depending on the dashboard schema version. + query = variable.get('query', '') + return query.get('query', '') if isinstance(query, dict) else query + + +class WorkflowResourcesDashboardTest(unittest.TestCase): + """Structural and deployment-neutrality checks for the OSS dashboard.""" + + dashboard: dict[str, Any] + + @classmethod + def setUpClass(cls) -> None: + cls.dashboard = _load_dashboard('workflow_resources_usage.json') + + def test_has_workflow_resource_panels(self): + # Floor, not an exact count: panels may be added freely. + self.assertGreaterEqual(len(self.dashboard['panels']), 8) + + def test_dashboard_is_deployment_neutral(self): + variables = _variables(self.dashboard) + self.assertNotIn('project', variables) + uuid_query = _query(variables['uuid']) + self.assertIn('kube_pod_info', uuid_query) + self.assertNotIn('label_project', json.dumps(self.dashboard)) + + def test_panel_ids_are_unique(self): + panel_ids = [panel['id'] for panel in self.dashboard['panels']] + self.assertEqual(len(panel_ids), len(set(panel_ids))) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/deployment_guide/references/configs_definitions/workflow.rst b/docs/deployment_guide/references/configs_definitions/workflow.rst index 22d2d5c11b..28ad9a910a 100644 --- a/docs/deployment_guide/references/configs_definitions/workflow.rst +++ b/docs/deployment_guide/references/configs_definitions/workflow.rst @@ -310,7 +310,19 @@ Existing and in-flight workflows are not modified, although their detail-page warnings always reflect the current warn policy. In ConfigMap mode, an invalid edit is rejected and the previous valid snapshot remains active. -Admission emits +Only configured policy keys become workflow-label dimensions on +``osmo_tasks_count``. Attribute names start with ``workflow_label_``. Letters +and numbers are unchanged; ``_``, ``-``, ``.``, and ``/`` are encoded as +``__``, ``_dash_``, ``_dot_``, and ``_slash_`` respectively. For example, +``project`` is exported as ``workflow_label_project``. Values in the configured +allow-list are exported verbatim; a present value outside that list is clamped +to ````, and a missing key is reported as ````. Angle +brackets are not valid in label values, so the sentinels never collide with +real values. An empty allow-list exports every present value as ````. +This keeps the number of series bounded to the allow-list plus two sentinels +per key. + +Admission also emits ``osmo_label_validation_total{key, outcome}``, where ``outcome`` is ``ok``, ``missing``, ``invalid``, or ``rejected``. The counter covers rejected submissions that do not create a workflow row. Keep the policy list small to diff --git a/src/service/core/workflow/helpers.py b/src/service/core/workflow/helpers.py index db922415cd..a7245df118 100644 --- a/src/service/core/workflow/helpers.py +++ b/src/service/core/workflow/helpers.py @@ -531,7 +531,8 @@ def get_recent_tasks(database: connectors.PostgresConnector, minutes_ago: How many minutes back to look for completed tasks Returns: - List of task records with task and workflow information + Aggregated task-count rows grouped by pool, user, workflow, task + status, and the workflow's labels """ now = datetime.datetime.now(datetime.timezone.utc) cutoff_time = now - datetime.timedelta(minutes=minutes_ago) @@ -542,7 +543,9 @@ def get_recent_tasks(database: connectors.PostgresConnector, w.pool AS pool, w.submitted_by AS user, w.workflow_uuid AS workflow_uuid, - t.status AS status + t.status AS status, + w.labels AS labels, + COUNT(*) AS count FROM tasks t JOIN @@ -551,7 +554,7 @@ def get_recent_tasks(database: connectors.PostgresConnector, (t.end_time is NULL AND w.status IN ('WAITING', 'PENDING', 'RUNNING')) OR t.end_time > %s - GROUP BY w.pool, w.submitted_by, t.status, w.workflow_uuid + GROUP BY w.pool, w.submitted_by, w.workflow_uuid, t.status, w.labels """ return database.execute_fetch_command(query, (cutoff_time,), True) diff --git a/src/service/core/workflow/tests/BUILD b/src/service/core/workflow/tests/BUILD index 0521907322..95878c10c5 100644 --- a/src/service/core/workflow/tests/BUILD +++ b/src/service/core/workflow/tests/BUILD @@ -104,3 +104,13 @@ osmo_py_test( ], tags = ["requires-network"], ) + +py_test( + name = "test_workflow_metrics", + srcs = ["test_workflow_metrics.py"], + deps = [ + "//src/service/core/workflow", + "//src/utils/connectors", + "//src/utils/metrics", + ], +) diff --git a/src/service/core/workflow/tests/test_helpers.py b/src/service/core/workflow/tests/test_helpers.py index 6c4a132b39..f5974c893d 100644 --- a/src/service/core/workflow/tests/test_helpers.py +++ b/src/service/core/workflow/tests/test_helpers.py @@ -595,6 +595,18 @@ def test_get_router_cookie_joins_multiple_cookies_with_comma(self): class TestGetRecentTasks(unittest.TestCase): + def test_get_recent_tasks_selects_labels_and_count(self): + database = mock.Mock() + database.execute_fetch_command.return_value = [] + + helpers.get_recent_tasks(database, minutes_ago=5) + + query = database.execute_fetch_command.call_args.args[0] + self.assertIn('w.labels AS labels', query) + self.assertIn('COUNT(*) AS count', query) + group_by_clause = query[query.index('GROUP BY'):] + self.assertIn('w.labels', group_by_clause) + def test_get_recent_tasks_passes_cutoff_time_to_database(self): database = mock.Mock() database.execute_fetch_command.return_value = [] diff --git a/src/service/core/workflow/tests/test_workflow_metrics.py b/src/service/core/workflow/tests/test_workflow_metrics.py new file mode 100644 index 0000000000..b80793f624 --- /dev/null +++ b/src/service/core/workflow/tests/test_workflow_metrics.py @@ -0,0 +1,296 @@ +""" +SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +""" + +import os +import types +import unittest +from unittest import mock + +from src.service.core.workflow import workflow_metrics +from src.utils import connectors + + +def _workflow_config( + policy_allow_lists: dict[str, list[str]] | None = None, +) -> types.SimpleNamespace: + policy_allow_lists = policy_allow_lists if policy_allow_lists is not None else {} + policy = [ + connectors.LabelPolicy(key=key, allow_list=allow_list) + for key, allow_list in policy_allow_lists.items() + ] + return types.SimpleNamespace( + labels_config=types.SimpleNamespace(policy=policy) + ) + + +class GetTaskMetricsTest(unittest.TestCase): + """Task counts are projected onto configured attribution dimensions.""" + + def setUp(self): + self._disable_metrics = os.environ.pop('OSMO_DISABLE_TASK_METRICS', None) + workflow_metrics._metric_cache.clear() # pylint: disable=protected-access + workflow_metrics._last_refresh_time = 0 # pylint: disable=protected-access + + def tearDown(self): + if self._disable_metrics is None: + os.environ.pop('OSMO_DISABLE_TASK_METRICS', None) + else: + os.environ['OSMO_DISABLE_TASK_METRICS'] = self._disable_metrics + workflow_metrics._metric_cache.clear() # pylint: disable=protected-access + workflow_metrics._last_refresh_time = 0 # pylint: disable=protected-access + + def _observe(self, database): + with mock.patch.object( + connectors.PostgresConnector, 'get_instance', return_value=database + ), mock.patch.object( + workflow_metrics.time, 'time', return_value=100 + ): + return list(workflow_metrics.get_task_metrics()) + + def test_sums_database_counts_and_collapses_non_policy_labels(self): + rows = [ + { + 'pool': 'pool-a', + 'user': 'alice', + 'workflow_uuid': 'workflow-1', + 'status': 'RUNNING', + 'labels': { + 'project': 'project-a', + 'cost-center': 'center-1', + 'experiment': 'first', + }, + 'count': 2, + }, + { + 'pool': 'pool-a', + 'user': 'alice', + 'workflow_uuid': 'workflow-1', + 'status': 'RUNNING', + 'labels': { + 'project': 'project-a', + 'cost-center': 'center-1', + 'experiment': 'second', + }, + 'count': 3, + }, + { + 'pool': 'pool-a', + 'user': 'alice', + 'workflow_uuid': 'workflow-1', + 'status': 'RUNNING', + 'labels': {'project': 'project-a'}, + 'count': 4, + }, + { + 'pool': 'pool-a', + 'user': 'alice', + 'workflow_uuid': 'workflow-1', + 'status': 'RUNNING', + 'labels': None, + 'count': 1, + }, + { + 'pool': 'pool-a', + 'user': 'alice', + 'workflow_uuid': 'workflow-1', + 'status': 'RUNNING', + 'labels': ['unexpected-type'], + 'count': 2, + }, + { + 'pool': 'pool-a', + 'user': 'alice', + 'workflow_uuid': 'workflow-1', + 'status': 'RUNNING', + 'labels': {'project': 'unattributed'}, + 'count': 6, + }, + ] + database = mock.Mock() + database.get_workflow_configs.return_value = _workflow_config({ + 'project': ['project-a'], + 'cost-center': ['center-1'], + }) + + with mock.patch.object( + workflow_metrics.helpers, 'get_recent_tasks', + return_value=rows): + observations = self._observe(database) + + counts = {} + for observation in observations: + attributes = observation.attributes + if attributes is None: + self.fail('Task metric observation is missing attributes.') + counts[( + attributes['workflow_label_project'], + attributes['workflow_label_cost_dash_center'], + )] = observation.value + self.assertEqual(attributes['pool'], 'pool-a') + self.assertEqual(attributes['user'], 'alice') + self.assertEqual(attributes['workflow_uuid'], 'workflow-1') + self.assertEqual(attributes['status'], 'RUNNING') + self.assertEqual(counts, { + ('project-a', 'center-1'): 5, + ('project-a', ''): 4, + ('', ''): 3, + ('', ''): 6, + }) + + def test_empty_allow_list_clamps_all_present_values_to_other(self): + label_policy = connectors.LabelPolicy(key='project') + + metric_value = workflow_metrics._workflow_label_metric_value # pylint: disable=protected-access + self.assertEqual( + metric_value({'project': 'arbitrary'}, label_policy), '') + self.assertEqual(metric_value({}, label_policy), '') + + def test_policy_label_attributes_cannot_overwrite_or_sanitize_to_same_name(self): + database = mock.Mock() + database.get_workflow_configs.return_value = _workflow_config({ + 'pool': ['label-pool'], + 'a.b': ['dot'], + 'a/b': ['slash'], + 'a_dot_b': ['literal-token'], + }) + rows = [{ + 'pool': 'pool-a', + 'user': 'alice', + 'workflow_uuid': 'workflow-1', + 'status': 'RUNNING', + 'labels': { + 'pool': 'label-pool', + 'a.b': 'dot', + 'a/b': 'slash', + 'a_dot_b': 'literal-token', + }, + 'count': 1, + }] + + with mock.patch.object( + workflow_metrics.helpers, 'get_recent_tasks', + return_value=rows): + observations = self._observe(database) + + attributes = observations[0].attributes + if attributes is None: + self.fail('Task metric observation is missing attributes.') + self.assertEqual(attributes['pool'], 'pool-a') + self.assertEqual(attributes['workflow_label_pool'], 'label-pool') + self.assertEqual(attributes['workflow_label_a_dot_b'], 'dot') + self.assertEqual(attributes['workflow_label_a_slash_b'], 'slash') + self.assertEqual( + attributes['workflow_label_a__dot__b'], 'literal-token' + ) + + def test_no_label_policies_keeps_base_dimensions_and_database_count(self): + database = mock.Mock() + database.get_workflow_configs.return_value = _workflow_config() + rows = [{ + 'pool': None, + 'user': 'alice', + 'workflow_uuid': 'workflow-1', + 'status': 'COMPLETED', + 'labels': {'experiment': 'ignored'}, + 'count': 7, + }] + + with mock.patch.object( + workflow_metrics.helpers, 'get_recent_tasks', + return_value=rows): + observations = self._observe(database) + + self.assertEqual(len(observations), 1) + self.assertEqual(observations[0].value, 7) + attributes = observations[0].attributes + if attributes is None: + self.fail('Task metric observation is missing attributes.') + self.assertEqual(dict(attributes), { + 'pool': 'unknown', + 'user': 'alice', + 'workflow_uuid': 'workflow-1', + 'status': 'COMPLETED', + }) + + def test_cache_is_reused_until_30_second_ttl_expires(self): + database = mock.Mock() + database.get_workflow_configs.return_value = _workflow_config({'project': ['project-a']}) + first_rows = [{ + 'pool': 'pool-a', + 'user': 'alice', + 'workflow_uuid': 'workflow-1', + 'status': 'RUNNING', + 'labels': {'project': 'project-a'}, + 'count': 2, + }] + refreshed_rows = [{ + **first_rows[0], + 'count': 9, + }] + + with mock.patch.object( + connectors.PostgresConnector, 'get_instance', return_value=database + ), mock.patch.object( + workflow_metrics.helpers, + 'get_recent_tasks', + side_effect=[first_rows, refreshed_rows], + ) as get_recent_tasks, mock.patch.object( + workflow_metrics.time, 'time', side_effect=[100, 129, 131] + ): + first = list(workflow_metrics.get_task_metrics()) + cached = list(workflow_metrics.get_task_metrics()) + refreshed = list(workflow_metrics.get_task_metrics()) + + self.assertEqual(first[0].value, 2) + self.assertEqual(cached[0].value, 2) + self.assertEqual(refreshed[0].value, 9) + self.assertEqual(get_recent_tasks.call_count, 2) + self.assertEqual(database.get_workflow_configs.call_count, 2) + + def test_disabled_metrics_do_not_query_database(self): + os.environ['OSMO_DISABLE_TASK_METRICS'] = 'true' + + with mock.patch.object( + connectors.PostgresConnector, 'get_instance' + ) as get_database: + observations = list(workflow_metrics.get_task_metrics()) + + self.assertEqual(observations, []) + get_database.assert_not_called() + + +class RegisterTaskMetricsTest(unittest.TestCase): + def test_description_mentions_curated_workflow_labels(self): + metric_creator = mock.Mock() + + with mock.patch.object( + workflow_metrics.metrics.MetricCreator, + 'get_meter_instance', + return_value=metric_creator, + ): + workflow_metrics.register_task_metrics() + + metric_creator.send_observable_gauge.assert_called_once() + kwargs = metric_creator.send_observable_gauge.call_args.kwargs + self.assertEqual(kwargs['name'], 'osmo_tasks_count') + self.assertEqual(kwargs['callbacks'], workflow_metrics.get_task_metrics) + self.assertIn('workflow labels', kwargs['description']) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/service/core/workflow/workflow_metrics.py b/src/service/core/workflow/workflow_metrics.py index 2c8985f2fc..2dc75eb955 100644 --- a/src/service/core/workflow/workflow_metrics.py +++ b/src/service/core/workflow/workflow_metrics.py @@ -1,5 +1,5 @@ """ -SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # pylint: disable=line-too-long Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,10 +16,11 @@ SPDX-License-Identifier: Apache-2.0 """ +from collections.abc import Mapping import logging import os import time -from typing import Dict, Iterable, List, Tuple +from typing import Any, Dict, Iterable, List, Tuple # Import with type: ignore to avoid import errors in linting import opentelemetry.metrics as otelmetrics # type: ignore @@ -34,6 +35,17 @@ _metric_cache: List[otelmetrics.Observation] = [] _last_refresh_time: float = 0 _CACHE_TTL_SECONDS: int = 30 # Refresh cache every 30 seconds +# Angle brackets are not valid label characters, so these sentinels can +# never collide with real label values. +_MISSING_WORKFLOW_LABEL_VALUE = '' +_OTHER_WORKFLOW_LABEL_VALUE = '' +_WORKFLOW_LABEL_ATTRIBUTE_PREFIX = 'workflow_label_' +_WORKFLOW_LABEL_ATTRIBUTE_ESCAPES = { + '_': '__', + '-': '_dash_', + '.': '_dot_', + '/': '_slash_', +} def _is_task_metrics_disabled() -> bool: @@ -43,6 +55,42 @@ def _is_task_metrics_disabled() -> bool: ) +def _parse_workflow_labels(raw_labels: Any) -> Dict[str, str]: + """Return string workflow labels from a decoded JSONB row value.""" + if not isinstance(raw_labels, Mapping): + return {} + return { + key: value + for key, value in raw_labels.items() + if isinstance(value, str) + } + + +def _workflow_label_attribute_name(label_key: str) -> str: + """Build a readable, collision-free Prometheus-safe attribute name.""" + encoded_key = ''.join( + _WORKFLOW_LABEL_ATTRIBUTE_ESCAPES.get(character, character) + for character in label_key + ) + return f'{_WORKFLOW_LABEL_ATTRIBUTE_PREFIX}{encoded_key}' + + +def _workflow_label_metric_value( + workflow_labels: Dict[str, str], + label_policy: connectors.LabelPolicy) -> str: + """Clamp a policy label to its bounded metric vocabulary. + + With an empty allow-list every present value clamps to the other + sentinel, so series stay bounded in every enforcement mode. + """ + value = workflow_labels.get(label_policy.key) + if value is None: + return _MISSING_WORKFLOW_LABEL_VALUE + if not label_policy.allow_list or value not in label_policy.allow_list: + return _OTHER_WORKFLOW_LABEL_VALUE + return value + + def get_task_metrics( *args, # pylint: disable=unused-argument minutes_ago: int = 5 @@ -94,8 +142,11 @@ def get_task_metrics( prev_age ) + label_policies: List[connectors.LabelPolicy] = [] try: database = connectors.PostgresConnector.get_instance() + workflow_config = database.get_workflow_configs() + label_policies = workflow_config.labels_config.policy rows = helpers.get_recent_tasks(database, minutes_ago) except osmo_errors.OSMODatabaseError as err: logging.debug( @@ -104,17 +155,27 @@ def get_task_metrics( ) rows = [] - # Count tasks by unique label combinations + policy_attributes = [ + (_workflow_label_attribute_name(label_policy.key), label_policy) + for label_policy in label_policies + ] + # SQL groups by the raw labels JSONB; clamping to the bounded policy + # vocabulary can collapse distinct rows onto the same attribute set, so + # counts are summed per projected key. task_counts: Dict[Tuple[Tuple[str, str], ...], int] = {} for row in rows: + workflow_labels = _parse_workflow_labels(row['labels']) labels = { 'pool': row['pool'] or 'unknown', 'user': row['user'], 'workflow_uuid': row['workflow_uuid'], 'status': row['status'] } + for attribute_name, label_policy in policy_attributes: + labels[attribute_name] = _workflow_label_metric_value( + workflow_labels, label_policy) key = tuple(sorted(labels.items())) - task_counts[key] = task_counts.get(key, 0) + 1 + task_counts[key] = task_counts.get(key, 0) + int(row['count']) # Generate observations _metric_cache.clear() @@ -140,7 +201,10 @@ def register_task_metrics(): metric_creator.send_observable_gauge( name='osmo_tasks_count', callbacks=get_task_metrics, - description='Count of OSMO tasks by status, pool, workflow', + description=( + 'Count of OSMO tasks by status, pool, workflow, ' + 'and prefixed curated workflow labels' + ), unit='count' ) except (ValueError, AttributeError, TypeError) as err: diff --git a/src/ui/e2e/journeys/compact-mode-toggle.spec.ts b/src/ui/e2e/journeys/compact-mode-toggle.spec.ts index 4908ef6b49..d33d326f6c 100644 --- a/src/ui/e2e/journeys/compact-mode-toggle.spec.ts +++ b/src/ui/e2e/journeys/compact-mode-toggle.spec.ts @@ -15,15 +15,8 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - createPoolResponse, - PoolStatus, -} from "@/mocks/factories"; -import { - setupDefaultMocks, - setupPools, - setupProfile, -} from "@/e2e/utils/mock-setup"; +import { createPoolResponse, PoolStatus } from "@/mocks/factories"; +import { setupDefaultMocks, setupPools, setupProfile } from "@/e2e/utils/mock-setup"; /** * Compact Mode Toggle Tests @@ -68,9 +61,7 @@ test.describe("Compact Mode Toggle — Pools Page", () => { await page.waitForLoadState("networkidle"); // ASSERT — default is comfortable (not compact) - await expect( - page.getByRole("button", { name: /currently in comfortable view/i }), - ).toBeVisible(); + await expect(page.getByRole("button", { name: /currently in comfortable view/i })).toBeVisible(); }); test("clicking toggle switches to compact view", async ({ page }) => { @@ -83,9 +74,7 @@ test.describe("Compact Mode Toggle — Pools Page", () => { await toggleButton.click(); // ASSERT — now in compact mode - await expect( - page.getByRole("button", { name: /currently in compact view/i }), - ).toBeVisible(); + await expect(page.getByRole("button", { name: /currently in compact view/i })).toBeVisible(); }); test("clicking toggle twice returns to comfortable view", async ({ page }) => { @@ -96,16 +85,12 @@ test.describe("Compact Mode Toggle — Pools Page", () => { // Toggle to compact const toggleButton = page.getByRole("button", { name: /currently in comfortable view/i }); await toggleButton.click(); - await expect( - page.getByRole("button", { name: /currently in compact view/i }), - ).toBeVisible(); + await expect(page.getByRole("button", { name: /currently in compact view/i })).toBeVisible(); // Toggle back to comfortable await page.getByRole("button", { name: /currently in compact view/i }).click(); // ASSERT — back to comfortable - await expect( - page.getByRole("button", { name: /currently in comfortable view/i }), - ).toBeVisible(); + await expect(page.getByRole("button", { name: /currently in comfortable view/i })).toBeVisible(); }); }); diff --git a/src/ui/e2e/journeys/cross-page-navigation.spec.ts b/src/ui/e2e/journeys/cross-page-navigation.spec.ts index 543d7ce9ff..ab71694a50 100644 --- a/src/ui/e2e/journeys/cross-page-navigation.spec.ts +++ b/src/ui/e2e/journeys/cross-page-navigation.spec.ts @@ -15,15 +15,8 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - createPoolResponse, - PoolStatus, -} from "@/mocks/factories"; -import { - setupDefaultMocks, - setupProfile, - setupPools, -} from "@/e2e/utils/mock-setup"; +import { createPoolResponse, PoolStatus } from "@/mocks/factories"; +import { setupDefaultMocks, setupProfile, setupPools } from "@/e2e/utils/mock-setup"; /** * Pool Quick Links Navigation Tests @@ -51,9 +44,7 @@ test.describe("Pool Quick Links Navigation", () => { ); }); - test("resources quick link navigates to resources filtered by pool", async ({ - page, - }) => { + test("resources quick link navigates to resources filtered by pool", async ({ page }) => { // ACT await page.goto("/pools?all=true&view=prod-gpu"); await page.waitForLoadState("networkidle"); @@ -73,9 +64,7 @@ test.describe("Pool Quick Links Navigation", () => { await expect(page).toHaveURL(/prod-gpu/); }); - test("workflows quick link navigates to workflows filtered by pool", async ({ - page, - }) => { + test("workflows quick link navigates to workflows filtered by pool", async ({ page }) => { // ACT await page.goto("/pools?all=true&view=prod-gpu"); await page.waitForLoadState("networkidle"); @@ -92,9 +81,7 @@ test.describe("Pool Quick Links Navigation", () => { await expect(page).toHaveURL(/prod-gpu/); }); - test("occupancy quick link navigates to occupancy filtered by pool", async ({ - page, - }) => { + test("occupancy quick link navigates to occupancy filtered by pool", async ({ page }) => { // ACT await page.goto("/pools?all=true&view=prod-gpu"); await page.waitForLoadState("networkidle"); @@ -111,9 +98,7 @@ test.describe("Pool Quick Links Navigation", () => { await expect(page).toHaveURL(/prod-gpu/); }); - test("quick links show correct href attributes before clicking", async ({ - page, - }) => { + test("quick links show correct href attributes before clicking", async ({ page }) => { // ACT await page.goto("/pools?all=true&view=prod-gpu"); await page.waitForLoadState("networkidle"); diff --git a/src/ui/e2e/journeys/dashboard-errors.spec.ts b/src/ui/e2e/journeys/dashboard-errors.spec.ts index 7bc1421cc7..a860e41e44 100644 --- a/src/ui/e2e/journeys/dashboard-errors.spec.ts +++ b/src/ui/e2e/journeys/dashboard-errors.spec.ts @@ -15,18 +15,8 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - createPoolResponse, - createWorkflowsResponse, - PoolStatus, - WorkflowStatus, -} from "@/mocks/factories"; -import { - setupDefaultMocks, - setupProfile, - setupPools, - setupWorkflows, -} from "@/e2e/utils/mock-setup"; +import { createPoolResponse, createWorkflowsResponse, PoolStatus, WorkflowStatus } from "@/mocks/factories"; +import { setupDefaultMocks, setupProfile, setupPools, setupWorkflows } from "@/e2e/utils/mock-setup"; /** * Dashboard Error & Edge Case Tests diff --git a/src/ui/e2e/journeys/dashboard.spec.ts b/src/ui/e2e/journeys/dashboard.spec.ts index 71cd6a702d..cde14f3115 100644 --- a/src/ui/e2e/journeys/dashboard.spec.ts +++ b/src/ui/e2e/journeys/dashboard.spec.ts @@ -15,18 +15,8 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - createPoolResponse, - createWorkflowsResponse, - PoolStatus, - WorkflowStatus, -} from "@/mocks/factories"; -import { - setupDefaultMocks, - setupPools, - setupProfile, - setupWorkflows, -} from "@/e2e/utils/mock-setup"; +import { createPoolResponse, createWorkflowsResponse, PoolStatus, WorkflowStatus } from "@/mocks/factories"; +import { setupDefaultMocks, setupPools, setupProfile, setupWorkflows } from "@/e2e/utils/mock-setup"; /** * Dashboard Journey Tests @@ -109,9 +99,7 @@ test.describe("Dashboard Recent Workflows", () => { await setupPools(page, createPoolResponse([{ name: "prod", status: PoolStatus.ONLINE }])); await setupWorkflows( page, - createWorkflowsResponse([ - { name: "recent-e2e-workflow", status: WorkflowStatus.COMPLETED, user: "test-user" }, - ]), + createWorkflowsResponse([{ name: "recent-e2e-workflow", status: WorkflowStatus.COMPLETED, user: "test-user" }]), ); // ACT @@ -170,12 +158,7 @@ test.describe("Dashboard Stat Card Links", () => { test("Active Workflows stat card links to workflows filtered by RUNNING status", async ({ page }) => { // ARRANGE await setupPools(page, createPoolResponse([{ name: "prod", status: PoolStatus.ONLINE }])); - await setupWorkflows( - page, - createWorkflowsResponse([ - { name: "running-1", status: WorkflowStatus.RUNNING }, - ]), - ); + await setupWorkflows(page, createWorkflowsResponse([{ name: "running-1", status: WorkflowStatus.RUNNING }])); // ACT await page.goto("/"); @@ -276,12 +259,7 @@ test.describe("Dashboard Edge Cases", () => { test("failed workflows stat card links to workflows filtered by FAILED status", async ({ page }) => { // ARRANGE await setupPools(page, createPoolResponse([{ name: "prod", status: PoolStatus.ONLINE }])); - await setupWorkflows( - page, - createWorkflowsResponse([ - { name: "failed-1", status: WorkflowStatus.FAILED }, - ]), - ); + await setupWorkflows(page, createWorkflowsResponse([{ name: "failed-1", status: WorkflowStatus.FAILED }])); // ACT await page.goto("/"); @@ -299,12 +277,7 @@ test.describe("Dashboard Edge Cases", () => { test("completed workflows stat card links to workflows filtered by COMPLETED status", async ({ page }) => { // ARRANGE await setupPools(page, createPoolResponse([{ name: "prod", status: PoolStatus.ONLINE }])); - await setupWorkflows( - page, - createWorkflowsResponse([ - { name: "completed-1", status: WorkflowStatus.COMPLETED }, - ]), - ); + await setupWorkflows(page, createWorkflowsResponse([{ name: "completed-1", status: WorkflowStatus.COMPLETED }])); // ACT await page.goto("/"); diff --git a/src/ui/e2e/journeys/display-mode-toggle.spec.ts b/src/ui/e2e/journeys/display-mode-toggle.spec.ts index 21c6db2b7c..2ec3d0ac2d 100644 --- a/src/ui/e2e/journeys/display-mode-toggle.spec.ts +++ b/src/ui/e2e/journeys/display-mode-toggle.spec.ts @@ -15,10 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - createResourcesResponse, - BackendResourceType, -} from "@/mocks/factories"; +import { createResourcesResponse, BackendResourceType } from "@/mocks/factories"; import { setupDefaultMocks, setupResources, setupProfile } from "@/e2e/utils/mock-setup"; /** diff --git a/src/ui/e2e/journeys/empty-states.spec.ts b/src/ui/e2e/journeys/empty-states.spec.ts index d2db087b08..6ec54d5de7 100644 --- a/src/ui/e2e/journeys/empty-states.spec.ts +++ b/src/ui/e2e/journeys/empty-states.spec.ts @@ -22,13 +22,7 @@ import { WorkflowStatus, PoolStatus, } from "@/mocks/factories"; -import { - setupDefaultMocks, - setupProfile, - setupWorkflows, - setupPools, - setupResources, -} from "@/e2e/utils/mock-setup"; +import { setupDefaultMocks, setupProfile, setupWorkflows, setupPools, setupResources } from "@/e2e/utils/mock-setup"; /** * Empty State Tests diff --git a/src/ui/e2e/journeys/log-viewer-recent.spec.ts b/src/ui/e2e/journeys/log-viewer-recent.spec.ts index 4440039352..074df1e85d 100644 --- a/src/ui/e2e/journeys/log-viewer-recent.spec.ts +++ b/src/ui/e2e/journeys/log-viewer-recent.spec.ts @@ -15,10 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - setupDefaultMocks, - setupProfile, -} from "@/e2e/utils/mock-setup"; +import { setupDefaultMocks, setupProfile } from "@/e2e/utils/mock-setup"; /** * Log Viewer Recent Workflows Tests @@ -81,10 +78,7 @@ test.describe("Log Viewer Recent Workflows", () => { // ARRANGE await page.goto("/log-viewer"); await page.evaluate(() => { - localStorage.setItem( - "osmo:recent-workflows", - JSON.stringify(["wf-1", "wf-2"]), - ); + localStorage.setItem("osmo:recent-workflows", JSON.stringify(["wf-1", "wf-2"])); }); await page.reload(); await page.waitForLoadState("networkidle"); @@ -103,10 +97,7 @@ test.describe("Log Viewer Recent Workflows", () => { // ARRANGE await page.goto("/log-viewer"); await page.evaluate(() => { - localStorage.setItem( - "osmo:recent-workflows", - JSON.stringify(["my-recent-workflow"]), - ); + localStorage.setItem("osmo:recent-workflows", JSON.stringify(["my-recent-workflow"])); }); await page.reload(); await page.waitForLoadState("networkidle"); @@ -122,10 +113,7 @@ test.describe("Log Viewer Recent Workflows", () => { // ARRANGE await page.goto("/log-viewer"); await page.evaluate(() => { - localStorage.setItem( - "osmo:recent-workflows", - JSON.stringify(["keep-this", "remove-this"]), - ); + localStorage.setItem("osmo:recent-workflows", JSON.stringify(["keep-this", "remove-this"])); }); await page.reload(); await page.waitForLoadState("networkidle"); @@ -137,9 +125,7 @@ test.describe("Log Viewer Recent Workflows", () => { // ACT — hover over "remove-this" to make remove button visible, then click it const removeThisEntry = page.getByText("remove-this").first(); await removeThisEntry.hover(); - await page - .getByRole("button", { name: /remove remove-this from recent workflows/i }) - .click(); + await page.getByRole("button", { name: /remove remove-this from recent workflows/i }).click(); // ASSERT — "remove-this" is gone, "keep-this" remains await expect(page.getByText("remove-this")).not.toBeVisible(); diff --git a/src/ui/e2e/journeys/log-viewer.spec.ts b/src/ui/e2e/journeys/log-viewer.spec.ts index 6b2d5b06af..dc48ba4519 100644 --- a/src/ui/e2e/journeys/log-viewer.spec.ts +++ b/src/ui/e2e/journeys/log-viewer.spec.ts @@ -15,10 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - setupDefaultMocks, - setupProfile, -} from "@/e2e/utils/mock-setup"; +import { setupDefaultMocks, setupProfile } from "@/e2e/utils/mock-setup"; /** * Log Viewer Page Journey Tests diff --git a/src/ui/e2e/journeys/not-found-page.spec.ts b/src/ui/e2e/journeys/not-found-page.spec.ts index c29ba22bde..5033b87494 100644 --- a/src/ui/e2e/journeys/not-found-page.spec.ts +++ b/src/ui/e2e/journeys/not-found-page.spec.ts @@ -85,10 +85,7 @@ test.describe("Not Found Page Content", () => { await expect(dashboardAction).toBeVisible(); // Click and wait for navigation - await Promise.all([ - page.waitForURL(/\/$/), - dashboardAction.click(), - ]); + await Promise.all([page.waitForURL(/\/$/), dashboardAction.click()]); // ASSERT — navigated to home await expect(page).toHaveURL(/\/$/); diff --git a/src/ui/e2e/journeys/occupancy-toolbar.spec.ts b/src/ui/e2e/journeys/occupancy-toolbar.spec.ts index bc17b94e6e..5621b2f7be 100644 --- a/src/ui/e2e/journeys/occupancy-toolbar.spec.ts +++ b/src/ui/e2e/journeys/occupancy-toolbar.spec.ts @@ -140,12 +140,7 @@ test.describe("Occupancy Group By Toggle URL State", () => { test("clicking By User updates URL with groupBy=user", async ({ page }) => { // ARRANGE - await setupOccupancy( - page, - createOccupancySummaries([ - { user: "alice", pool: "prod", gpu: 4 }, - ]), - ); + await setupOccupancy(page, createOccupancySummaries([{ user: "alice", pool: "prod", gpu: 4 }])); // ACT await page.goto("/occupancy"); @@ -160,12 +155,7 @@ test.describe("Occupancy Group By Toggle URL State", () => { test("clicking By Pool after By User removes groupBy from URL", async ({ page }) => { // ARRANGE - await setupOccupancy( - page, - createOccupancySummaries([ - { user: "alice", pool: "prod", gpu: 4 }, - ]), - ); + await setupOccupancy(page, createOccupancySummaries([{ user: "alice", pool: "prod", gpu: 4 }])); // ACT await page.goto("/occupancy?groupBy=user"); diff --git a/src/ui/e2e/journeys/occupancy-truncation.spec.ts b/src/ui/e2e/journeys/occupancy-truncation.spec.ts index 8aacf613e9..4a851e3c93 100644 --- a/src/ui/e2e/journeys/occupancy-truncation.spec.ts +++ b/src/ui/e2e/journeys/occupancy-truncation.spec.ts @@ -15,11 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - setupDefaultMocks, - setupProfile, - setupOccupancy, -} from "@/e2e/utils/mock-setup"; +import { setupDefaultMocks, setupProfile, setupOccupancy } from "@/e2e/utils/mock-setup"; /** * Occupancy Truncation Warning Tests @@ -53,8 +49,24 @@ function createLargeOccupancySummaries(count: number) { function createSmallOccupancySummaries() { return { summaries: [ - { user: "alice", pool: "prod", gpu: 8, cpu: 64, memory: 64 * 1024 * 1024 * 1024, storage: 100 * 1024 * 1024 * 1024, priority: "NORMAL" }, - { user: "bob", pool: "staging", gpu: 4, cpu: 32, memory: 32 * 1024 * 1024 * 1024, storage: 50 * 1024 * 1024 * 1024, priority: "NORMAL" }, + { + user: "alice", + pool: "prod", + gpu: 8, + cpu: 64, + memory: 64 * 1024 * 1024 * 1024, + storage: 100 * 1024 * 1024 * 1024, + priority: "NORMAL", + }, + { + user: "bob", + pool: "staging", + gpu: 4, + cpu: 32, + memory: 32 * 1024 * 1024 * 1024, + storage: 50 * 1024 * 1024 * 1024, + priority: "NORMAL", + }, ], }; } @@ -74,12 +86,8 @@ test.describe("Occupancy Truncation Warning", () => { await page.waitForLoadState("networkidle"); // ASSERT — truncation banner is visible - await expect( - page.getByText(/results may be incomplete/i).first(), - ).toBeVisible(); - await expect( - page.getByText(/10,000 row fetch limit/i).first(), - ).toBeVisible(); + await expect(page.getByText(/results may be incomplete/i).first()).toBeVisible(); + await expect(page.getByText(/10,000 row fetch limit/i).first()).toBeVisible(); }); test("does not show truncation warning for small datasets", async ({ page }) => { @@ -91,9 +99,7 @@ test.describe("Occupancy Truncation Warning", () => { await page.waitForLoadState("networkidle"); // ASSERT — no truncation warning visible - await expect( - page.getByText(/results may be incomplete/i), - ).not.toBeVisible(); + await expect(page.getByText(/results may be incomplete/i)).not.toBeVisible(); }); test("truncation warning does not block table rendering", async ({ page }) => { diff --git a/src/ui/e2e/journeys/occupancy.spec.ts b/src/ui/e2e/journeys/occupancy.spec.ts index 0175da9d98..27e4a3e4ab 100644 --- a/src/ui/e2e/journeys/occupancy.spec.ts +++ b/src/ui/e2e/journeys/occupancy.spec.ts @@ -15,11 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - setupDefaultMocks, - setupProfile, - setupOccupancy, -} from "@/e2e/utils/mock-setup"; +import { setupDefaultMocks, setupProfile, setupOccupancy } from "@/e2e/utils/mock-setup"; /** * Occupancy Page Journey Tests diff --git a/src/ui/e2e/journeys/panel-keyboard-interactions.spec.ts b/src/ui/e2e/journeys/panel-keyboard-interactions.spec.ts index ce5ffb182a..107f6fa1d4 100644 --- a/src/ui/e2e/journeys/panel-keyboard-interactions.spec.ts +++ b/src/ui/e2e/journeys/panel-keyboard-interactions.spec.ts @@ -213,10 +213,7 @@ test.describe("Pool Panel Quick Links Navigation", () => { test("Resources link navigates to resources with pool filter", async ({ page }) => { // ARRANGE - await setupPools( - page, - createPoolResponse([{ name: "nav-pool", status: PoolStatus.ONLINE }]), - ); + await setupPools(page, createPoolResponse([{ name: "nav-pool", status: PoolStatus.ONLINE }])); // ACT await page.goto("/pools?all=true&view=nav-pool"); @@ -237,10 +234,7 @@ test.describe("Pool Panel Quick Links Navigation", () => { test("Workflows link navigates to workflows with pool filter", async ({ page }) => { // ARRANGE - await setupPools( - page, - createPoolResponse([{ name: "wf-pool", status: PoolStatus.ONLINE }]), - ); + await setupPools(page, createPoolResponse([{ name: "wf-pool", status: PoolStatus.ONLINE }])); // ACT await page.goto("/pools?all=true&view=wf-pool"); diff --git a/src/ui/e2e/journeys/pools.spec.ts b/src/ui/e2e/journeys/pools.spec.ts index 5673103f2c..c57046f759 100644 --- a/src/ui/e2e/journeys/pools.spec.ts +++ b/src/ui/e2e/journeys/pools.spec.ts @@ -36,11 +36,14 @@ test.describe("Pools List", () => { }); test("renders all pools", async ({ page }) => { - await setupPools(page, createPoolResponse([ - { name: "production", status: PoolStatus.ONLINE }, - { name: "staging", status: PoolStatus.ONLINE }, - { name: "maintenance", status: PoolStatus.OFFLINE }, - ])); + await setupPools( + page, + createPoolResponse([ + { name: "production", status: PoolStatus.ONLINE }, + { name: "staging", status: PoolStatus.ONLINE }, + { name: "maintenance", status: PoolStatus.OFFLINE }, + ]), + ); await page.goto("/pools?all=true"); await page.waitForLoadState("networkidle"); @@ -78,10 +81,13 @@ test.describe("Pools List", () => { }); test("search creates a filter chip for the typed pool name", async ({ page }) => { - await setupPools(page, createPoolResponse([ - { name: "production", status: PoolStatus.ONLINE }, - { name: "development", status: PoolStatus.ONLINE }, - ])); + await setupPools( + page, + createPoolResponse([ + { name: "production", status: PoolStatus.ONLINE }, + { name: "development", status: PoolStatus.ONLINE }, + ]), + ); await page.goto("/pools?all=true"); await page.waitForLoadState("networkidle"); @@ -117,20 +123,23 @@ test.describe("Pool Panel", () => { }); test("shows GPU quota and capacity sections", async ({ page }) => { - await setupPools(page, createPoolResponse([ - { - name: "gpu-cluster", - status: PoolStatus.ONLINE, - resource_usage: { - quota_used: "50", - quota_free: "50", - quota_limit: "100", - total_usage: "64", - total_capacity: "128", - total_free: "64", + await setupPools( + page, + createPoolResponse([ + { + name: "gpu-cluster", + status: PoolStatus.ONLINE, + resource_usage: { + quota_used: "50", + quota_free: "50", + quota_limit: "100", + total_usage: "64", + total_capacity: "128", + total_free: "64", + }, }, - }, - ])); + ]), + ); await page.goto("/pools?all=true&view=gpu-cluster"); await page.waitForLoadState("networkidle"); @@ -164,9 +173,16 @@ test.describe("Pool Panel", () => { }); test("shows pool description when provided", async ({ page }) => { - await setupPools(page, createPoolResponse([ - { name: "described-pool", status: PoolStatus.ONLINE, description: "High-performance GPU cluster for AI training" }, - ])); + await setupPools( + page, + createPoolResponse([ + { + name: "described-pool", + status: PoolStatus.ONLINE, + description: "High-performance GPU cluster for AI training", + }, + ]), + ); await page.goto("/pools?all=true&view=described-pool"); await page.waitForLoadState("networkidle"); @@ -176,16 +192,19 @@ test.describe("Pool Panel", () => { }); test("shows platform configuration section for pools with platforms", async ({ page }) => { - await setupPools(page, createPoolResponse([ - { - name: "platform-pool", - status: PoolStatus.ONLINE, - platforms: { - dgx: { description: "DGX H100 nodes" }, - cpu: { description: "CPU-only nodes" }, + await setupPools( + page, + createPoolResponse([ + { + name: "platform-pool", + status: PoolStatus.ONLINE, + platforms: { + dgx: { description: "DGX H100 nodes" }, + cpu: { description: "CPU-only nodes" }, + }, }, - }, - ])); + ]), + ); await page.goto("/pools?all=true&view=platform-pool"); await page.waitForLoadState("networkidle"); @@ -217,9 +236,10 @@ test.describe("Pool Edge Cases", () => { }); test("offline pool is visible in the list", async ({ page }) => { - await setupPools(page, createPoolResponse([ - { name: "offline-pool", status: PoolStatus.OFFLINE, description: "Down for maintenance" }, - ])); + await setupPools( + page, + createPoolResponse([{ name: "offline-pool", status: PoolStatus.OFFLINE, description: "Down for maintenance" }]), + ); await page.goto("/pools?all=true"); await page.waitForLoadState("networkidle"); @@ -246,9 +266,7 @@ test.describe("Pool Toolbar", () => { }); test("has toolbar with search and column controls", async ({ page }) => { - await setupPools(page, createPoolResponse([ - { name: "toolbar-pool", status: PoolStatus.ONLINE }, - ])); + await setupPools(page, createPoolResponse([{ name: "toolbar-pool", status: PoolStatus.ONLINE }])); await page.goto("/pools?all=true"); await page.waitForLoadState("networkidle"); @@ -259,10 +277,13 @@ test.describe("Pool Toolbar", () => { }); test("shows results count", async ({ page }) => { - await setupPools(page, createPoolResponse([ - { name: "pool-1", status: PoolStatus.ONLINE }, - { name: "pool-2", status: PoolStatus.OFFLINE }, - ])); + await setupPools( + page, + createPoolResponse([ + { name: "pool-1", status: PoolStatus.ONLINE }, + { name: "pool-2", status: PoolStatus.OFFLINE }, + ]), + ); await page.goto("/pools?all=true"); await page.waitForLoadState("networkidle"); diff --git a/src/ui/e2e/journeys/resource-panel-content.spec.ts b/src/ui/e2e/journeys/resource-panel-content.spec.ts index c69316c13e..1ab10f03a0 100644 --- a/src/ui/e2e/journeys/resource-panel-content.spec.ts +++ b/src/ui/e2e/journeys/resource-panel-content.spec.ts @@ -15,17 +15,8 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - createPoolResponse, - createResourcesResponse, - PoolStatus, - BackendResourceType, -} from "@/mocks/factories"; -import { - setupDefaultMocks, - setupPools, - setupResources, -} from "@/e2e/utils/mock-setup"; +import { createPoolResponse, createResourcesResponse, PoolStatus, BackendResourceType } from "@/mocks/factories"; +import { setupDefaultMocks, setupPools, setupResources } from "@/e2e/utils/mock-setup"; /** * Resource Panel Content Tests @@ -46,10 +37,7 @@ import { test.describe("Resource Panel Capacity Display", () => { test.beforeEach(async ({ page }) => { await setupDefaultMocks(page); - await setupPools( - page, - createPoolResponse([{ name: "test-pool", status: PoolStatus.ONLINE }]), - ); + await setupPools(page, createPoolResponse([{ name: "test-pool", status: PoolStatus.ONLINE }])); }); test("shows hostname in resource panel", async ({ page }) => { @@ -193,10 +181,7 @@ test.describe("Resource Panel Platform Config", () => { test("shows RESERVED badge for reserved resources", async ({ page }) => { // ARRANGE - await setupPools( - page, - createPoolResponse([{ name: "test-pool", status: PoolStatus.ONLINE }]), - ); + await setupPools(page, createPoolResponse([{ name: "test-pool", status: PoolStatus.ONLINE }])); await setupResources( page, createResourcesResponse([ diff --git a/src/ui/e2e/journeys/resources.spec.ts b/src/ui/e2e/journeys/resources.spec.ts index f4f8ca87fa..7bb3b8b016 100644 --- a/src/ui/e2e/journeys/resources.spec.ts +++ b/src/ui/e2e/journeys/resources.spec.ts @@ -37,18 +37,21 @@ test.describe("Resources List", () => { }); test("shows resources from all pools", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "node-a.cluster", - exposed_fields: { node: "node-a", "pool/platform": ["pool-a/base"] }, - pool_platform_labels: { "pool-a": ["base"] }, - }, - { - hostname: "node-b.cluster", - exposed_fields: { node: "node-b", "pool/platform": ["pool-b/gpu"] }, - pool_platform_labels: { "pool-b": ["gpu"] }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "node-a.cluster", + exposed_fields: { node: "node-a", "pool/platform": ["pool-a/base"] }, + pool_platform_labels: { "pool-a": ["base"] }, + }, + { + hostname: "node-b.cluster", + exposed_fields: { node: "node-b", "pool/platform": ["pool-b/gpu"] }, + pool_platform_labels: { "pool-b": ["gpu"] }, + }, + ]), + ); await page.goto("/resources"); await page.waitForLoadState("networkidle"); @@ -58,23 +61,26 @@ test.describe("Resources List", () => { }); test("search creates a filter chip for the typed resource name", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "dgx-001.cluster", - exposed_fields: { node: "dgx-001", "pool/platform": ["prod/dgx"] }, - pool_platform_labels: { prod: ["dgx"] }, - }, - { - hostname: "dgx-002.cluster", - exposed_fields: { node: "dgx-002", "pool/platform": ["prod/dgx"] }, - pool_platform_labels: { prod: ["dgx"] }, - }, - { - hostname: "cpu-001.cluster", - exposed_fields: { node: "cpu-001", "pool/platform": ["prod/cpu"] }, - pool_platform_labels: { prod: ["cpu"] }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "dgx-001.cluster", + exposed_fields: { node: "dgx-001", "pool/platform": ["prod/dgx"] }, + pool_platform_labels: { prod: ["dgx"] }, + }, + { + hostname: "dgx-002.cluster", + exposed_fields: { node: "dgx-002", "pool/platform": ["prod/dgx"] }, + pool_platform_labels: { prod: ["dgx"] }, + }, + { + hostname: "cpu-001.cluster", + exposed_fields: { node: "cpu-001", "pool/platform": ["prod/cpu"] }, + pool_platform_labels: { prod: ["cpu"] }, + }, + ]), + ); await page.goto("/resources"); await page.waitForLoadState("networkidle"); @@ -93,18 +99,21 @@ test.describe("Resources List", () => { }); test("pool filter chip via URL shows only that pool's resources", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "prod-node.cluster", - exposed_fields: { node: "prod-node", "pool/platform": ["production/base"] }, - pool_platform_labels: { production: ["base"] }, - }, - { - hostname: "dev-node.cluster", - exposed_fields: { node: "dev-node", "pool/platform": ["development/base"] }, - pool_platform_labels: { development: ["base"] }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "prod-node.cluster", + exposed_fields: { node: "prod-node", "pool/platform": ["production/base"] }, + pool_platform_labels: { production: ["base"] }, + }, + { + hostname: "dev-node.cluster", + exposed_fields: { node: "dev-node", "pool/platform": ["development/base"] }, + pool_platform_labels: { development: ["base"] }, + }, + ]), + ); // Navigate with a pool chip pre-applied await page.goto("/resources?f=pool:production"); @@ -115,26 +124,29 @@ test.describe("Resources List", () => { }); test("shows all resource types in the table", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "shared-node.cluster", - resource_type: BackendResourceType.SHARED, - exposed_fields: { node: "shared-node", "pool/platform": ["prod/base"] }, - pool_platform_labels: { prod: ["base"] }, - }, - { - hostname: "reserved-node.cluster", - resource_type: BackendResourceType.RESERVED, - exposed_fields: { node: "reserved-node", "pool/platform": ["prod/base"] }, - pool_platform_labels: { prod: ["base"] }, - }, - { - hostname: "unused-node.cluster", - resource_type: BackendResourceType.UNUSED, - exposed_fields: { node: "unused-node", "pool/platform": ["prod/base"] }, - pool_platform_labels: { prod: ["base"] }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "shared-node.cluster", + resource_type: BackendResourceType.SHARED, + exposed_fields: { node: "shared-node", "pool/platform": ["prod/base"] }, + pool_platform_labels: { prod: ["base"] }, + }, + { + hostname: "reserved-node.cluster", + resource_type: BackendResourceType.RESERVED, + exposed_fields: { node: "reserved-node", "pool/platform": ["prod/base"] }, + pool_platform_labels: { prod: ["base"] }, + }, + { + hostname: "unused-node.cluster", + resource_type: BackendResourceType.UNUSED, + exposed_fields: { node: "unused-node", "pool/platform": ["prod/base"] }, + pool_platform_labels: { prod: ["base"] }, + }, + ]), + ); await page.goto("/resources"); await page.waitForLoadState("networkidle"); @@ -153,13 +165,16 @@ test.describe("Resource Panel", () => { }); test("clicking a resource row opens the details panel", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "gpu-node.cluster", - exposed_fields: { node: "gpu-node", "pool/platform": ["prod/dgx"] }, - pool_platform_labels: { prod: ["dgx"] }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "gpu-node.cluster", + exposed_fields: { node: "gpu-node", "pool/platform": ["prod/dgx"] }, + pool_platform_labels: { prod: ["dgx"] }, + }, + ]), + ); await page.goto("/resources"); await page.waitForLoadState("networkidle"); @@ -175,13 +190,16 @@ test.describe("Resource Panel", () => { }); test("navigating directly to a resource opens its panel", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "direct-node.cluster", - exposed_fields: { node: "direct-node", "pool/platform": ["prod/dgx"] }, - pool_platform_labels: { prod: ["dgx"] }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "direct-node.cluster", + exposed_fields: { node: "direct-node", "pool/platform": ["prod/dgx"] }, + pool_platform_labels: { prod: ["dgx"] }, + }, + ]), + ); await page.goto("/resources?view=direct-node"); await page.waitForLoadState("networkidle"); @@ -192,13 +210,16 @@ test.describe("Resource Panel", () => { }); test("closes with the close button and clears URL state", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "closeable-node.cluster", - exposed_fields: { node: "closeable-node", "pool/platform": ["prod/base"] }, - pool_platform_labels: { prod: ["base"] }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "closeable-node.cluster", + exposed_fields: { node: "closeable-node", "pool/platform": ["prod/base"] }, + pool_platform_labels: { prod: ["base"] }, + }, + ]), + ); await page.goto("/resources?view=closeable-node"); await page.waitForLoadState("networkidle"); @@ -213,13 +234,16 @@ test.describe("Resource Panel", () => { }); test("shows resource name in panel header", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "named-node.cluster", - exposed_fields: { node: "named-node", "pool/platform": ["prod/dgx"] }, - pool_platform_labels: { prod: ["dgx"] }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "named-node.cluster", + exposed_fields: { node: "named-node", "pool/platform": ["prod/dgx"] }, + pool_platform_labels: { prod: ["dgx"] }, + }, + ]), + ); await page.goto("/resources?view=named-node"); await page.waitForLoadState("networkidle"); @@ -246,14 +270,17 @@ test.describe("Resource Edge Cases", () => { }); test("shows resources with node conditions", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "problematic-node.cluster", - conditions: ["Ready", "SchedulingDisabled", "MemoryPressure"], - exposed_fields: { node: "problematic-node", "pool/platform": ["prod/base"] }, - pool_platform_labels: { prod: ["base"] }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "problematic-node.cluster", + conditions: ["Ready", "SchedulingDisabled", "MemoryPressure"], + exposed_fields: { node: "problematic-node", "pool/platform": ["prod/base"] }, + pool_platform_labels: { prod: ["base"] }, + }, + ]), + ); await page.goto("/resources"); await page.waitForLoadState("networkidle"); @@ -262,16 +289,19 @@ test.describe("Resource Edge Cases", () => { }); test("shows CPU-only nodes with zero GPU", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "cpu-only-node.cluster", - resource_type: BackendResourceType.SHARED, - exposed_fields: { node: "cpu-only-node", "pool/platform": ["prod/cpu"] }, - pool_platform_labels: { prod: ["cpu"] }, - allocatable_fields: { gpu: 0, cpu: 256, memory: 1024 * 1024, storage: 0 }, - usage_fields: { gpu: 0, cpu: 128, memory: 512 * 1024, storage: 0 }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "cpu-only-node.cluster", + resource_type: BackendResourceType.SHARED, + exposed_fields: { node: "cpu-only-node", "pool/platform": ["prod/cpu"] }, + pool_platform_labels: { prod: ["cpu"] }, + allocatable_fields: { gpu: 0, cpu: 256, memory: 1024 * 1024, storage: 0 }, + usage_fields: { gpu: 0, cpu: 128, memory: 512 * 1024, storage: 0 }, + }, + ]), + ); await page.goto("/resources"); await page.waitForLoadState("networkidle"); @@ -286,13 +316,16 @@ test.describe("Resource Toolbar", () => { }); test("has toolbar with search controls", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "toolbar-node.cluster", - exposed_fields: { node: "toolbar-node", "pool/platform": ["prod/base"] }, - pool_platform_labels: { prod: ["base"] }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "toolbar-node.cluster", + exposed_fields: { node: "toolbar-node", "pool/platform": ["prod/base"] }, + pool_platform_labels: { prod: ["base"] }, + }, + ]), + ); await page.goto("/resources"); await page.waitForLoadState("networkidle"); @@ -302,18 +335,21 @@ test.describe("Resource Toolbar", () => { }); test("shows results count", async ({ page }) => { - await setupResources(page, createResourcesResponse([ - { - hostname: "count-node-1.cluster", - exposed_fields: { node: "count-node-1", "pool/platform": ["prod/base"] }, - pool_platform_labels: { prod: ["base"] }, - }, - { - hostname: "count-node-2.cluster", - exposed_fields: { node: "count-node-2", "pool/platform": ["prod/base"] }, - pool_platform_labels: { prod: ["base"] }, - }, - ])); + await setupResources( + page, + createResourcesResponse([ + { + hostname: "count-node-1.cluster", + exposed_fields: { node: "count-node-1", "pool/platform": ["prod/base"] }, + pool_platform_labels: { prod: ["base"] }, + }, + { + hostname: "count-node-2.cluster", + exposed_fields: { node: "count-node-2", "pool/platform": ["prod/base"] }, + pool_platform_labels: { prod: ["base"] }, + }, + ]), + ); await page.goto("/resources"); await page.waitForLoadState("networkidle"); diff --git a/src/ui/e2e/journeys/status-display.spec.ts b/src/ui/e2e/journeys/status-display.spec.ts index 3b2b5c475a..b9c958870f 100644 --- a/src/ui/e2e/journeys/status-display.spec.ts +++ b/src/ui/e2e/journeys/status-display.spec.ts @@ -15,18 +15,8 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - createWorkflowsResponse, - createPoolResponse, - WorkflowStatus, - PoolStatus, -} from "@/mocks/factories"; -import { - setupDefaultMocks, - setupProfile, - setupWorkflows, - setupPools, -} from "@/e2e/utils/mock-setup"; +import { createWorkflowsResponse, createPoolResponse, WorkflowStatus, PoolStatus } from "@/mocks/factories"; +import { setupDefaultMocks, setupProfile, setupWorkflows, setupPools } from "@/e2e/utils/mock-setup"; /** * Workflow Status Display Tests @@ -52,9 +42,7 @@ test.describe("Workflow Status Display", () => { // ARRANGE await setupWorkflows( page, - createWorkflowsResponse([ - { name: "training-job-1", status: WorkflowStatus.RUNNING, user: "alice" }, - ]), + createWorkflowsResponse([{ name: "training-job-1", status: WorkflowStatus.RUNNING, user: "alice" }]), ); // ACT @@ -70,9 +58,7 @@ test.describe("Workflow Status Display", () => { // ARRANGE await setupWorkflows( page, - createWorkflowsResponse([ - { name: "training-done", status: WorkflowStatus.COMPLETED, user: "bob" }, - ]), + createWorkflowsResponse([{ name: "training-done", status: WorkflowStatus.COMPLETED, user: "bob" }]), ); // ACT @@ -88,9 +74,7 @@ test.describe("Workflow Status Display", () => { // ARRANGE await setupWorkflows( page, - createWorkflowsResponse([ - { name: "broken-job", status: WorkflowStatus.FAILED, user: "charlie" }, - ]), + createWorkflowsResponse([{ name: "broken-job", status: WorkflowStatus.FAILED, user: "charlie" }]), ); // ACT @@ -106,9 +90,7 @@ test.describe("Workflow Status Display", () => { // ARRANGE await setupWorkflows( page, - createWorkflowsResponse([ - { name: "queued-job", status: WorkflowStatus.PENDING, user: "dave" }, - ]), + createWorkflowsResponse([{ name: "queued-job", status: WorkflowStatus.PENDING, user: "dave" }]), ); // ACT @@ -124,9 +106,7 @@ test.describe("Workflow Status Display", () => { // ARRANGE await setupWorkflows( page, - createWorkflowsResponse([ - { name: "stopping-job", status: WorkflowStatus.FAILED_CANCELED, user: "eve" }, - ]), + createWorkflowsResponse([{ name: "stopping-job", status: WorkflowStatus.FAILED_CANCELED, user: "eve" }]), ); // ACT diff --git a/src/ui/e2e/journeys/submit-workflow-form.spec.ts b/src/ui/e2e/journeys/submit-workflow-form.spec.ts index 590bb0571b..466f11cce8 100644 --- a/src/ui/e2e/journeys/submit-workflow-form.spec.ts +++ b/src/ui/e2e/journeys/submit-workflow-form.spec.ts @@ -233,6 +233,82 @@ test.describe("Submit Workflow Form Validation", () => { // ASSERT — High is now checked await expect(overlay.getByRole("radio", { name: "High priority" })).toBeChecked(); }); + + test("submits YAML labels in the body without a separate label query override", async ({ page }) => { + // Extended: this journey renders the full detail/submit surface with + // several mocked round trips. + test.setTimeout(30_000); + let submittedLabels: string[] | null = null; + let submittedBody: string | null = null; + await page.route("**/api/pool/test-pool/workflow*", (route) => { + const url = new URL(route.request().url()); + if (!url.searchParams.has("validation_only") && !url.searchParams.has("dry_run")) { + submittedLabels = url.searchParams.getAll("label"); + submittedBody = route.request().postData(); + return route.fulfill({ + status: 200, + contentType: CT_JSON, + body: JSON.stringify({ + name: "yaml-labels", + logs: "/api/workflow/yaml-labels/logs", + warnings: [], + }), + }); + } + return route.fulfill({ status: 404, contentType: CT_JSON, body: '{"detail":"Not mocked"}' }); + }); + + const overlay = await openFormView(page); + await waitForPoolSelected(overlay, "test-pool"); + const editor = overlay.getByRole("textbox", { name: "YAML workflow specification editor" }); + await editor.click(); + await page.keyboard.insertText("workflow:\n labels:\n project: robotics\n tasks:\n - name: hello"); + + await expect(overlay.getByText("Workflow Labels", { exact: true })).toHaveCount(0); + await expect(overlay.getByRole("button", { name: "Add workflow label" })).toHaveCount(0); + await overlay.getByRole("button", { name: "Submit workflow", exact: true }).click(); + + await expect.poll(() => submittedLabels).toEqual([]); + await expect.poll(() => submittedBody).toContain("labels:"); + await expect.poll(() => submittedBody).toContain("project: robotics"); + await expect(page.getByText("Workflow submitted as yaml-labels")).toBeVisible(); + }); + + test("shows workflow warnings returned by validation", async ({ page }) => { + const warning = + "Workflow is missing label 'project'; add it now to avoid rejected submissions once it is required."; + let submittedLabels: string[] | null = null; + await page.route("**/api/pool/test-pool/workflow*", (route) => { + const url = new URL(route.request().url()); + if (url.searchParams.get("validation_only") === "true") { + submittedLabels = url.searchParams.getAll("label"); + return route.fulfill({ + status: 200, + contentType: CT_JSON, + body: JSON.stringify({ + name: "hello-osmo", + logs: "Workflow spec is valid.", + warnings: [warning], + }), + }); + } + return route.fulfill({ status: 404, contentType: CT_JSON, body: '{"detail":"Not mocked"}' }); + }); + + const overlay = await openFormView(page); + await waitForPoolSelected(overlay, "test-pool"); + const editor = overlay.getByRole("textbox", { name: "YAML workflow specification editor" }); + await editor.click(); + await page.keyboard.insertText("workflow:\n tasks:\n - name: hello"); + + await overlay.getByRole("button", { name: "More workflow options" }).click(); + const validateItem = page.getByRole("menuitem", { name: /validate/i }); + await expect(validateItem).toBeVisible(); + await validateItem.click(); + + await expect.poll(() => submittedLabels).toEqual([]); + await expect(overlay.getByText(warning)).toBeVisible(); + }); }); test.describe("Submit Workflow Localpath Warnings", () => { diff --git a/src/ui/e2e/journeys/table-column-toggle.spec.ts b/src/ui/e2e/journeys/table-column-toggle.spec.ts index 56a8097253..3828bc7c9b 100644 --- a/src/ui/e2e/journeys/table-column-toggle.spec.ts +++ b/src/ui/e2e/journeys/table-column-toggle.spec.ts @@ -23,13 +23,7 @@ import { WorkflowStatus, BackendResourceType, } from "@/mocks/factories"; -import { - setupDefaultMocks, - setupProfile, - setupPools, - setupWorkflows, - setupResources, -} from "@/e2e/utils/mock-setup"; +import { setupDefaultMocks, setupProfile, setupPools, setupWorkflows, setupResources } from "@/e2e/utils/mock-setup"; /** * Table Column Visibility Tests @@ -76,9 +70,7 @@ test.describe("Pools Table Column Toggle", () => { ); }); - test("toggle columns button opens column visibility menu", async ({ - page, - }) => { + test("toggle columns button opens column visibility menu", async ({ page }) => { // ACT await page.goto("/pools?all=true"); await page.waitForLoadState("networkidle"); @@ -87,14 +79,10 @@ test.describe("Pools Table Column Toggle", () => { await page.getByRole("button", { name: /toggle columns/i }).click(); // ASSERT — dropdown menu with column options visible - await expect( - page.getByRole("menuitemcheckbox").first(), - ).toBeVisible(); + await expect(page.getByRole("menuitemcheckbox").first()).toBeVisible(); }); - test("pool table shows Status and Backend columns by default", async ({ - page, - }) => { + test("pool table shows Status and Backend columns by default", async ({ page }) => { // ACT await page.goto("/pools?all=true"); await page.waitForLoadState("networkidle"); @@ -142,9 +130,7 @@ test.describe("Workflows Table Column Toggle", () => { ); }); - test("workflow table toggle columns shows column options", async ({ - page, - }) => { + test("workflow table toggle columns shows column options", async ({ page }) => { // ACT await page.goto("/workflows?all=true"); await page.waitForLoadState("networkidle"); @@ -153,14 +139,10 @@ test.describe("Workflows Table Column Toggle", () => { await page.getByRole("button", { name: /toggle columns/i }).click(); // ASSERT — at least one column checkbox is visible - await expect( - page.getByRole("menuitemcheckbox").first(), - ).toBeVisible(); + await expect(page.getByRole("menuitemcheckbox").first()).toBeVisible(); }); - test("workflows table shows workflow names and status", async ({ - page, - }) => { + test("workflows table shows workflow names and status", async ({ page }) => { // ACT await page.goto("/workflows?all=true"); await page.waitForLoadState("networkidle"); @@ -174,12 +156,7 @@ test.describe("Workflows Table Column Toggle", () => { test.describe("Resources Table Column Toggle", () => { test.beforeEach(async ({ page }) => { await setupDefaultMocks(page); - await setupPools( - page, - createPoolResponse([ - { name: "gpu-pool", status: PoolStatus.ONLINE }, - ]), - ); + await setupPools(page, createPoolResponse([{ name: "gpu-pool", status: PoolStatus.ONLINE }])); await setupResources( page, createResourcesResponse([ @@ -209,9 +186,7 @@ test.describe("Resources Table Column Toggle", () => { ); }); - test("resources table toggle columns shows column options", async ({ - page, - }) => { + test("resources table toggle columns shows column options", async ({ page }) => { // ACT await page.goto("/resources?all=true"); await page.waitForLoadState("networkidle"); @@ -220,9 +195,7 @@ test.describe("Resources Table Column Toggle", () => { await page.getByRole("button", { name: /toggle columns/i }).click(); // ASSERT — at least one column checkbox is visible - await expect( - page.getByRole("menuitemcheckbox").first(), - ).toBeVisible(); + await expect(page.getByRole("menuitemcheckbox").first()).toBeVisible(); }); test("resources table shows node names", async ({ page }) => { @@ -235,9 +208,7 @@ test.describe("Resources Table Column Toggle", () => { await expect(page.getByText("node-b").first()).toBeVisible(); }); - test("resources table distinguishes shared and reserved types", async ({ - page, - }) => { + test("resources table distinguishes shared and reserved types", async ({ page }) => { // ACT await page.goto("/resources?all=true"); await page.waitForLoadState("networkidle"); diff --git a/src/ui/e2e/journeys/toolbar-refresh.spec.ts b/src/ui/e2e/journeys/toolbar-refresh.spec.ts index 8b15abcc39..4682bd9763 100644 --- a/src/ui/e2e/journeys/toolbar-refresh.spec.ts +++ b/src/ui/e2e/journeys/toolbar-refresh.spec.ts @@ -135,10 +135,7 @@ test.describe("Occupancy Column Toggle & Search Presets", () => { test("toggle columns button is visible in occupancy toolbar", async ({ page }) => { // ARRANGE - await setupOccupancy( - page, - createOccupancySummaries([{ user: "alice", pool: "prod", gpu: 8 }]), - ); + await setupOccupancy(page, createOccupancySummaries([{ user: "alice", pool: "prod", gpu: 8 }])); // ACT await page.goto("/occupancy"); @@ -150,10 +147,7 @@ test.describe("Occupancy Column Toggle & Search Presets", () => { test("toggle columns button opens column visibility menu in occupancy", async ({ page }) => { // ARRANGE - await setupOccupancy( - page, - createOccupancySummaries([{ user: "alice", pool: "prod", gpu: 8 }]), - ); + await setupOccupancy(page, createOccupancySummaries([{ user: "alice", pool: "prod", gpu: 8 }])); // ACT await page.goto("/occupancy"); @@ -189,10 +183,7 @@ test.describe("Occupancy Column Toggle & Search Presets", () => { test("occupancy refresh button is visible", async ({ page }) => { // ARRANGE - await setupOccupancy( - page, - createOccupancySummaries([{ user: "alice", pool: "prod", gpu: 8 }]), - ); + await setupOccupancy(page, createOccupancySummaries([{ user: "alice", pool: "prod", gpu: 8 }])); // ACT await page.goto("/occupancy"); diff --git a/src/ui/e2e/journeys/workflow-cancel-mutation.spec.ts b/src/ui/e2e/journeys/workflow-cancel-mutation.spec.ts index 25b2ce7e9f..930e792026 100644 --- a/src/ui/e2e/journeys/workflow-cancel-mutation.spec.ts +++ b/src/ui/e2e/journeys/workflow-cancel-mutation.spec.ts @@ -131,7 +131,10 @@ test.describe("Workflow Cancel Mutation Flow", () => { await page.waitForLoadState("networkidle"); // Open cancel dialog - await page.getByRole("button", { name: /cancel workflow/i }).first().click(); + await page + .getByRole("button", { name: /cancel workflow/i }) + .first() + .click(); await expect(page.getByLabel(/reason/i)).toBeVisible(); // Type a reason @@ -147,7 +150,10 @@ test.describe("Workflow Cancel Mutation Flow", () => { await page.waitForLoadState("networkidle"); // Open cancel dialog - await page.getByRole("button", { name: /cancel workflow/i }).first().click(); + await page + .getByRole("button", { name: /cancel workflow/i }) + .first() + .click(); const forceCheckbox = page.getByRole("checkbox", { name: /force cancel/i }); await expect(forceCheckbox).toBeVisible(); @@ -175,7 +181,10 @@ test.describe("Workflow Cancel Mutation Flow", () => { await page.waitForLoadState("networkidle"); // Open cancel dialog and click confirm - await page.getByRole("button", { name: /cancel workflow/i }).first().click(); + await page + .getByRole("button", { name: /cancel workflow/i }) + .first() + .click(); await page.getByRole("button", { name: /confirm/i }).click(); // ASSERT — button shows "Cancelling..." loading state @@ -188,7 +197,10 @@ test.describe("Workflow Cancel Mutation Flow", () => { await page.waitForLoadState("networkidle"); // Open cancel dialog - await page.getByRole("button", { name: /cancel workflow/i }).first().click(); + await page + .getByRole("button", { name: /cancel workflow/i }) + .first() + .click(); // Hover over the info icon next to force cancel const infoButton = page.getByRole("button", { name: /what is force cancel/i }); diff --git a/src/ui/e2e/journeys/workflow-detail-actions.spec.ts b/src/ui/e2e/journeys/workflow-detail-actions.spec.ts index 0fab749671..e62b47f7fe 100644 --- a/src/ui/e2e/journeys/workflow-detail-actions.spec.ts +++ b/src/ui/e2e/journeys/workflow-detail-actions.spec.ts @@ -72,10 +72,7 @@ function createWorkflowDetailResponse( tags: [], submit_time: oneHourAgo.toISOString(), start_time: oneHourAgo.toISOString(), - end_time: - overrides.status === WorkflowStatus.COMPLETED - ? now.toISOString() - : null, + end_time: overrides.status === WorkflowStatus.COMPLETED ? now.toISOString() : null, exec_timeout: null, queue_timeout: null, duration: 3600, @@ -87,9 +84,7 @@ function createWorkflowDetailResponse( { name: "train", status: "RUNNING", - tasks: [ - { name: "train-task", retry_id: 0, status: "RUNNING" }, - ], + tasks: [{ name: "train-task", retry_id: 0, status: "RUNNING" }], }, ] ).map((g) => ({ @@ -139,9 +134,7 @@ function createWorkflowDetailResponse( async function setupWorkflowDetail( page: Parameters[0], name: string, - data: - | ReturnType - | { status: number; detail: string }, + data: ReturnType | { status: number; detail: string }, ) { const response = "detail" in data @@ -156,9 +149,7 @@ async function setupWorkflowDetail( body: JSON.stringify(data), }; - await page.route(`**/api/workflow/${name}*`, (route) => - route.fulfill(response), - ); + await page.route(`**/api/workflow/${name}*`, (route) => route.fulfill(response)); } test.describe("Workflow Detail Actions", () => { @@ -168,9 +159,7 @@ test.describe("Workflow Detail Actions", () => { await setupProfile(page); }); - test("shows Cancel Workflow button for RUNNING workflow", async ({ - page, - }) => { + test("shows Cancel Workflow button for RUNNING workflow", async ({ page }) => { // ARRANGE const wfName = "running-wf-action"; await setupWorkflowDetail( @@ -186,14 +175,10 @@ test.describe("Workflow Detail Actions", () => { await page.waitForLoadState("networkidle"); // ASSERT — Cancel Workflow button visible in actions - await expect( - page.getByRole("button", { name: /cancel workflow/i }).first(), - ).toBeVisible(); + await expect(page.getByRole("button", { name: /cancel workflow/i }).first()).toBeVisible(); }); - test("Cancel Workflow button is disabled for COMPLETED workflow", async ({ - page, - }) => { + test("Cancel Workflow button is disabled for COMPLETED workflow", async ({ page }) => { // ARRANGE const wfName = "completed-wf-action"; await setupWorkflowDetail( @@ -205,9 +190,7 @@ test.describe("Workflow Detail Actions", () => { { name: "train", status: "COMPLETED", - tasks: [ - { name: "train-task", retry_id: 0, status: "COMPLETED" }, - ], + tasks: [{ name: "train-task", retry_id: 0, status: "COMPLETED" }], }, ], }), @@ -218,16 +201,12 @@ test.describe("Workflow Detail Actions", () => { await page.waitForLoadState("networkidle"); // ASSERT — Cancel Workflow button is visible but disabled - const cancelButton = page - .getByRole("button", { name: /cancel workflow/i }) - .first(); + const cancelButton = page.getByRole("button", { name: /cancel workflow/i }).first(); await expect(cancelButton).toBeVisible(); await expect(cancelButton).toBeDisabled(); }); - test("clicking Cancel Workflow opens confirmation dialog", async ({ - page, - }) => { + test("clicking Cancel Workflow opens confirmation dialog", async ({ page }) => { // ARRANGE const wfName = "cancel-dialog-wf"; await setupWorkflowDetail( @@ -251,17 +230,11 @@ test.describe("Workflow Detail Actions", () => { // ASSERT — dialog opens with workflow name and action buttons await expect(page.getByText("Cancel Workflow").first()).toBeVisible(); await expect(page.getByText(wfName).first()).toBeVisible(); - await expect( - page.getByRole("button", { name: /keep running/i }), - ).toBeVisible(); - await expect( - page.getByRole("button", { name: /confirm/i }), - ).toBeVisible(); + await expect(page.getByRole("button", { name: /keep running/i })).toBeVisible(); + await expect(page.getByRole("button", { name: /confirm/i })).toBeVisible(); }); - test("cancel dialog has reason input and force checkbox", async ({ - page, - }) => { + test("cancel dialog has reason input and force checkbox", async ({ page }) => { // ARRANGE const wfName = "cancel-form-wf"; await setupWorkflowDetail( @@ -285,9 +258,7 @@ test.describe("Workflow Detail Actions", () => { await expect(page.getByRole("checkbox", { name: /force cancel/i })).toBeVisible(); }); - test("Keep Running button dismisses the cancel dialog", async ({ - page, - }) => { + test("Keep Running button dismisses the cancel dialog", async ({ page }) => { // ARRANGE const wfName = "keep-running-wf"; await setupWorkflowDetail( @@ -307,17 +278,13 @@ test.describe("Workflow Detail Actions", () => { .click(); // Wait for dialog to appear - await expect( - page.getByRole("button", { name: /keep running/i }), - ).toBeVisible(); + await expect(page.getByRole("button", { name: /keep running/i })).toBeVisible(); // Click Keep Running await page.getByRole("button", { name: /keep running/i }).click(); // ASSERT — dialog is dismissed - await expect( - page.getByRole("button", { name: /keep running/i }), - ).not.toBeVisible(); + await expect(page.getByRole("button", { name: /keep running/i })).not.toBeVisible(); }); }); @@ -328,16 +295,10 @@ test.describe("Workflow Detail Tabs", () => { await setupProfile(page); }); - test("shows all panel tabs: Overview, Tasks, Logs, Events, Spec", async ({ - page, - }) => { + test("shows all panel tabs: Overview, Tasks, Logs, Events, Spec", async ({ page }) => { // ARRANGE const wfName = "tabs-wf"; - await setupWorkflowDetail( - page, - wfName, - createWorkflowDetailResponse(wfName), - ); + await setupWorkflowDetail(page, wfName, createWorkflowDetailResponse(wfName)); // ACT await page.goto(`/workflows/${wfName}`); @@ -363,9 +324,7 @@ test.describe("Workflow Detail Tabs", () => { { name: "train", status: "COMPLETED", - tasks: [ - { name: "train-task", retry_id: 0, status: "COMPLETED" }, - ], + tasks: [{ name: "train-task", retry_id: 0, status: "COMPLETED" }], }, ], }), @@ -376,9 +335,7 @@ test.describe("Workflow Detail Tabs", () => { await page.waitForLoadState("networkidle"); // ASSERT — Resubmit button visible - await expect( - page.getByRole("button", { name: /resubmit workflow/i }).first(), - ).toBeVisible(); + await expect(page.getByRole("button", { name: /resubmit workflow/i }).first()).toBeVisible(); }); test("workflow detail shows pool and backend info", async ({ page }) => { @@ -397,8 +354,6 @@ test.describe("Workflow Detail Tabs", () => { await page.waitForLoadState("networkidle"); // ASSERT — pool name visible in details - await expect( - page.getByText("production-pool").first(), - ).toBeVisible(); + await expect(page.getByText("production-pool").first()).toBeVisible(); }); }); diff --git a/src/ui/e2e/journeys/workflow-detail-overview.spec.ts b/src/ui/e2e/journeys/workflow-detail-overview.spec.ts index 7cc429d36d..50751bc0d8 100644 --- a/src/ui/e2e/journeys/workflow-detail-overview.spec.ts +++ b/src/ui/e2e/journeys/workflow-detail-overview.spec.ts @@ -49,6 +49,8 @@ function createWorkflowDetailResponse( outputs?: string; priority?: string; tags?: string[]; + labels?: Record; + warnings?: string[]; groups?: Array<{ name: string; status?: string; @@ -75,9 +77,7 @@ function createWorkflowDetailResponse( WorkflowStatus.FAILED, WorkflowStatus.FAILED_CANCELED, ]; - const isTerminal = terminalWorkflowStatuses.includes( - (overrides.status as WorkflowStatus) ?? WorkflowStatus.RUNNING, - ); + const isTerminal = terminalWorkflowStatuses.includes((overrides.status as WorkflowStatus) ?? WorkflowStatus.RUNNING); return { name, @@ -94,6 +94,8 @@ function createWorkflowDetailResponse( dashboard_url: overrides.dashboard_url !== undefined ? overrides.dashboard_url : null, grafana_url: overrides.grafana_url !== undefined ? overrides.grafana_url : null, tags: overrides.tags ?? [], + labels: overrides.labels ?? {}, + warnings: overrides.warnings ?? [], submit_time: twoHoursAgo.toISOString(), start_time: twoHoursAgo.toISOString(), end_time: isTerminal ? oneHourAgo.toISOString() : null, @@ -289,9 +291,7 @@ test.describe("Workflow Detail Overview — Failed Workflow", () => { await page.waitForLoadState("networkidle"); // ASSERT — Cancel Workflow button is disabled - await expect( - page.getByRole("button", { name: /cancel workflow/i }).first(), - ).toBeDisabled(); + await expect(page.getByRole("button", { name: /cancel workflow/i }).first()).toBeDisabled(); }); }); @@ -331,11 +331,7 @@ test.describe("Workflow Detail Overview — Details Section", () => { test("shows UUID with copy button", async ({ page }) => { const wfName = "uuid-wf"; - await setupWorkflowDetail( - page, - wfName, - createWorkflowDetailResponse(wfName), - ); + await setupWorkflowDetail(page, wfName, createWorkflowDetailResponse(wfName)); // ACT await page.goto(`/workflows/${wfName}`); @@ -346,14 +342,58 @@ test.describe("Workflow Detail Overview — Details Section", () => { await expect(page.getByText(`uuid-${wfName}`).first()).toBeVisible(); }); - test("user name links to workflows filtered by user", async ({ page }) => { - const wfName = "user-link-wf"; + test("shows immutable workflow labels separately from tags", async ({ page }) => { + // Extended: this journey renders the full detail/submit surface with + // several mocked round trips. + test.setTimeout(30_000); + const wfName = "labels-wf"; await setupWorkflowDetail( page, wfName, - createWorkflowDetailResponse(wfName), + createWorkflowDetailResponse(wfName, { + labels: { team: "robotics", experiment: "run42" }, + tags: ["mutable-tag"], + }), ); + await page.goto(`/workflows/${wfName}`); + await page.waitForLoadState("networkidle"); + + await expect(page.getByText("Labels", { exact: true })).toBeVisible(); + const teamLabelLink = page.getByRole("link", { name: "team=robotics", exact: true }); + const experimentLabelLink = page.getByRole("link", { name: "experiment=run42", exact: true }); + await expect(teamLabelLink).toHaveAttribute("href", "/workflows?f=label:team%3Drobotics&all=true"); + await expect(experimentLabelLink).toHaveAttribute("href", "/workflows?f=label:experiment%3Drun42&all=true"); + await expect(page.getByText("Tags", { exact: true })).toBeVisible(); + }); + + test("shows current workflow warnings on completed workflows", async ({ page }) => { + // The backend recomputes warnings from the current policy for every + // status, including COMPLETED, so users see violations on finished runs. + const wfName = "warnings-wf"; + const warning = + "Workflow is missing label 'project'; add it now to avoid rejected submissions once it is required."; + await setupWorkflowDetail( + page, + wfName, + createWorkflowDetailResponse(wfName, { + status: WorkflowStatus.COMPLETED, + warnings: [warning], + }), + ); + + await page.goto(`/workflows/${wfName}`); + await page.waitForLoadState("networkidle"); + + const warningRegion = page.getByRole("region", { name: "Workflow warnings" }); + await expect(warningRegion).toBeVisible(); + await expect(warningRegion.getByText(warning)).toBeVisible(); + }); + + test("user name links to workflows filtered by user", async ({ page }) => { + const wfName = "user-link-wf"; + await setupWorkflowDetail(page, wfName, createWorkflowDetailResponse(wfName)); + // ACT await page.goto(`/workflows/${wfName}`); await page.waitForLoadState("networkidle"); @@ -366,11 +406,7 @@ test.describe("Workflow Detail Overview — Details Section", () => { test("pool name links to workflows filtered by pool", async ({ page }) => { const wfName = "pool-link-wf"; - await setupWorkflowDetail( - page, - wfName, - createWorkflowDetailResponse(wfName, { pool: "production-pool" }), - ); + await setupWorkflowDetail(page, wfName, createWorkflowDetailResponse(wfName, { pool: "production-pool" })); // ACT await page.goto(`/workflows/${wfName}`); diff --git a/src/ui/e2e/journeys/workflow-detail-tabs.spec.ts b/src/ui/e2e/journeys/workflow-detail-tabs.spec.ts index 19bfbb420c..87c59efc42 100644 --- a/src/ui/e2e/journeys/workflow-detail-tabs.spec.ts +++ b/src/ui/e2e/journeys/workflow-detail-tabs.spec.ts @@ -73,8 +73,7 @@ function createWorkflowDetailResponse( tags: overrides.tags ?? ["training", "v2"], submit_time: oneHourAgo.toISOString(), start_time: oneHourAgo.toISOString(), - end_time: - overrides.status === WorkflowStatus.COMPLETED ? now.toISOString() : null, + end_time: overrides.status === WorkflowStatus.COMPLETED ? now.toISOString() : null, exec_timeout: null, queue_timeout: null, duration: 3600, @@ -110,9 +109,7 @@ function createWorkflowDetailResponse( remaining_upstream_groups: [], downstream_groups: g.downstream_groups ?? [], failure_message: null, - tasks: ( - g.tasks ?? [{ name: `${g.name}-task`, retry_id: 0, status: g.status ?? "RUNNING" }] - ).map((t) => ({ + tasks: (g.tasks ?? [{ name: `${g.name}-task`, retry_id: 0, status: g.status ?? "RUNNING" }]).map((t) => ({ name: t.name, retry_id: t.retry_id ?? 0, status: t.status ?? "RUNNING", @@ -157,11 +154,7 @@ test.describe("Workflow Detail Tab Content", () => { await page.emulateMedia({ reducedMotion: "reduce" }); await setupDefaultMocks(page); await setupProfile(page); - await setupWorkflowDetail( - page, - wfName, - createWorkflowDetailResponse(wfName), - ); + await setupWorkflowDetail(page, wfName, createWorkflowDetailResponse(wfName)); }); test("Overview tab shows timeline and details sections", async ({ page }) => { @@ -235,12 +228,8 @@ test.describe("Workflow Detail Tab Content", () => { const toolbar = page.getByRole("toolbar", { name: "Spec viewer controls" }); await expect(toolbar).toBeVisible({ timeout: 10_000 }); - await expect( - page.getByRole("radio", { name: /yaml/i }), - ).toBeVisible(); - await expect( - page.getByRole("radio", { name: /template/i }), - ).toBeVisible(); + await expect(page.getByRole("radio", { name: /yaml/i })).toBeVisible(); + await expect(page.getByRole("radio", { name: /template/i })).toBeVisible(); }); test("Events tab renders event viewer component", async ({ page }) => { diff --git a/src/ui/e2e/journeys/workflow-detail.spec.ts b/src/ui/e2e/journeys/workflow-detail.spec.ts index 9e8408a3fa..69fc0d291b 100644 --- a/src/ui/e2e/journeys/workflow-detail.spec.ts +++ b/src/ui/e2e/journeys/workflow-detail.spec.ts @@ -16,10 +16,7 @@ import { test, expect } from "@playwright/test"; import { WorkflowStatus } from "@/mocks/factories"; -import { - setupDefaultMocks, - setupProfile, -} from "@/e2e/utils/mock-setup"; +import { setupDefaultMocks, setupProfile } from "@/e2e/utils/mock-setup"; /** * Workflow Detail Page Journey Tests @@ -77,22 +74,22 @@ function createWorkflowDetailResponse( tags: [], submit_time: oneHourAgo.toISOString(), start_time: oneHourAgo.toISOString(), - end_time: overrides.status === WorkflowStatus.COMPLETED - ? now.toISOString() - : null, + end_time: overrides.status === WorkflowStatus.COMPLETED ? now.toISOString() : null, exec_timeout: null, queue_timeout: null, duration: 3600, queued_time: 5, status: overrides.status ?? WorkflowStatus.RUNNING, outputs: "", - groups: (overrides.groups ?? [ - { - name: "train", - status: "RUNNING", - tasks: [{ name: "train-task", retry_id: 0, status: "RUNNING" }], - }, - ]).map((g) => ({ + groups: ( + overrides.groups ?? [ + { + name: "train", + status: "RUNNING", + tasks: [{ name: "train-task", retry_id: 0, status: "RUNNING" }], + }, + ] + ).map((g) => ({ name: g.name, status: g.status ?? "RUNNING", start_time: oneHourAgo.toISOString(), @@ -203,9 +200,7 @@ test.describe("Workflow Detail Page", () => { // ASSERT — page must not crash, should show error state // The SSR prefetch may fail silently, then the client-side fetch triggers error await expect(page.locator("body")).not.toBeEmpty(); - await expect( - page.getByText(/error|unable to load|not found/i).first() - ).toBeVisible({ timeout: 15_000 }); + await expect(page.getByText(/error|unable to load|not found/i).first()).toBeVisible({ timeout: 15_000 }); }); test("Workflows breadcrumb link navigates back to workflows list", async ({ page }) => { @@ -410,8 +405,6 @@ test.describe("Workflow Detail 404", () => { // ASSERT — should show not-found or error message await expect(page.locator("body")).not.toBeEmpty(); - await expect( - page.getByText(/not found|error|does not exist/i).first() - ).toBeVisible({ timeout: 15_000 }); + await expect(page.getByText(/not found|error|does not exist/i).first()).toBeVisible({ timeout: 15_000 }); }); }); diff --git a/src/ui/e2e/journeys/workflow-filters.spec.ts b/src/ui/e2e/journeys/workflow-filters.spec.ts index fa8a85bc5d..1d2868fc86 100644 --- a/src/ui/e2e/journeys/workflow-filters.spec.ts +++ b/src/ui/e2e/journeys/workflow-filters.spec.ts @@ -246,4 +246,33 @@ test.describe("Workflow URL Filter State", () => { await expect(page.locator("body")).not.toBeEmpty(); await expect(page).toHaveURL(/f=pool(%3A|:)production/); }); + + test("forwards wildcard and inline-alternative workflow label selectors unchanged", async ({ page }) => { + const response = createWorkflowsResponse([ + { name: "label-wf", status: WorkflowStatus.RUNNING, user: "test-user", labels: { project: "robotics_team" } }, + ]); + const observedLabelSelectors: string[][] = []; + await page.route("**/api/workflow?*", (route) => { + observedLabelSelectors.push(new URL(route.request().url()).searchParams.getAll("label")); + return route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify(response), + }); + }); + + await page.goto("/workflows?all=true"); + await page.waitForLoadState("networkidle"); + + const selectors = ["project=(team_*|osmo_*)", "project=team_(a|b)"]; + const filterInput = page.getByRole("combobox", { name: /search and filter/i }); + for (const selector of selectors) { + await filterInput.fill(`label:${selector}`); + await filterInput.press("Enter"); + } + + await expect + .poll(() => observedLabelSelectors.some((observed) => selectors.every((selector) => observed.includes(selector)))) + .toBe(true); + }); }); diff --git a/src/ui/e2e/journeys/workflow-pagination.spec.ts b/src/ui/e2e/journeys/workflow-pagination.spec.ts index f84e4238b1..f2460d802f 100644 --- a/src/ui/e2e/journeys/workflow-pagination.spec.ts +++ b/src/ui/e2e/journeys/workflow-pagination.spec.ts @@ -15,15 +15,8 @@ // SPDX-License-Identifier: Apache-2.0 import { test, expect } from "@playwright/test"; -import { - createWorkflowsResponse, - WorkflowStatus, -} from "@/mocks/factories"; -import { - setupDefaultMocks, - setupProfile, - setupWorkflows, -} from "@/e2e/utils/mock-setup"; +import { createWorkflowsResponse, WorkflowStatus } from "@/mocks/factories"; +import { setupDefaultMocks, setupProfile, setupWorkflows } from "@/e2e/utils/mock-setup"; /** * Workflow Pagination / Infinite Scroll Tests @@ -71,9 +64,7 @@ test.describe("Workflow Pagination", () => { await setupWorkflows( page, createWorkflowsResponse( - [ - { name: "wf-single", status: WorkflowStatus.COMPLETED, user: "alice" }, - ], + [{ name: "wf-single", status: WorkflowStatus.COMPLETED, user: "alice" }], false, // moreEntries = false ), ); diff --git a/src/ui/e2e/journeys/workflow-resubmit-panel.spec.ts b/src/ui/e2e/journeys/workflow-resubmit-panel.spec.ts index e95fac88ee..22bda614f6 100644 --- a/src/ui/e2e/journeys/workflow-resubmit-panel.spec.ts +++ b/src/ui/e2e/journeys/workflow-resubmit-panel.spec.ts @@ -110,6 +110,7 @@ function createCompletedWorkflow(name: string) { app_name: null, app_version: null, plugins: { rsync: false }, + labels: { project: "robotics" }, }; } @@ -133,10 +134,7 @@ test.describe("Workflow Resubmit Panel", () => { ); // Setup pools for pool picker - await setupPools( - page, - createPoolResponse([{ name: "test-pool", status: PoolStatus.ONLINE }]), - ); + await setupPools(page, createPoolResponse([{ name: "test-pool", status: PoolStatus.ONLINE }])); }); test("resubmit button opens panel with workflow name in header", async ({ page }) => { @@ -145,7 +143,10 @@ test.describe("Workflow Resubmit Panel", () => { await page.waitForLoadState("networkidle"); // Click the Resubmit Workflow button - await page.getByRole("button", { name: /resubmit workflow/i }).first().click(); + await page + .getByRole("button", { name: /resubmit workflow/i }) + .first() + .click(); // ASSERT — panel opens with correct aria-label and header content const panel = page.locator(`[aria-label="Resubmit workflow: ${wfName}"]`); @@ -159,7 +160,10 @@ test.describe("Workflow Resubmit Panel", () => { await page.goto(`/workflows/${wfName}`); await page.waitForLoadState("networkidle"); - await page.getByRole("button", { name: /resubmit workflow/i }).first().click(); + await page + .getByRole("button", { name: /resubmit workflow/i }) + .first() + .click(); const panel = page.locator(`[aria-label="Resubmit workflow: ${wfName}"]`); await expect(panel).toBeVisible(); @@ -173,7 +177,10 @@ test.describe("Workflow Resubmit Panel", () => { await page.goto(`/workflows/${wfName}`); await page.waitForLoadState("networkidle"); - await page.getByRole("button", { name: /resubmit workflow/i }).first().click(); + await page + .getByRole("button", { name: /resubmit workflow/i }) + .first() + .click(); const panel = page.locator(`[aria-label="Resubmit workflow: ${wfName}"]`); await expect(panel).toBeVisible(); @@ -187,7 +194,10 @@ test.describe("Workflow Resubmit Panel", () => { await page.goto(`/workflows/${wfName}`); await page.waitForLoadState("networkidle"); - await page.getByRole("button", { name: /resubmit workflow/i }).first().click(); + await page + .getByRole("button", { name: /resubmit workflow/i }) + .first() + .click(); const panel = page.locator(`[aria-label="Resubmit workflow: ${wfName}"]`); await expect(panel).toBeVisible(); @@ -204,13 +214,16 @@ test.describe("Workflow Resubmit Panel", () => { await page.goto(`/workflows/${wfName}`); await page.waitForLoadState("networkidle"); - await page.getByRole("button", { name: /resubmit workflow/i }).first().click(); + await page + .getByRole("button", { name: /resubmit workflow/i }) + .first() + .click(); const panel = page.locator(`[aria-label="Resubmit workflow: ${wfName}"]`); await expect(panel).toBeVisible(); // ASSERT — Workflow Specification section visible - await expect(panel.getByText("Workflow Specification")).toBeVisible(); + await expect(panel.getByText("Workflow Specification", { exact: true })).toBeVisible(); }); test("submit button has correct aria-label with workflow name", async ({ page }) => { @@ -218,15 +231,43 @@ test.describe("Workflow Resubmit Panel", () => { await page.goto(`/workflows/${wfName}`); await page.waitForLoadState("networkidle"); - await page.getByRole("button", { name: /resubmit workflow/i }).first().click(); + await page + .getByRole("button", { name: /resubmit workflow/i }) + .first() + .click(); const panel = page.locator(`[aria-label="Resubmit workflow: ${wfName}"]`); await expect(panel).toBeVisible(); // ASSERT — Submit button exists with proper aria-label - await expect( - panel.getByRole("button", { name: `Submit workflow ${wfName}` }), - ).toBeVisible(); + await expect(panel.getByRole("button", { name: `Submit workflow ${wfName}` })).toBeVisible(); + }); + + test("resubmit preserves existing label keys while allowing value overrides", async ({ page }) => { + // Extended: this journey renders the full detail/submit surface with + // several mocked round trips. + test.setTimeout(30_000); + await page.goto(`/workflows/${wfName}`); + await page.waitForLoadState("networkidle"); + await page + .getByRole("button", { name: /resubmit workflow/i }) + .first() + .click(); + + const panel = page.locator(`[aria-label="Resubmit workflow: ${wfName}"]`); + await expect(panel.getByRole("textbox", { name: "Workflow label key 1" })).toHaveValue("project"); + await expect(panel.getByRole("textbox", { name: "Workflow label key 1" })).toBeDisabled(); + await expect(panel.getByRole("button", { name: "Remove workflow label 1" })).toBeDisabled(); + await panel.getByRole("textbox", { name: "Workflow label value 1" }).fill("simulation"); + + await panel.getByRole("button", { name: "Add workflow label" }).click(); + await panel.getByRole("textbox", { name: "Workflow label key 2" }).fill("team"); + await panel.getByRole("textbox", { name: "Workflow label value 2" }).fill("robotics"); + + await expect(panel.getByRole("textbox", { name: "Workflow label value 1" })).toHaveValue("simulation"); + await expect(panel.getByRole("textbox", { name: "Workflow label key 2" })).toHaveValue("team"); + await expect(panel.getByRole("textbox", { name: "Workflow label value 2" })).toHaveValue("robotics"); + await expect(panel.getByRole("button", { name: `Submit workflow ${wfName}` })).toBeEnabled(); }); test("submit shows 'Submitting...' while pending", async ({ page }) => { @@ -239,7 +280,10 @@ test.describe("Workflow Resubmit Panel", () => { await page.goto(`/workflows/${wfName}`); await page.waitForLoadState("networkidle"); - await page.getByRole("button", { name: /resubmit workflow/i }).first().click(); + await page + .getByRole("button", { name: /resubmit workflow/i }) + .first() + .click(); const panel = page.locator(`[aria-label="Resubmit workflow: ${wfName}"]`); await expect(panel).toBeVisible(); @@ -259,7 +303,10 @@ test.describe("Workflow Resubmit Panel", () => { await page.goto(`/workflows/${wfName}`); await page.waitForLoadState("networkidle"); - await page.getByRole("button", { name: /resubmit workflow/i }).first().click(); + await page + .getByRole("button", { name: /resubmit workflow/i }) + .first() + .click(); const panel = page.locator(`[aria-label="Resubmit workflow: ${wfName}"]`); await expect(panel).toBeVisible(); diff --git a/src/ui/e2e/journeys/workflow-spec-viewer.spec.ts b/src/ui/e2e/journeys/workflow-spec-viewer.spec.ts index 52b5f55619..672cc8c112 100644 --- a/src/ui/e2e/journeys/workflow-spec-viewer.spec.ts +++ b/src/ui/e2e/journeys/workflow-spec-viewer.spec.ts @@ -104,10 +104,7 @@ function createWorkflowDetailForSpec(name: string) { }; } -async function setupWorkflowAndSpec( - page: Parameters[0], - name: string, -) { +async function setupWorkflowAndSpec(page: Parameters[0], name: string) { const data = createWorkflowDetailForSpec(name); // Mock the spec endpoint — registered BEFORE the catch-all so LIFO gives these priority diff --git a/src/ui/e2e/utils/mock-setup.ts b/src/ui/e2e/utils/mock-setup.ts index b6b9f02a99..e06adc7a64 100644 --- a/src/ui/e2e/utils/mock-setup.ts +++ b/src/ui/e2e/utils/mock-setup.ts @@ -96,10 +96,7 @@ export async function setupPools(page: Page, data: PoolResponse | ApiError): Pro await page.route("**/api/pool_quota*", (route) => route.fulfill(response)); } -export async function setupResources( - page: Page, - data: ResourcesResponse | ApiError, -): Promise { +export async function setupResources(page: Page, data: ResourcesResponse | ApiError): Promise { if ("detail" in data) { const response = { status: data.status, @@ -118,8 +115,7 @@ export async function setupResources( const filtered = { resources: data.resources?.filter((r) => { - const pp = - ((r.exposed_fields as Record)?.["pool/platform"] as string[]) ?? []; + const pp = ((r.exposed_fields as Record)?.["pool/platform"] as string[]) ?? []; return pp.some((p) => pools.some((pool) => p.startsWith(`${pool}/`))); }) ?? [], }; @@ -163,10 +159,7 @@ export async function setupWorkflows( // ── Occupancy (task summary) ───────────────────────────────────────────────── -export async function setupOccupancy( - page: Page, - data: { summaries: unknown[] } | ApiError, -): Promise { +export async function setupOccupancy(page: Page, data: { summaries: unknown[] } | ApiError): Promise { const response = "detail" in data ? { status: data.status, contentType: CT_JSON, body: JSON.stringify({ detail: data.detail }) } diff --git a/src/ui/openapi.json b/src/ui/openapi.json index 02127094ed..ee300c29f9 100644 --- a/src/ui/openapi.json +++ b/src/ui/openapi.json @@ -2641,7 +2641,7 @@ { "type": "array", "items": { - "$ref": "#/components/schemas/src__lib__utils__config_history__ConfigHistoryType" + "$ref": "#/components/schemas/ConfigHistoryType" } }, { @@ -3007,7 +3007,7 @@ "in": "query", "required": true, "schema": { - "$ref": "#/components/schemas/src__lib__utils__config_history__ConfigHistoryType" + "$ref": "#/components/schemas/ConfigHistoryType" } }, { @@ -5137,6 +5137,50 @@ }, "explode": true }, + { + "name": "label", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Workflow label selector: key=value with optional * wildcards and (a|b) alternatives, for example key=(team_*|osmo_*) or key=team_(a|b). Repeat for AND semantics.", + "title": "Label" + }, + "description": "Workflow label selector: key=value with optional * wildcards and (a|b) alternatives, for example key=(team_*|osmo_*) or key=team_(a|b). Repeat for AND semantics.", + "explode": true + }, + { + "name": "no_label", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Label key that must be absent from the workflow; workflows without any labels match. Repeat for AND semantics.", + "title": "No Label" + }, + "description": "Label key that must be absent from the workflow; workflows without any labels match. Repeat for AND semantics.", + "explode": true + }, { "name": "x-osmo-user", "in": "header", @@ -6898,6 +6942,20 @@ }, "explode": true }, + { + "name": "label", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "title": "Label" + }, + "explode": true + }, { "name": "x-osmo-user", "in": "header", @@ -7976,7 +8034,7 @@ "ConfigHistory": { "properties": { "config_type": { - "$ref": "#/components/schemas/src__lib__utils__config_history__ConfigHistoryType" + "$ref": "#/components/schemas/ConfigHistoryType" }, "name": { "type": "string", @@ -8029,6 +8087,23 @@ "title": "ConfigHistory", "description": "Object storing config history." }, + "ConfigHistoryType": { + "type": "string", + "enum": [ + "DATASET", + "SERVICE", + "WORKFLOW", + "BACKEND", + "POOL", + "POD_TEMPLATE", + "GROUP_TEMPLATE", + "RESOURCE_VALIDATION", + "BACKEND_TEST", + "ROLE" + ], + "title": "ConfigHistoryType", + "description": "Type of configs supported by config history " + }, "ConfigsRequest": { "properties": { "description": { @@ -8643,6 +8718,80 @@ "title": "JwtTokenResponse", "description": "Response for JWT token creation endpoints." }, + "LabelEnforcement": { + "type": "string", + "enum": [ + "off", + "warn", + "enforce" + ], + "title": "LabelEnforcement", + "description": "Per-key policy strictness: 'off' skips checking, 'warn' surfaces\nmissing or unlisted values as submission warnings, and 'enforce'\nrejects them." + }, + "LabelPolicy": { + "properties": { + "key": { + "type": "string", + "title": "Key" + }, + "allow_list": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Allow List", + "default": [] + }, + "enforcement": { + "$ref": "#/components/schemas/LabelEnforcement", + "default": "off" + }, + "assert_message": { + "type": "string", + "title": "Assert Message", + "default": "" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "key" + ], + "title": "LabelPolicy", + "description": "Configuration for one admin-designated workflow label key.\n\nAn empty allow_list accepts any well-formed value; enforcement then\napplies only to the key being present." + }, + "LabelsConfig-Input": { + "properties": { + "policy": { + "items": { + "$ref": "#/components/schemas/LabelPolicy" + }, + "type": "array", + "title": "Policy", + "default": [] + } + }, + "additionalProperties": false, + "type": "object", + "title": "LabelsConfig", + "description": "Curated workflow label policy; empty by default, so no policy\napplies until configured." + }, + "LabelsConfig-Output": { + "properties": { + "policy": { + "items": { + "$ref": "#/components/schemas/LabelPolicy" + }, + "type": "array", + "title": "Policy", + "default": [] + } + }, + "additionalProperties": false, + "type": "object", + "title": "LabelsConfig", + "description": "Curated workflow label policy; empty by default, so no policy\napplies until configured." + }, "ListBackendsResponse": { "properties": { "backends": { @@ -9123,6 +9272,22 @@ "type": "object", "title": "NotificationConfig" }, + "OperableConfigHistoryType": { + "type": "string", + "enum": [ + "SERVICE", + "WORKFLOW", + "BACKEND", + "POOL", + "POD_TEMPLATE", + "GROUP_TEMPLATE", + "RESOURCE_VALIDATION", + "BACKEND_TEST", + "ROLE" + ], + "title": "OperableConfigHistoryType", + "description": "Type of configs supported by config history mutations." + }, "OperatorType": { "type": "string", "enum": [ @@ -11679,7 +11844,7 @@ "title": "Tags" }, "config_type": { - "$ref": "#/components/schemas/src__lib__utils__config_history__ConfigHistoryType" + "$ref": "#/components/schemas/OperableConfigHistoryType" }, "revision": { "type": "integer", @@ -12010,6 +12175,13 @@ } ], "title": "Dashboard Url" + }, + "warnings": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Warnings" } }, "additionalProperties": false, @@ -12954,6 +13126,13 @@ "type": { "type": "string", "title": "Error Type" + }, + "input": { + "title": "Input" + }, + "ctx": { + "type": "object", + "title": "Context" } }, "type": "object", @@ -13088,6 +13267,12 @@ } } }, + "labels_config": { + "$ref": "#/components/schemas/LabelsConfig-Input", + "default": { + "policy": [] + } + }, "max_num_tasks": { "type": "integer", "title": "Max Num Tasks", @@ -13249,6 +13434,12 @@ } } }, + "labels_config": { + "$ref": "#/components/schemas/LabelsConfig-Output", + "default": { + "policy": [] + } + }, "max_num_tasks": { "type": "integer", "title": "Max Num Tasks", @@ -13612,6 +13803,20 @@ "priority": { "type": "string", "title": "Priority" + }, + "labels": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Labels" + }, + "warnings": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Warnings" } }, "additionalProperties": false, @@ -13658,22 +13863,6 @@ "title": "WorkflowStatus", "description": "Represents the status of a workflow. " }, - "src__lib__utils__config_history__ConfigHistoryType": { - "type": "string", - "enum": [ - "SERVICE", - "WORKFLOW", - "BACKEND", - "POOL", - "POD_TEMPLATE", - "GROUP_TEMPLATE", - "RESOURCE_VALIDATION", - "BACKEND_TEST", - "ROLE" - ], - "title": "ConfigHistoryType", - "description": "Type of configs supported by config history " - }, "src__service__core__app__objects__ListEntry": { "properties": { "uuid": { @@ -13884,6 +14073,13 @@ "priority": { "type": "string", "title": "Priority" + }, + "labels": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Labels" } }, "additionalProperties": false, diff --git a/src/ui/src/components/submit-workflow/submit-workflow-config-panel.tsx b/src/ui/src/components/submit-workflow/submit-workflow-config-panel.tsx index b477c79566..3d7d453867 100644 --- a/src/ui/src/components/submit-workflow/submit-workflow-config-panel.tsx +++ b/src/ui/src/components/submit-workflow/submit-workflow-config-panel.tsx @@ -85,6 +85,7 @@ export interface SubmitWorkflowConfigPanelProps { isValidatePending: boolean; validationOk: boolean | null; validationError: string | null; + validationWarnings: string[]; canValidate: boolean; onValidate: () => void; } @@ -107,6 +108,7 @@ export const SubmitWorkflowConfigPanel = memo(function SubmitWorkflowConfigPanel isValidatePending, validationOk, validationError, + validationWarnings, canValidate, onValidate, }: SubmitWorkflowConfigPanelProps) { @@ -194,6 +196,16 @@ export const SubmitWorkflowConfigPanel = memo(function SubmitWorkflowConfigPanel Workflow spec is valid )} + {validationWarnings.length > 0 && ( +
+ {validationWarnings.map((warning, index) => ( +

{warning}

+ ))} +
+ )} {validationError && (
diff --git a/src/ui/src/components/submit-workflow/use-submit-workflow-form.ts b/src/ui/src/components/submit-workflow/use-submit-workflow-form.ts index 28cb17dcf6..bd432490ee 100644 --- a/src/ui/src/components/submit-workflow/use-submit-workflow-form.ts +++ b/src/ui/src/components/submit-workflow/use-submit-workflow-form.ts @@ -33,6 +33,8 @@ import { useProfile } from "@/lib/api/adapter/hooks"; import { usePoolSelection } from "@/components/workflow/use-pool-selection"; import { detectLocalpathUsage, type LocalpathWarnings } from "@/components/submit-workflow/detect-localpath"; +const NO_WARNINGS: string[] = []; + /** Extract a human-readable error message from various error shapes. */ function extractErrorMessage(err: unknown): string { if (!err) return "Unknown error"; @@ -60,6 +62,7 @@ interface ValidationState { spec: string; ok: boolean; error: string | null; + warnings: string[]; } export interface UseSubmitWorkflowFormReturn { @@ -86,6 +89,7 @@ export interface UseSubmitWorkflowFormReturn { isValidatePending: boolean; validationOk: boolean | null; validationError: string | null; + validationWarnings: string[]; canValidate: boolean; handleValidate: () => void; // Lifecycle @@ -115,6 +119,7 @@ export function useSubmitWorkflowForm(initialSpec = ""): UseSubmitWorkflowFormRe const isValidationFresh = validationState !== null && validationState.spec === spec; const validationOk = isValidationFresh ? (validationState.ok ? true : null) : null; const validationError = isValidationFresh ? validationState.error : null; + const validationWarnings = isValidationFresh ? validationState.warnings : NO_WARNINGS; // ── Mutation hooks ──────────────────────────────────────────────────────── @@ -122,6 +127,9 @@ export function useSubmitWorkflowForm(initialSpec = ""): UseSubmitWorkflowFormRe mutation: { onSuccess: (response) => { const newName = response.name; + for (const warning of response.warnings ?? []) { + toast.warning(warning); + } toast.success(`Workflow submitted as ${newName}`, { action: { label: "View Workflow", @@ -203,13 +211,18 @@ export function useSubmitWorkflowForm(initialSpec = ""): UseSubmitWorkflowFormRe params: { priority, validation_only: true }, }, { - onSuccess: () => { - setValidationState({ spec: specAtCall, ok: true, error: null }); + onSuccess: (response) => { + setValidationState({ + spec: specAtCall, + ok: true, + error: null, + warnings: response.warnings ?? [], + }); announcer.announce("Workflow spec is valid", "polite"); }, onError: (err) => { const msg = extractErrorMessage(err); - setValidationState({ spec: specAtCall, ok: false, error: msg }); + setValidationState({ spec: specAtCall, ok: false, error: msg, warnings: [] }); announcer.announce(`Validation failed: ${msg}`, "assertive"); }, }, @@ -244,6 +257,7 @@ export function useSubmitWorkflowForm(initialSpec = ""): UseSubmitWorkflowFormRe isValidatePending, validationOk, validationError, + validationWarnings, canValidate, handleValidate, handleClose, @@ -267,6 +281,7 @@ export function useSubmitWorkflowForm(initialSpec = ""): UseSubmitWorkflowFormRe isValidatePending, validationOk, validationError, + validationWarnings, canValidate, handleValidate, handleClose, diff --git a/src/ui/src/components/workflow/workflow-label-editor.tsx b/src/ui/src/components/workflow/workflow-label-editor.tsx new file mode 100644 index 0000000000..28d4119f0f --- /dev/null +++ b/src/ui/src/components/workflow/workflow-label-editor.tsx @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +"use client"; + +import { Plus, X } from "lucide-react"; +import { Button } from "@/components/shadcn/button"; +import { Input } from "@/components/shadcn/input"; +import { MAX_WORKFLOW_LABELS, type WorkflowLabelDraft } from "@/lib/workflow-labels"; + +export interface WorkflowLabelEditorProps { + labels: WorkflowLabelDraft[]; + onChange: (labels: WorkflowLabelDraft[]) => void; + disabled?: boolean; + error?: string | null; + /** Count of leading drafts seeded from the workflow's own labels; their keys are locked. */ + lockedLabelCount?: number; +} + +export function WorkflowLabelEditor({ + labels, + onChange, + disabled = false, + error, + lockedLabelCount = 0, +}: WorkflowLabelEditorProps) { + const updateLabel = (index: number, field: keyof WorkflowLabelDraft, value: string) => { + onChange(labels.map((label, labelIndex) => (labelIndex === index ? { ...label, [field]: value } : label))); + }; + + const removeLabel = (index: number) => { + onChange(labels.filter((_, labelIndex) => labelIndex !== index)); + }; + + return ( +
+

+ Per-run overrides take precedence over labels in the workflow specification. + {lockedLabelCount > 0 && + " Existing keys cannot be removed here; edit the workflow specification to remove one."} +

+ {labels.map((label, index) => { + const keyIsLocked = index < lockedLabelCount; + return ( +
+ updateLabel(index, "key", event.target.value)} + /> + updateLabel(index, "value", event.target.value)} + /> + +
+ ); + })} + + {error && ( +

+ {error} +

+ )} +
+ ); +} diff --git a/src/ui/src/features/workflows/detail/components/panel/ui/workflow/workflow-details.tsx b/src/ui/src/features/workflows/detail/components/panel/ui/workflow/workflow-details.tsx index e3c0f1d849..b7d04a6599 100644 --- a/src/ui/src/features/workflows/detail/components/panel/ui/workflow/workflow-details.tsx +++ b/src/ui/src/features/workflows/detail/components/panel/ui/workflow/workflow-details.tsx @@ -34,6 +34,7 @@ import { Loader2, RotateCw, FileCode, + TriangleAlert, } from "lucide-react"; import { cn } from "@/lib/utils"; import { Card, CardContent } from "@/components/shadcn/card"; @@ -45,6 +46,7 @@ import { PanelTabs, type PanelTab } from "@/components/panel/panel-tabs"; import { SeparatedParts } from "@/components/panel/separated-parts"; import { TabPanel } from "@/components/panel/tab-panel"; import type { WorkflowQueryResponse } from "@/lib/api/adapter/types"; +import { sortedWorkflowLabelEntries } from "@/lib/workflow-labels"; import type { GroupWithLayout, TaskQueryResponse } from "@/features/workflows/detail/lib/workflow-types"; import { formatDuration } from "@/features/workflows/detail/lib/workflow-types"; import { getStatusIcon } from "@/features/workflows/detail/lib/status"; @@ -186,8 +188,36 @@ const StatusDisplay = memo(function StatusDisplay({ ); }); +/** Warnings the API returns for this workflow (currently warn-mode label-policy violations, recomputed from the stored labels and active policy). */ +const WorkflowWarnings = memo(function WorkflowWarnings({ warnings }: { warnings: string[] | undefined }) { + if (!warnings || warnings.length === 0) return null; + + return ( +
+

Warnings

+
+
+
+ ); +}); + /** Details section */ const Details = memo(function Details({ workflow }: { workflow: WorkflowQueryResponse }) { + const labelEntries = sortedWorkflowLabelEntries(workflow.labels); return (

Details

@@ -247,6 +277,25 @@ const Details = memo(function Details({ workflow }: { workflow: WorkflowQueryRes
)} + {labelEntries.length > 0 && ( +
+
+ + Labels +
+
+ {labelEntries.map(([key, value]) => ( + + {key}={value} + + ))} +
+
+ )} @@ -304,6 +353,8 @@ const OverviewTab = memo(function OverviewTab({ workflow, canCancel, onCancel, o return (
+ + {/* Timeline section */}

Timeline

diff --git a/src/ui/src/features/workflows/detail/components/resubmit/resubmit-panel-content.tsx b/src/ui/src/features/workflows/detail/components/resubmit/resubmit-panel-content.tsx index 5d519e732f..0f61456732 100644 --- a/src/ui/src/features/workflows/detail/components/resubmit/resubmit-panel-content.tsx +++ b/src/ui/src/features/workflows/detail/components/resubmit/resubmit-panel-content.tsx @@ -35,6 +35,7 @@ import { PriorityPicker, PRIORITY_LABELS } from "@/components/workflow/priority- import { useSpecData } from "@/features/workflows/detail/hooks/use-spec-data"; import { SpecSection } from "@/features/workflows/detail/components/resubmit/spec-section"; import { useResubmitForm } from "@/features/workflows/detail/components/resubmit/use-resubmit-form"; +import { WorkflowLabelEditor } from "@/components/workflow/workflow-label-editor"; export interface ResubmitPanelContentProps { workflow: WorkflowQueryResponse; @@ -62,6 +63,7 @@ export const ResubmitPanelContent = memo(function ResubmitPanelContent({ const focusPanel = usePanelFocus(); const [poolOpen, setPoolOpen] = useState(true); const [priorityOpen, setPriorityOpen] = useState(true); + const [labelsOpen, setLabelsOpen] = useState(true); // Return focus to panel after priority selection so ESC works const handlePriorityChange = useCallback( @@ -114,13 +116,31 @@ export const ResubmitPanelContent = memo(function ResubmitPanelContent({ open={priorityOpen} onOpenChange={setPriorityOpen} selectedValue={PRIORITY_LABELS[form.priority]} - isLast > + + 0 ? `${form.labels.length} label${form.labels.length === 1 ? "" : "s"}` : undefined + } + isLast + > + +
{/* Error message */} diff --git a/src/ui/src/features/workflows/detail/components/resubmit/use-resubmit-form.ts b/src/ui/src/features/workflows/detail/components/resubmit/use-resubmit-form.ts index df75b1e938..eb6eebcabc 100644 --- a/src/ui/src/features/workflows/detail/components/resubmit/use-resubmit-form.ts +++ b/src/ui/src/features/workflows/detail/components/resubmit/use-resubmit-form.ts @@ -30,6 +30,12 @@ import type { WorkflowQueryResponse } from "@/lib/api/adapter/types"; import { WorkflowPriority } from "@/lib/api/generated"; import { usePoolSelection } from "@/components/workflow/use-pool-selection"; import { useResubmitMutation } from "@/features/workflows/detail/components/resubmit/use-resubmit-mutation"; +import { + getChangedWorkflowLabelAssignments, + sortedWorkflowLabelEntries, + validateWorkflowLabelDrafts, + type WorkflowLabelDraft, +} from "@/lib/workflow-labels"; export interface UseResubmitFormOptions { workflow: WorkflowQueryResponse; @@ -41,6 +47,11 @@ export interface UseResubmitFormReturn { setPool: (pool: string) => void; priority: WorkflowPriority; setPriority: (priority: WorkflowPriority) => void; + labels: WorkflowLabelDraft[]; + setLabels: (labels: WorkflowLabelDraft[]) => void; + labelError: string | null; + /** Count of leading drafts seeded from the workflow's own labels; their keys are locked. */ + lockedLabelCount: number; /** * Custom spec (if edited AND changed, otherwise undefined = use original via workflow_id). * - undefined: User hasn't edited OR content is identical to original @@ -68,13 +79,25 @@ export function useResubmitForm({ workflow, onSuccess }: UseResubmitFormOptions) const { pool, setPool } = usePoolSelection(workflow.pool ?? ""); const [priority, setPriority] = useState(() => deriveInitialPriority(workflow)); const [spec, setSpec] = useState(undefined); + const [labels, setLabels] = useState(() => + sortedWorkflowLabelEntries(workflow.labels).map(([key, value]) => ({ key, value })), + ); + const lockedLabelCount = Object.keys(workflow.labels ?? {}).length; + const labelError = useMemo(() => validateWorkflowLabelDrafts(labels), [labels]); + const labelAssignments = useMemo( + () => getChangedWorkflowLabelAssignments(labels, workflow.labels ?? {}), + [labels, workflow.labels], + ); const { execute, isPending, error } = useResubmitMutation({ - onSuccess: (newWorkflowName) => { + onSuccess: (newWorkflowName, warnings) => { const message = newWorkflowName ? `Workflow resubmitted as ${newWorkflowName}` : "Workflow resubmitted successfully"; + for (const warning of warnings) { + toast.warning(warning); + } toast.success(message, { action: newWorkflowName ? { @@ -88,7 +111,7 @@ export function useResubmitForm({ workflow, onSuccess }: UseResubmitFormOptions) }, }); - const canSubmit = pool.length > 0 && !isPending; + const canSubmit = pool.length > 0 && !isPending && !labelError; const handleSubmit = useCallback(() => { if (!canSubmit) return; @@ -98,8 +121,9 @@ export function useResubmitForm({ workflow, onSuccess }: UseResubmitFormOptions) poolName: pool, priority, spec, + labels: labelAssignments, }); - }, [canSubmit, execute, workflow.name, pool, priority, spec]); + }, [canSubmit, execute, workflow.name, pool, priority, spec, labelAssignments]); return useMemo( () => ({ @@ -107,6 +131,10 @@ export function useResubmitForm({ workflow, onSuccess }: UseResubmitFormOptions) setPool, priority, setPriority, + labels, + setLabels, + labelError, + lockedLabelCount, spec, setSpec, canSubmit, @@ -114,6 +142,6 @@ export function useResubmitForm({ workflow, onSuccess }: UseResubmitFormOptions) isPending, error, }), - [pool, setPool, priority, spec, canSubmit, handleSubmit, isPending, error], + [pool, setPool, priority, labels, labelError, lockedLabelCount, spec, canSubmit, handleSubmit, isPending, error], ); } diff --git a/src/ui/src/features/workflows/detail/components/resubmit/use-resubmit-mutation.ts b/src/ui/src/features/workflows/detail/components/resubmit/use-resubmit-mutation.ts index ef97bf793d..88045c7230 100644 --- a/src/ui/src/features/workflows/detail/components/resubmit/use-resubmit-mutation.ts +++ b/src/ui/src/features/workflows/detail/components/resubmit/use-resubmit-mutation.ts @@ -21,8 +21,8 @@ import { useServices } from "@/contexts/service-context"; import { resubmitWorkflow, type ResubmitParams } from "@/features/workflows/list/lib/actions"; export interface UseResubmitMutationOptions { - /** Called on successful resubmission with the new workflow name */ - onSuccess?: (newWorkflowName: string | undefined) => void; + /** Called on successful resubmission with the new workflow name and any admission warnings */ + onSuccess?: (newWorkflowName: string | undefined, warnings: string[]) => void; } export interface UseResubmitMutationReturn { @@ -48,7 +48,7 @@ export function useResubmitMutation(options: UseResubmitMutationOptions = {}): U if (actionResult.success) { announcer.announce("Workflow submitted successfully", "polite"); - onSuccess?.(actionResult.newWorkflowName); + onSuccess?.(actionResult.newWorkflowName, actionResult.warnings ?? []); } else { const errorMsg = actionResult.error ?? "Unknown error"; setError(errorMsg); diff --git a/src/ui/src/features/workflows/list/components/table/workflow-column-defs.tsx b/src/ui/src/features/workflows/list/components/table/workflow-column-defs.tsx index 7c0f35f9d6..9cf6c6e17d 100644 --- a/src/ui/src/features/workflows/list/components/table/workflow-column-defs.tsx +++ b/src/ui/src/features/workflows/list/components/table/workflow-column-defs.tsx @@ -32,6 +32,7 @@ import { PRIORITY_DISPLAY } from "@/lib/workflows/priority-display"; import { WORKFLOW_STATUS_ICONS } from "@/lib/workflows/workflow-status-icons"; import { formatDuration } from "@/lib/format-date"; import { WorkflowStatus, WorkflowPriority } from "@/lib/api/generated"; +import { formatWorkflowLabels } from "@/lib/workflow-labels"; export interface WorkflowSelectionOptions { selectedWorkflowNames: ReadonlySet; @@ -243,5 +244,25 @@ export function createWorkflowColumns(selection?: WorkflowSelectionOptions): Col {row.original.app_name || "—"} ), }, + { + id: "labels", + accessorKey: "labels", + header: COLUMN_LABELS.labels, + minSize: getMinSize("labels"), + enableSorting: false, + cell: ({ row }) => { + const labels = row.original.labels; + const hasLabels = Object.keys(labels ?? {}).length > 0; + const formatted = formatWorkflowLabels(labels); + return ( + + {formatted} + + ); + }, + }, ]; } diff --git a/src/ui/src/features/workflows/list/components/workflows-toolbar.tsx b/src/ui/src/features/workflows/list/components/workflows-toolbar.tsx index 803806abc3..f8ed4fed99 100644 --- a/src/ui/src/features/workflows/list/components/workflows-toolbar.tsx +++ b/src/ui/src/features/workflows/list/components/workflows-toolbar.tsx @@ -72,6 +72,8 @@ export const WorkflowsToolbar = memo(function WorkflowsToolbar({ WORKFLOW_FIELD.priority, WORKFLOW_FIELD.app, WORKFLOW_FIELD.tag, + WORKFLOW_FIELD.label, + WORKFLOW_FIELD.no_label, ], [userField, poolField], ); @@ -142,7 +144,7 @@ export const WorkflowsToolbar = memo(function WorkflowsToolbar({ searchChips={searchChips} onSearchChipsChange={onSearchChipsChange} defaultField="name" - placeholder="Search workflows... (try 'name:', 'status:', 'submitted:', 'pool:')" + placeholder="Search workflows... (try 'name:', 'status:', 'label:', 'no-label:')" searchPresets={searchPresets} resultsCount={resultsCount} autoRefreshProps={autoRefreshProps} diff --git a/src/ui/src/features/workflows/list/lib/actions.test.ts b/src/ui/src/features/workflows/list/lib/actions.test.ts new file mode 100644 index 0000000000..ddeca2dac0 --- /dev/null +++ b/src/ui/src/features/workflows/list/lib/actions.test.ts @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const { customFetch } = vi.hoisted(() => ({ customFetch: vi.fn() })); + +vi.mock("next/cache", () => ({ + revalidatePath: vi.fn(), + updateTag: vi.fn(), + refresh: vi.fn(), +})); +vi.mock("@/lib/api/fetcher", () => ({ customFetch })); + +import { resubmitWorkflow } from "@/features/workflows/list/lib/actions"; + +const WARN_MISSING_PROJECT_MESSAGE = + "Workflow is missing label 'project'; add it now to avoid rejected submissions once it is required."; + +describe("resubmit workflow labels", () => { + beforeEach(() => { + customFetch.mockReset(); + }); + + it("returns the raw submit name and warnings and sends repeated labels", async () => { + customFetch.mockResolvedValue({ + name: "workflow-copy-2", + warnings: [WARN_MISSING_PROJECT_MESSAGE], + }); + + const result = await resubmitWorkflow({ + workflowId: "workflow-1", + poolName: "pool-a", + priority: "NORMAL", + labels: ["project=robotics", "run=42"], + }); + + const endpoint = new URL(customFetch.mock.calls[0][0], "https://osmo.invalid"); + expect(endpoint.searchParams.getAll("label")).toEqual(["project=robotics", "run=42"]); + expect(result).toMatchObject({ + success: true, + newWorkflowName: "workflow-copy-2", + warnings: [WARN_MISSING_PROJECT_MESSAGE], + }); + }); +}); diff --git a/src/ui/src/features/workflows/list/lib/actions.ts b/src/ui/src/features/workflows/list/lib/actions.ts index 2b3e43cdb4..608d0b682b 100644 --- a/src/ui/src/features/workflows/list/lib/actions.ts +++ b/src/ui/src/features/workflows/list/lib/actions.ts @@ -41,6 +41,8 @@ import { revalidatePath, updateTag, refresh } from "next/cache"; import { customFetch } from "@/lib/api/fetcher"; +import type { SubmitResponse, SubmitWorkflowParams } from "@/lib/api/adapter/types"; +import { getSubmitWorkflowApiPoolPoolNameWorkflowPostUrl } from "@/lib/api/generated"; import type { ActionResult } from "@/lib/server-actions"; // ============================================================================= @@ -263,6 +265,8 @@ export async function cancelTaskGroup(workflowName: string, groupName: string): export interface ResubmitResult extends ActionResult { /** New workflow name returned by the backend on success */ newWorkflowName?: string; + /** Admission warnings returned for accepted submissions */ + warnings?: string[]; } export interface ResubmitParams { @@ -271,7 +275,7 @@ export interface ResubmitParams { /** Target pool for execution */ poolName: string; /** Execution priority */ - priority: string; + priority: SubmitWorkflowParams["priority"]; /** * Optional custom spec (if user edited and changed it) * - undefined: Backend fetches original spec via workflow_id (efficient) @@ -280,6 +284,8 @@ export interface ResubmitParams { * Backend constraint: EITHER template_spec OR workflow_id, never both. */ spec?: string; + /** Per-run key=value label overrides */ + labels?: string[]; } // ============================================================================= @@ -296,22 +302,22 @@ export interface ResubmitParams { * - If spec is provided: sends template_spec in body (custom workflow) * - If spec is NOT provided: sends workflow_id query param (reuses original spec) * - * @param params - Resubmit configuration (workflowId, poolName, priority, optional spec) + * @param params - Resubmit configuration (workflowId, poolName, priority, labels, optional spec) * @returns Result with the new workflow name on success, or error message */ export async function resubmitWorkflow(params: ResubmitParams): Promise { - const { workflowId, poolName, priority, spec } = params; - - const queryParams = new URLSearchParams(); - queryParams.set("priority", priority); - - // Backend constraint: EITHER template_spec OR workflow_id, never both - if (!spec) { - // No custom spec: send workflow_id to reuse original spec - queryParams.set("workflow_id", workflowId); - } + const { workflowId, poolName, priority, spec, labels = [] } = params; + + // Backend constraint: EITHER template_spec OR workflow_id, never both. + // The generated URL builder explodes repeated `label` params and skips + // undefined values. + const queryParams: SubmitWorkflowParams = { + priority, + label: labels.length > 0 ? labels : undefined, + workflow_id: spec ? undefined : workflowId, + }; - const endpoint = `/api/pool/${encodeURIComponent(poolName)}/workflow?${queryParams.toString()}`; + const endpoint = getSubmitWorkflowApiPoolPoolNameWorkflowPostUrl(encodeURIComponent(poolName), queryParams); try { const init: RequestInit = { method: "POST" }; @@ -321,16 +327,16 @@ export async function resubmitWorkflow(params: ResubmitParams): Promise(endpoint, init); + const response = await customFetch(endpoint, init); - const newName = response?.data?.name; + const newName = response.name; // No cache revalidation needed - creating a new workflow doesn't affect: // - Current workflow page (unchanged) // - New workflow page (will fetch fresh when user navigates to it) // - Workflows list (will fetch fresh when user navigates to it) - return { success: true, newWorkflowName: newName }; + return { success: true, newWorkflowName: newName, warnings: response.warnings ?? [] }; } catch (error) { return { success: false, diff --git a/src/ui/src/features/workflows/list/lib/workflow-columns.test.ts b/src/ui/src/features/workflows/list/lib/workflow-columns.test.ts new file mode 100644 index 0000000000..2112b0d0c0 --- /dev/null +++ b/src/ui/src/features/workflows/list/lib/workflow-columns.test.ts @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { describe, expect, it } from "vitest"; +import { + COLUMN_LABELS, + DEFAULT_COLUMN_ORDER, + DEFAULT_VISIBLE_COLUMNS, + isWorkflowColumnId, +} from "@/features/workflows/list/lib/workflow-columns"; + +describe("workflow labels column", () => { + it("is visible by default and has a stable column identity", () => { + expect(isWorkflowColumnId("labels")).toBe(true); + expect(COLUMN_LABELS.labels).toBe("Labels"); + expect(DEFAULT_COLUMN_ORDER).toContain("labels"); + expect(DEFAULT_VISIBLE_COLUMNS).toContain("labels"); + }); +}); diff --git a/src/ui/src/features/workflows/list/lib/workflow-columns.ts b/src/ui/src/features/workflows/list/lib/workflow-columns.ts index 88fc8a6502..85d21205f1 100644 --- a/src/ui/src/features/workflows/list/lib/workflow-columns.ts +++ b/src/ui/src/features/workflows/list/lib/workflow-columns.ts @@ -33,7 +33,8 @@ export type WorkflowColumnId = | "queued_time" | "pool" | "priority" - | "app_name"; + | "app_name" + | "labels"; // ============================================================================= // Column Configuration (via factory) @@ -53,6 +54,7 @@ const workflowColumnConfig = createColumnConfig({ "pool", "priority", "app_name", + "labels", ] as const, labels: { _select: "", @@ -67,9 +69,10 @@ const workflowColumnConfig = createColumnConfig({ pool: "Pool", priority: "Priority", app_name: "App", + labels: "Labels", }, mandatory: ["_select", "name"], - defaultVisible: ["_select", "name", "status", "user", "submit_time", "duration", "pool", "priority"], + defaultVisible: ["_select", "name", "status", "user", "submit_time", "duration", "pool", "priority", "labels"], defaultOrder: [ "_select", "name", @@ -83,6 +86,7 @@ const workflowColumnConfig = createColumnConfig({ "pool", "priority", "app_name", + "labels", ], sizeConfig: [ { @@ -145,6 +149,11 @@ const workflowColumnConfig = createColumnConfig({ minWidthRem: COLUMN_MIN_WIDTHS_REM.TEXT_SHORT, preferredWidthRem: COLUMN_PREFERRED_WIDTHS_REM.TEXT_SHORT, }, + { + id: "labels", + minWidthRem: COLUMN_MIN_WIDTHS_REM.TEXT_TRUNCATE, + preferredWidthRem: COLUMN_PREFERRED_WIDTHS_REM.TEXT_TRUNCATE * 1.5, + }, ], defaultSort: { column: "submit_time", direction: "desc" }, }); diff --git a/src/ui/src/features/workflows/list/lib/workflow-search-fields.test.ts b/src/ui/src/features/workflows/list/lib/workflow-search-fields.test.ts index 99fad43140..e5c4c7eba3 100644 --- a/src/ui/src/features/workflows/list/lib/workflow-search-fields.test.ts +++ b/src/ui/src/features/workflows/list/lib/workflow-search-fields.test.ts @@ -47,6 +47,8 @@ describe("WORKFLOW_STATIC_FIELDS structure", () => { expect(fieldIds).toContain("priority"); expect(fieldIds).toContain("app"); expect(fieldIds).toContain("tag"); + expect(fieldIds).toContain("label"); + expect(fieldIds).toContain("no_label"); }); it("all fields have required properties", () => { @@ -74,6 +76,8 @@ describe("WORKFLOW_STATIC_FIELDS structure", () => { expect(getField("priority").prefix).toBe("priority:"); expect(getField("app").prefix).toBe("app:"); expect(getField("tag").prefix).toBe("tag:"); + expect(getField("label").prefix).toBe("label:"); + expect(getField("no_label").prefix).toBe("no-label:"); }); }); @@ -177,6 +181,27 @@ describe("tag field", () => { }); }); +describe("workflow label fields", () => { + it("accepts exact, glob, and alternative key=value filters as free-form input", () => { + const labelField = getField("label"); + + expect(getFieldValues(labelField, [])).toEqual([]); + expect(labelField.freeFormHint).toContain("key=value"); + expect(labelField.freeFormHint).toContain("key=(team_*|osmo_*)"); + expect(labelField.freeFormHint).toContain("key=team_(a|b)"); + expect(labelField.prefix).toBe("label:"); + expect(labelField.singular).toBeUndefined(); + }); + + it("accepts missing-key filters as free-form input", () => { + const noLabelField = getField("no_label"); + + expect(getFieldValues(noLabelField, [])).toEqual([]); + expect(noLabelField.freeFormHint).toContain("key"); + expect(noLabelField.singular).toBeUndefined(); + }); +}); + describe("STATUS_PRESETS", () => { it("contains expected preset categories", () => { expect(STATUS_PRESETS).toHaveProperty("running"); diff --git a/src/ui/src/features/workflows/list/lib/workflow-search-fields.ts b/src/ui/src/features/workflows/list/lib/workflow-search-fields.ts index e097872b57..81370f539e 100644 --- a/src/ui/src/features/workflows/list/lib/workflow-search-fields.ts +++ b/src/ui/src/features/workflows/list/lib/workflow-search-fields.ts @@ -75,6 +75,24 @@ export const WORKFLOW_FIELD: Readonly [], }, + label: { + id: "label", + label: "Label", + hint: "workflow label selector (exact, glob, or alternatives)", + prefix: "label:", + freeFormHint: "Type key=value, key=(team_*|osmo_*), or key=team_(a|b), press Enter", + getValues: () => [], + }, + no_label: { + // The id matches the backend query parameter; the typed prefix stays + // kebab-case like the other chips. + id: "no_label", + label: "Missing Label", + hint: "workflow missing a label key", + prefix: "no-label:", + freeFormHint: "Type a label key, press Enter", + getValues: () => [], + }, }); export const WORKFLOW_STATIC_FIELDS: readonly SearchField[] = Object.freeze( diff --git a/src/ui/src/lib/api/adapter/types.ts b/src/ui/src/lib/api/adapter/types.ts index 15c7769cf1..f9d4feb51b 100644 --- a/src/ui/src/lib/api/adapter/types.ts +++ b/src/ui/src/lib/api/adapter/types.ts @@ -36,6 +36,8 @@ export type { WorkflowQueryResponse, GroupQueryResponse, TaskQueryResponse, + SubmitResponse, + SubmitWorkflowApiPoolPoolNameWorkflowPostParams as SubmitWorkflowParams, SrcServiceCoreWorkflowObjectsListEntry as WorkflowListEntry, } from "@/lib/api/generated"; diff --git a/src/ui/src/lib/api/adapter/workflows-shim.test.ts b/src/ui/src/lib/api/adapter/workflows-shim.test.ts new file mode 100644 index 0000000000..3c4a511e9b --- /dev/null +++ b/src/ui/src/lib/api/adapter/workflows-shim.test.ts @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { describe, expect, it } from "vitest"; +import { buildWorkflowApiParams, buildWorkflowsQueryKey } from "@/lib/api/adapter/workflows-shim"; + +const chips = [ + { field: "label", value: "team=robotics", label: "label: team=robotics" }, + { field: "label", value: "run=42", label: "label: run=42" }, + { field: "no_label", value: "deprecated", label: "no-label: deprecated" }, +]; + +describe("workflow label filters", () => { + it("maps chips to repeatable backend query parameters", () => { + expect(buildWorkflowApiParams(chips, false, 0, 50, "DESC")).toMatchObject({ + label: ["team=robotics", "run=42"], + no_label: ["deprecated"], + }); + }); + + it("keeps label selectors in the stable query key", () => { + expect(buildWorkflowsQueryKey(chips, false, "DESC")).toEqual([ + "workflows", + "paginated", + { + labels: ["run=42", "team=robotics"], + missingLabels: ["deprecated"], + showAllUsers: false, + sortDirection: "DESC", + }, + ]); + }); +}); diff --git a/src/ui/src/lib/api/adapter/workflows-shim.ts b/src/ui/src/lib/api/adapter/workflows-shim.ts index 11ec34c446..1cdbc62d16 100644 --- a/src/ui/src/lib/api/adapter/workflows-shim.ts +++ b/src/ui/src/lib/api/adapter/workflows-shim.ts @@ -77,7 +77,7 @@ function getFirstChipValue(chips: SearchChip[], field: string): string | undefin /** * Build API parameters from search chips and options. */ -function buildApiParams( +export function buildWorkflowApiParams( chips: SearchChip[], showAllUsers: boolean, offset: number, @@ -90,6 +90,8 @@ function buildApiParams( const userChips = getChipValues(chips, "user"); const priorityChips = getChipValues(chips, "priority"); const tagChips = getChipValues(chips, "tag"); + const labelChips = getChipValues(chips, "label"); + const missingLabelChips = getChipValues(chips, "no_label"); // Resolve submitted date range: chip takes precedence over prop let resolvedAfter = submittedAfter; @@ -114,6 +116,8 @@ function buildApiParams( app: getFirstChipValue(chips, "app"), priority: priorityChips.length > 0 ? (priorityChips as WorkflowPriority[]) : undefined, tags: tagChips.length > 0 ? tagChips : undefined, + label: labelChips.length > 0 ? labelChips : undefined, + no_label: missingLabelChips.length > 0 ? missingLabelChips : undefined, all_users: userChips.length === 0 && showAllUsers ? true : undefined, all_pools: poolChips.length === 0, submitted_after: resolvedAfter, @@ -138,7 +142,7 @@ export async function fetchPaginatedWorkflows( const { offset = 0, limit, searchChips, showAllUsers = false, sortDirection = "DESC", submittedAfter } = params; // Build API params from chips - const apiParams = buildApiParams( + const apiParams = buildWorkflowApiParams( searchChips, showAllUsers, offset, @@ -191,6 +195,8 @@ export function buildWorkflowsQueryKey( const pools = getChipValues(searchChips, "pool").sort(); const priority = getChipValues(searchChips, "priority").sort(); const tags = getChipValues(searchChips, "tag").sort(); + const labels = getChipValues(searchChips, "label").sort(); + const missingLabels = getChipValues(searchChips, "no_label").sort(); const submitted = getFirstChipValue(searchChips, "submitted"); // Build query key - only include filters that have values @@ -202,6 +208,8 @@ export function buildWorkflowsQueryKey( if (pools.length > 0) filters.pools = pools; if (priority.length > 0) filters.priority = priority; if (tags.length > 0) filters.tags = tags; + if (labels.length > 0) filters.labels = labels; + if (missingLabels.length > 0) filters.missingLabels = missingLabels; if (submitted) filters.submitted = submitted; return [ diff --git a/src/ui/src/lib/api/generated.ts b/src/ui/src/lib/api/generated.ts index c7f457b8cb..ef166ed036 100644 --- a/src/ui/src/lib/api/generated.ts +++ b/src/ui/src/lib/api/generated.ts @@ -1,5 +1,5 @@ /** - * Generated by orval v8.5.3 🍺 + * Generated by orval v8.17.0 🍺 * Do not edit manually. * FastAPI * OpenAPI spec version: 0.1.0 @@ -235,10 +235,11 @@ export interface ConfigDiffResponse { /** * Type of configs supported by config history */ -export type SrcLibUtilsConfigHistoryConfigHistoryType = typeof SrcLibUtilsConfigHistoryConfigHistoryType[keyof typeof SrcLibUtilsConfigHistoryConfigHistoryType]; +export type ConfigHistoryType = typeof ConfigHistoryType[keyof typeof ConfigHistoryType]; -export const SrcLibUtilsConfigHistoryConfigHistoryType = { +export const ConfigHistoryType = { + DATASET: 'DATASET', SERVICE: 'SERVICE', WORKFLOW: 'WORKFLOW', BACKEND: 'BACKEND', @@ -254,7 +255,7 @@ export const SrcLibUtilsConfigHistoryConfigHistoryType = { * Object storing config history. */ export interface ConfigHistory { - config_type: SrcLibUtilsConfigHistoryConfigHistoryType; + config_type: ConfigHistoryType; name: string; revision: number; username: string; @@ -309,6 +310,18 @@ export interface UserRegistryCredential { auth: string; } +/** + * S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. + */ +export type UserDataCredentialAddressingStyle = typeof UserDataCredentialAddressingStyle[keyof typeof UserDataCredentialAddressingStyle] | null; + + +export const UserDataCredentialAddressingStyle = { + virtual: 'virtual', + path: 'path', + auto: 'auto', +} as const; + /** * Authentication information for a data service. */ @@ -320,7 +333,7 @@ export interface UserDataCredential { /** HTTP service URL for S3-compatible providers (e.g., http://minio:9000, https://s3-compat.example.com). Leave unset for native AWS S3, GCS, Azure, etc. */ override_url?: string | null; /** S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. */ - addressing_style?: 'virtual' | 'path' | 'auto' | null; + addressing_style?: UserDataCredentialAddressingStyle; /** The authentication key for a data backend */ access_key_id: string; /** The authentication secret for a data backend */ @@ -352,6 +365,18 @@ export interface CredentialOptions { generic_credential?: UserCredential | null; } +/** + * S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. + */ +export type StaticDataCredentialAddressingStyle = typeof StaticDataCredentialAddressingStyle[keyof typeof StaticDataCredentialAddressingStyle] | null; + + +export const StaticDataCredentialAddressingStyle = { + virtual: 'virtual', + path: 'path', + auto: 'auto', +} as const; + /** * Static data credentials (i.e. credentials with access_key_id and access_key) for a data backend. */ @@ -363,7 +388,7 @@ export interface StaticDataCredential { /** HTTP service URL for S3-compatible providers (e.g., http://minio:9000, https://s3-compat.example.com). Leave unset for native AWS S3, GCS, Azure, etc. */ override_url?: string | null; /** S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. */ - addressing_style?: 'virtual' | 'path' | 'auto' | null; + addressing_style?: StaticDataCredentialAddressingStyle; /** The authentication key for a data backend */ access_key_id: string; /** The encrypted authentication secret for a data backend */ @@ -371,13 +396,25 @@ export interface StaticDataCredential { } /** - * Data credential that delegates resolution to the underlying SDK. + * S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. + */ +export type DefaultDataCredentialAddressingStyle = typeof DefaultDataCredentialAddressingStyle[keyof typeof DefaultDataCredentialAddressingStyle] | null; -Uses the SDK's default credential chain (e.g., Azure's DefaultAzureCredential, -boto3's credential resolution) which may include environment variables, -workload identity, instance metadata, and other provider-specific methods. -Intentionally left empty as all credential resolution is handled by the SDK. +export const DefaultDataCredentialAddressingStyle = { + virtual: 'virtual', + path: 'path', + auto: 'auto', +} as const; + +/** + * Data credential that delegates resolution to the underlying SDK. + * + * Uses the SDK's default credential chain (e.g., Azure's DefaultAzureCredential, + * boto3's credential resolution) which may include environment variables, + * workload identity, instance metadata, and other provider-specific methods. + * + * Intentionally left empty as all credential resolution is handled by the SDK. */ export interface DefaultDataCredential { /** The OSMO storage URI for the data service (e.g., s3://bucket). For S3-compatible services with HTTP endpoints, set 'override_url' separately rather than pasting the full HTTPS URL here. */ @@ -387,7 +424,7 @@ export interface DefaultDataCredential { /** HTTP service URL for S3-compatible providers (e.g., http://minio:9000, https://s3-compat.example.com). Leave unset for native AWS S3, GCS, Azure, etc. */ override_url?: string | null; /** S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. */ - addressing_style?: 'virtual' | 'path' | 'auto' | null; + addressing_style?: DefaultDataCredentialAddressingStyle; } /** @@ -442,7 +479,7 @@ export const PoolStatus = { /** * Resources like GPU or CPU that have a discrete number. For guarantee and maximum, a value of -1 -indicates that there is no limit. + * indicates that there is no limit. */ export interface PoolResourceCountable { guarantee?: number; @@ -614,10 +651,14 @@ export interface GroupQueryResponse { tasks?: TaskQueryResponse[]; } +export type ValidationErrorCtx = { [key: string]: unknown }; + export interface ValidationError { loc: (string | number)[]; msg: string; type: string; + input?: unknown; + ctx?: ValidationErrorCtx; } export interface HTTPValidationError { @@ -633,6 +674,49 @@ export interface JwtTokenResponse { error?: string | null; } +/** + * Per-key policy strictness: 'off' skips checking, 'warn' surfaces + * missing or unlisted values as submission warnings, and 'enforce' + * rejects them. + */ +export type LabelEnforcement = typeof LabelEnforcement[keyof typeof LabelEnforcement]; + + +export const LabelEnforcement = { + off: 'off', + warn: 'warn', + enforce: 'enforce', +} as const; + +/** + * Configuration for one admin-designated workflow label key. + * + * An empty allow_list accepts any well-formed value; enforcement then + * applies only to the key being present. + */ +export interface LabelPolicy { + key: string; + allow_list?: string[]; + enforcement?: LabelEnforcement; + assert_message?: string; +} + +/** + * Curated workflow label policy; empty by default, so no policy + * applies until configured. + */ +export interface LabelsConfigInput { + policy?: LabelPolicy[]; +} + +/** + * Curated workflow label policy; empty by default, so no policy + * applies until configured. + */ +export interface LabelsConfigOutput { + policy?: LabelPolicy[]; +} + /** * Object storing info for all backends. */ @@ -777,6 +861,24 @@ export interface NotificationConfig { smtp_settings?: SMTPConfig; } +/** + * Type of configs supported by config history mutations. + */ +export type OperableConfigHistoryType = typeof OperableConfigHistoryType[keyof typeof OperableConfigHistoryType]; + + +export const OperableConfigHistoryType = { + SERVICE: 'SERVICE', + WORKFLOW: 'WORKFLOW', + BACKEND: 'BACKEND', + POOL: 'POOL', + POD_TEMPLATE: 'POD_TEMPLATE', + GROUP_TEMPLATE: 'GROUP_TEMPLATE', + RESOURCE_VALIDATION: 'RESOURCE_VALIDATION', + BACKEND_TEST: 'BACKEND_TEST', + ROLE: 'ROLE', +} as const; + export type OperatorType = typeof OperatorType[keyof typeof OperatorType]; @@ -802,7 +904,7 @@ export interface RegistryCredential { /** * Dynamic Config for storing the image URLs for service images and the credentials needed -to pull them. + * to pull them. */ export interface OsmoImageConfig { init?: string; @@ -934,35 +1036,35 @@ export interface RsyncConfig { enabled?: boolean; enable_telemetry?: boolean; /** - * User pod's rsync read bandwidth limit in bytes per second, zero means no limit - * @minimum 0 - */ + * User pod's rsync read bandwidth limit in bytes per second, zero means no limit + * @minimum 0 + */ read_bandwidth_limit?: number; /** - * User pod's rsync write bandwidth limit in bytes per second, zero means no limit - * @minimum 0 - */ + * User pod's rsync write bandwidth limit in bytes per second, zero means no limit + * @minimum 0 + */ write_bandwidth_limit?: number; allowed_paths?: RsyncConfigAllowedPaths; /** - * Daemon debounce delay for rsync in seconds - * @exclusiveMinimum 0 - */ + * Daemon debounce delay for rsync in seconds + * @exclusiveMinimum 0 + */ daemon_debounce_delay?: number; /** - * Daemon poll interval for rsync in seconds - * @exclusiveMinimum 0 - */ + * Daemon poll interval for rsync in seconds + * @exclusiveMinimum 0 + */ daemon_poll_interval?: number; /** - * Daemon reconcile interval for rsync in seconds - * @exclusiveMinimum 0 - */ + * Daemon reconcile interval for rsync in seconds + * @exclusiveMinimum 0 + */ daemon_reconcile_interval?: number; /** - * Client upload rate limit for rsync in bytes per second, zero means no limit - * @minimum 0 - */ + * Client upload rate limit for rsync in bytes per second, zero means no limit + * @minimum 0 + */ client_upload_rate_limit?: number; } @@ -1180,7 +1282,7 @@ export interface TokenIdentity { /** * Profile and identity info. When token header is set, roles/pools are the -token's; otherwise they are the user's. JSON is self-explanatory for CLI. + * token's; otherwise they are the user's. JSON is self-explanatory for CLI. */ export interface ProfileResponse { profile: UserProfile; @@ -1308,13 +1410,13 @@ export interface PutResourceValidationsRequest { /** * Single Role Policy Entry. - -Contains a list of actions (semantic format "resource:Action") and optional -resources the policy applies to. If effect is Deny and the policy matches, -access is denied even if another policy allows it. - -Actions are validated via regex; API/DB still use [{"action": "..."}] for -compatibility with the Go authz_sidecar. + * + * Contains a list of actions (semantic format "resource:Action") and optional + * resources the policy applies to. If effect is Deny and the policy matches, + * access is denied even if another policy allows it. + * + * Actions are validated via regex; API/DB still use [{"action": "..."}] for + * compatibility with the Go authz_sidecar. */ export interface RolePolicy { effect?: PolicyEffect; @@ -1324,10 +1426,10 @@ export interface RolePolicy { /** * Sync mode for role assignments. - -- FORCE: Always apply this role to all users (e.g., for system roles) -- IMPORT: Role is imported from IDP claims or user_roles table (default) -- IGNORE: Ignore this role in IDP sync (role is managed manually) + * + * - FORCE: Always apply this role to all users (e.g., for system roles) + * - IMPORT: Role is imported from IDP claims or user_roles table (default) + * - IGNORE: Ignore this role in IDP sync (role is managed manually) */ export type SyncMode = typeof SyncMode[keyof typeof SyncMode]; @@ -1340,9 +1442,9 @@ export const SyncMode = { /** * Single Role Entry. - -Note: Authorization checking is now handled by the authz_sidecar (Go service). -This Python class is only used for role CRUD operations. + * + * Note: Authorization checking is now handled by the authz_sidecar (Go service). + * This Python class is only used for role CRUD operations. */ export interface RoleInput { name: string; @@ -1401,7 +1503,7 @@ export interface WorkflowInfo { /** * Stores workflow limits per user. Default is None, which means no limit. -If a limit is set, it must be greater than 0. + * If a limit is set, it must be greater than 0. */ export interface UserWorkflowLimitConfig { max_num_workflows?: number | null; @@ -1424,6 +1526,7 @@ export interface WorkflowConfigInput { credential_config?: CredentialConfig; user_workflow_limits?: UserWorkflowLimitConfig; plugins_config?: PluginsConfigInput; + labels_config?: LabelsConfigInput; max_num_tasks?: number; max_num_ports_per_task?: number; max_retry_per_task?: number; @@ -1520,9 +1623,9 @@ export interface ResourcesResponse { /** * Single Role Entry. - -Note: Authorization checking is now handled by the authz_sidecar (Go service). -This Python class is only used for role CRUD operations. + * + * Note: Authorization checking is now handled by the authz_sidecar (Go service). + * This Python class is only used for role CRUD operations. */ export interface RoleOutput { name: string; @@ -1549,11 +1652,11 @@ export interface RoleUsersResponse { export interface RollbackConfigRequest { description?: string | null; tags?: string[] | null; - config_type: SrcLibUtilsConfigHistoryConfigHistoryType; + config_type: OperableConfigHistoryType; /** - * Revision to roll back to - * @exclusiveMinimum 0 - */ + * Revision to roll back to + * @exclusiveMinimum 0 + */ revision: number; } @@ -1586,6 +1689,7 @@ export interface SubmitResponse { logs?: string | null; spec?: string | null; dashboard_url?: string | null; + warnings?: string[]; } /** @@ -1719,6 +1823,7 @@ export interface WorkflowConfigOutput { credential_config?: CredentialConfig; user_workflow_limits?: UserWorkflowLimitConfig; plugins_config?: PluginsConfigOutput; + labels_config?: LabelsConfigOutput; max_num_tasks?: number; max_num_ports_per_task?: number; max_retry_per_task?: number; @@ -1755,6 +1860,8 @@ export const WorkflowPriority = { LOW: 'LOW', } as const; +export type WorkflowQueryResponseLabels = {[key: string]: string}; + /** * Represents the status of a workflow. */ @@ -1815,6 +1922,8 @@ export interface WorkflowQueryResponse { app_version?: number | null; plugins: WorkflowPlugins; priority: string; + labels?: WorkflowQueryResponseLabels; + warnings?: string[]; } export interface SrcServiceCoreAppObjectsListEntry { @@ -1831,6 +1940,8 @@ export interface SrcServiceCoreAppObjectsListResponse { more_entries: boolean; } +export type SrcServiceCoreWorkflowObjectsListEntryLabels = {[key: string]: string}; + /** * Entry for list API results. */ @@ -1854,6 +1965,7 @@ export interface SrcServiceCoreWorkflowObjectsListEntry { app_name?: string | null; app_version?: number | null; priority: string; + labels?: SrcServiceCoreWorkflowObjectsListEntryLabels; } export interface SrcServiceCoreWorkflowObjectsListResponse { @@ -1916,7 +2028,7 @@ order?: ListOrder; /** * Filter by config types */ -config_types?: SrcLibUtilsConfigHistoryConfigHistoryType[] | null; +config_types?: ConfigHistoryType[] | null; /** * Filter by config name */ @@ -1948,7 +2060,7 @@ omit_data?: boolean; }; export type GetConfigDiffApiConfigsDiffGetParams = { -config_type: SrcLibUtilsConfigHistoryConfigHistoryType; +config_type: ConfigHistoryType; /** * First revision to compare * @exclusiveMinimum 0 @@ -2049,6 +2161,14 @@ submitted_after?: string | null; tags?: string[] | null; app?: string | null; priority?: WorkflowPriority[] | null; +/** + * Workflow label selector: key=value with optional * wildcards and (a|b) alternatives, for example key=(team_*|osmo_*) or key=team_(a|b). Repeat for AND semantics. + */ +label?: string[] | null; +/** + * Label key that must be absent from the workflow; workflows without any labels match. Repeat for AND semantics. + */ +no_label?: string[] | null; }; export type ListTaskApiTaskGetParams = { @@ -2146,6 +2266,7 @@ dry_run?: boolean; validation_only?: boolean; priority?: WorkflowPriority; env_vars?: string[]; +label?: string[]; }; export type SetNotificationSettingsApiProfileSettingsPostParams = { @@ -2160,10 +2281,6 @@ type SecondParameter unknown> = Parameters[1]; -/** - * Read all the service configurations - * @summary Read Service Configs - */ export const getReadServiceConfigsApiConfigsServiceGetUrl = () => { @@ -2172,6 +2289,10 @@ export const getReadServiceConfigsApiConfigsServiceGetUrl = () => { return `/api/configs/service` } +/** + * Read all the service configurations + * @summary Read Service Configs + */ export const readServiceConfigsApiConfigsServiceGet = async ( options?: RequestInit): Promise => { return customFetch(getReadServiceConfigsApiConfigsServiceGetUrl(), @@ -2271,10 +2392,8 @@ export const invalidateReadServiceConfigsApiConfigsServiceGet = async ( -/** - * Put service configurations - * @summary Put Service Configs - */ + + export const getPutServiceConfigsApiConfigsServicePutUrl = () => { @@ -2283,6 +2402,10 @@ export const getPutServiceConfigsApiConfigsServicePutUrl = () => { return `/api/configs/service` } +/** + * Put service configurations + * @summary Put Service Configs + */ export const putServiceConfigsApiConfigsServicePut = async (putServiceRequest: PutServiceRequest, options?: RequestInit): Promise => { return customFetch(getPutServiceConfigsApiConfigsServicePutUrl(), @@ -2290,8 +2413,7 @@ export const putServiceConfigsApiConfigsServicePut = async (putServiceRequest: P ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putServiceRequest,) + body: JSON.stringify(putServiceRequest) } );} @@ -2343,10 +2465,6 @@ export const usePutServiceConfigsApiConfigsServicePut = { @@ -2355,6 +2473,10 @@ export const getPatchServiceConfigsApiConfigsServicePatchUrl = () => { return `/api/configs/service` } +/** + * Patch service configurations + * @summary Patch Service Configs + */ export const patchServiceConfigsApiConfigsServicePatch = async (patchConfigRequest: PatchConfigRequest, options?: RequestInit): Promise => { return customFetch(getPatchServiceConfigsApiConfigsServicePatchUrl(), @@ -2362,8 +2484,7 @@ export const patchServiceConfigsApiConfigsServicePatch = async (patchConfigReque ...options, method: 'PATCH', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - patchConfigRequest,) + body: JSON.stringify(patchConfigRequest) } );} @@ -2415,10 +2536,6 @@ export const usePatchServiceConfigsApiConfigsServicePatch = { @@ -2427,6 +2544,10 @@ export const getReadWorkflowConfigsApiConfigsWorkflowGetUrl = () => { return `/api/configs/workflow` } +/** + * Read all the workflow configurations + * @summary Read Workflow Configs + */ export const readWorkflowConfigsApiConfigsWorkflowGet = async ( options?: RequestInit): Promise => { return customFetch(getReadWorkflowConfigsApiConfigsWorkflowGetUrl(), @@ -2526,10 +2647,8 @@ export const invalidateReadWorkflowConfigsApiConfigsWorkflowGet = async ( -/** - * Put workflow configurations - * @summary Put Workflow Configs - */ + + export const getPutWorkflowConfigsApiConfigsWorkflowPutUrl = () => { @@ -2538,6 +2657,10 @@ export const getPutWorkflowConfigsApiConfigsWorkflowPutUrl = () => { return `/api/configs/workflow` } +/** + * Put workflow configurations + * @summary Put Workflow Configs + */ export const putWorkflowConfigsApiConfigsWorkflowPut = async (putWorkflowRequest: PutWorkflowRequest, options?: RequestInit): Promise => { return customFetch(getPutWorkflowConfigsApiConfigsWorkflowPutUrl(), @@ -2545,8 +2668,7 @@ export const putWorkflowConfigsApiConfigsWorkflowPut = async (putWorkflowRequest ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putWorkflowRequest,) + body: JSON.stringify(putWorkflowRequest) } );} @@ -2598,10 +2720,6 @@ export const usePutWorkflowConfigsApiConfigsWorkflowPut = { @@ -2610,6 +2728,10 @@ export const getPatchWorkflowConfigsApiConfigsWorkflowPatchUrl = () => { return `/api/configs/workflow` } +/** + * Patch workflow configurations + * @summary Patch Workflow Configs + */ export const patchWorkflowConfigsApiConfigsWorkflowPatch = async (patchConfigRequest: PatchConfigRequest, options?: RequestInit): Promise => { return customFetch(getPatchWorkflowConfigsApiConfigsWorkflowPatchUrl(), @@ -2617,8 +2739,7 @@ export const patchWorkflowConfigsApiConfigsWorkflowPatch = async (patchConfigReq ...options, method: 'PATCH', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - patchConfigRequest,) + body: JSON.stringify(patchConfigRequest) } );} @@ -2670,10 +2791,6 @@ export const usePatchWorkflowConfigsApiConfigsWorkflowPatch = { @@ -2682,6 +2799,10 @@ export const getListBackendsApiConfigsBackendGetUrl = () => { return `/api/configs/backend` } +/** + * List all backends. + * @summary List Backends + */ export const listBackendsApiConfigsBackendGet = async ( options?: RequestInit): Promise => { return customFetch(getListBackendsApiConfigsBackendGetUrl(), @@ -2781,10 +2902,8 @@ export const invalidateListBackendsApiConfigsBackendGet = async ( -/** - * Override the config for a specific backend. - * @summary Update Backend - */ + + export const getUpdateBackendApiConfigsBackendNamePostUrl = (name: string,) => { @@ -2793,6 +2912,10 @@ export const getUpdateBackendApiConfigsBackendNamePostUrl = (name: string,) => { return `/api/configs/backend/${name}` } +/** + * Override the config for a specific backend. + * @summary Update Backend + */ export const updateBackendApiConfigsBackendNamePost = async (name: string, postBackendRequest: PostBackendRequest, options?: RequestInit): Promise => { @@ -2801,8 +2924,7 @@ export const updateBackendApiConfigsBackendNamePost = async (name: string, ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - postBackendRequest,) + body: JSON.stringify(postBackendRequest) } );} @@ -2854,10 +2976,6 @@ export const useUpdateBackendApiConfigsBackendNamePost = { @@ -2866,6 +2984,10 @@ export const getGetBackendApiConfigsBackendNameGetUrl = (name: string,) => { return `/api/configs/backend/${name}` } +/** + * Get info for a specific backend. + * @summary Get Backend + */ export const getBackendApiConfigsBackendNameGet = async (name: string, options?: RequestInit): Promise => { return customFetch(getGetBackendApiConfigsBackendNameGetUrl(name), @@ -2903,7 +3025,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type GetBackendApiConfigsBackendNameGetQueryResult = NonNullable>> @@ -2965,10 +3087,8 @@ export const invalidateGetBackendApiConfigsBackendNameGet = async ( -/** - * Remove a backend. - * @summary Delete Backend - */ + + export const getDeleteBackendApiConfigsBackendNameDeleteUrl = (name: string,) => { @@ -2977,6 +3097,10 @@ export const getDeleteBackendApiConfigsBackendNameDeleteUrl = (name: string,) => return `/api/configs/backend/${name}` } +/** + * Remove a backend. + * @summary Delete Backend + */ export const deleteBackendApiConfigsBackendNameDelete = async (name: string, deleteBackendRequest: DeleteBackendRequest, options?: RequestInit): Promise => { @@ -2985,8 +3109,7 @@ export const deleteBackendApiConfigsBackendNameDelete = async (name: string, ...options, method: 'DELETE', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - deleteBackendRequest,) + body: JSON.stringify(deleteBackendRequest) } );} @@ -3038,17 +3161,13 @@ export const useDeleteBackendApiConfigsBackendNameDelete = { const normalizedParams = new URLSearchParams(); Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -3057,6 +3176,10 @@ export const getListPoolsApiConfigsPoolGetUrl = (params?: ListPoolsApiConfigsPoo return stringifiedParams.length > 0 ? `/api/configs/pool?${stringifiedParams}` : `/api/configs/pool` } +/** + * List all Pools + * @summary List Pools + */ export const listPoolsApiConfigsPoolGet = async (params?: ListPoolsApiConfigsPoolGetParams, options?: RequestInit): Promise => { return customFetch(getListPoolsApiConfigsPoolGetUrl(params), @@ -3156,10 +3279,8 @@ export const invalidateListPoolsApiConfigsPoolGet = async ( -/** - * Put Pool configurations - * @summary Put Pools - */ + + export const getPutPoolsApiConfigsPoolPutUrl = () => { @@ -3168,6 +3289,10 @@ export const getPutPoolsApiConfigsPoolPutUrl = () => { return `/api/configs/pool` } +/** + * Put Pool configurations + * @summary Put Pools + */ export const putPoolsApiConfigsPoolPut = async (putPoolsRequest: PutPoolsRequest, options?: RequestInit): Promise => { return customFetch(getPutPoolsApiConfigsPoolPutUrl(), @@ -3175,8 +3300,7 @@ export const putPoolsApiConfigsPoolPut = async (putPoolsRequest: PutPoolsRequest ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putPoolsRequest,) + body: JSON.stringify(putPoolsRequest) } );} @@ -3228,13 +3352,6 @@ export const usePutPoolsApiConfigsPoolPut = { const normalizedParams = new URLSearchParams(); @@ -3242,7 +3359,7 @@ export const getReadPoolApiConfigsPoolNameGetUrl = (name: string, Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -3251,6 +3368,13 @@ export const getReadPoolApiConfigsPoolNameGetUrl = (name: string, return stringifiedParams.length > 0 ? `/api/configs/pool/${name}?${stringifiedParams}` : `/api/configs/pool/${name}` } +/** + * Read Pool configuration + * + * Return type Any to prevent unwanted artifacts between Pool and PoolEditable outputs + * Should return Pool or PoolEditable objects + * @summary Read Pool + */ export const readPoolApiConfigsPoolNameGet = async (name: string, params?: ReadPoolApiConfigsPoolNameGetParams, options?: RequestInit): Promise => { @@ -3291,7 +3415,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type ReadPoolApiConfigsPoolNameGetQueryResult = NonNullable>> @@ -3358,10 +3482,8 @@ export const invalidateReadPoolApiConfigsPoolNameGet = async ( -/** - * Put Pool configurations - * @summary Put Pool - */ + + export const getPutPoolApiConfigsPoolNamePutUrl = (name: string,) => { @@ -3370,6 +3492,10 @@ export const getPutPoolApiConfigsPoolNamePutUrl = (name: string,) => { return `/api/configs/pool/${name}` } +/** + * Put Pool configurations + * @summary Put Pool + */ export const putPoolApiConfigsPoolNamePut = async (name: string, putPoolRequest: PutPoolRequest, options?: RequestInit): Promise => { @@ -3378,8 +3504,7 @@ export const putPoolApiConfigsPoolNamePut = async (name: string, ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putPoolRequest,) + body: JSON.stringify(putPoolRequest) } );} @@ -3431,10 +3556,6 @@ export const usePutPoolApiConfigsPoolNamePut = { @@ -3443,6 +3564,10 @@ export const getPatchPoolApiConfigsPoolNamePatchUrl = (name: string,) => { return `/api/configs/pool/${name}` } +/** + * Patch Pool configurations + * @summary Patch Pool + */ export const patchPoolApiConfigsPoolNamePatch = async (name: string, patchPoolRequest: PatchPoolRequest, options?: RequestInit): Promise => { @@ -3451,8 +3576,7 @@ export const patchPoolApiConfigsPoolNamePatch = async (name: string, ...options, method: 'PATCH', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - patchPoolRequest,) + body: JSON.stringify(patchPoolRequest) } );} @@ -3504,10 +3628,6 @@ export const usePatchPoolApiConfigsPoolNamePatch = { @@ -3516,6 +3636,10 @@ export const getDeletePoolApiConfigsPoolNameDeleteUrl = (name: string,) => { return `/api/configs/pool/${name}` } +/** + * Delete Pool configurations + * @summary Delete Pool + */ export const deletePoolApiConfigsPoolNameDelete = async (name: string, configsRequest: ConfigsRequest, options?: RequestInit): Promise => { @@ -3524,8 +3648,7 @@ export const deletePoolApiConfigsPoolNameDelete = async (name: string, ...options, method: 'DELETE', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - configsRequest,) + body: JSON.stringify(configsRequest) } );} @@ -3577,10 +3700,6 @@ export const useDeletePoolApiConfigsPoolNameDelete = { @@ -3589,6 +3708,10 @@ export const getRenamePoolApiConfigsPoolNameRenamePutUrl = (name: string,) => { return `/api/configs/pool/${name}/rename` } +/** + * Rename Pool + * @summary Rename Pool + */ export const renamePoolApiConfigsPoolNameRenamePut = async (name: string, renamePoolRequest: RenamePoolRequest, options?: RequestInit): Promise => { @@ -3597,8 +3720,7 @@ export const renamePoolApiConfigsPoolNameRenamePut = async (name: string, ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - renamePoolRequest,) + body: JSON.stringify(renamePoolRequest) } );} @@ -3650,10 +3772,6 @@ export const useRenamePoolApiConfigsPoolNameRenamePut = { const normalizedParams = new URLSearchParams(); @@ -3661,7 +3779,7 @@ export const getListPlatformsInPoolApiConfigsPoolNamePlatformGetUrl = (name: str Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -3670,6 +3788,10 @@ export const getListPlatformsInPoolApiConfigsPoolNamePlatformGetUrl = (name: str return stringifiedParams.length > 0 ? `/api/configs/pool/${name}/platform?${stringifiedParams}` : `/api/configs/pool/${name}/platform` } +/** + * List all Platforms + * @summary List Platforms In Pool + */ export const listPlatformsInPoolApiConfigsPoolNamePlatformGet = async (name: string, params?: ListPlatformsInPoolApiConfigsPoolNamePlatformGetParams, options?: RequestInit): Promise => { @@ -3710,7 +3832,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type ListPlatformsInPoolApiConfigsPoolNamePlatformGetQueryResult = NonNullable>> @@ -3777,10 +3899,8 @@ export const invalidateListPlatformsInPoolApiConfigsPoolNamePlatformGet = async -/** - * Read Platform - * @summary Read Platform In Pool - */ + + export const getReadPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGetUrl = (name: string, platformName: string, params?: ReadPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGetParams,) => { @@ -3789,7 +3909,7 @@ export const getReadPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGetUrl = Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -3798,6 +3918,10 @@ export const getReadPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGetUrl = return stringifiedParams.length > 0 ? `/api/configs/pool/${name}/platform/${platformName}?${stringifiedParams}` : `/api/configs/pool/${name}/platform/${platformName}` } +/** + * Read Platform + * @summary Read Platform In Pool + */ export const readPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGet = async (name: string, platformName: string, params?: ReadPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGetParams, options?: RequestInit): Promise => { @@ -3841,7 +3965,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name && platformName), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined && platformName !== null && platformName !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type ReadPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGetQueryResult = NonNullable>> @@ -3913,10 +4037,8 @@ export const invalidateReadPlatformInPoolApiConfigsPoolNamePlatformPlatformNameG -/** - * Put Platform configurations - * @summary Put Platform In Pool - */ + + export const getPutPlatformInPoolApiConfigsPoolNamePlatformPlatformNamePutUrl = (name: string, platformName: string,) => { @@ -3926,6 +4048,10 @@ export const getPutPlatformInPoolApiConfigsPoolNamePlatformPlatformNamePutUrl = return `/api/configs/pool/${name}/platform/${platformName}` } +/** + * Put Platform configurations + * @summary Put Platform In Pool + */ export const putPlatformInPoolApiConfigsPoolNamePlatformPlatformNamePut = async (name: string, platformName: string, putPoolPlatformRequest: PutPoolPlatformRequest, options?: RequestInit): Promise => { @@ -3935,8 +4061,7 @@ export const putPlatformInPoolApiConfigsPoolNamePlatformPlatformNamePut = async ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putPoolPlatformRequest,) + body: JSON.stringify(putPoolPlatformRequest) } );} @@ -3988,10 +4113,6 @@ export const usePutPlatformInPoolApiConfigsPoolNamePlatformPlatformNamePut = { @@ -4001,6 +4122,10 @@ export const getRenamePlatformInPoolApiConfigsPoolNamePlatformPlatformNameRename return `/api/configs/pool/${name}/platform/${platformName}/rename` } +/** + * Rename Platform + * @summary Rename Platform In Pool + */ export const renamePlatformInPoolApiConfigsPoolNamePlatformPlatformNameRenamePut = async (name: string, platformName: string, renamePoolPlatformRequest: RenamePoolPlatformRequest, options?: RequestInit): Promise => { @@ -4010,8 +4135,7 @@ export const renamePlatformInPoolApiConfigsPoolNamePlatformPlatformNameRenamePut ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - renamePoolPlatformRequest,) + body: JSON.stringify(renamePoolPlatformRequest) } );} @@ -4063,10 +4187,6 @@ export const useRenamePlatformInPoolApiConfigsPoolNamePlatformPlatformNameRename return useMutation(getRenamePlatformInPoolApiConfigsPoolNamePlatformPlatformNameRenamePutMutationOptions(options), queryClient); } -/** - * List all Pod Template configurations - * @summary List Pod Templates - */ export const getListPodTemplatesApiConfigsPodTemplateGetUrl = () => { @@ -4075,9 +4195,13 @@ export const getListPodTemplatesApiConfigsPodTemplateGetUrl = () => { return `/api/configs/pod_template` } -export const listPodTemplatesApiConfigsPodTemplateGet = async ( options?: RequestInit): Promise => { - - return customFetch(getListPodTemplatesApiConfigsPodTemplateGetUrl(), +/** + * List all Pod Template configurations + * @summary List Pod Templates + */ +export const listPodTemplatesApiConfigsPodTemplateGet = async ( options?: RequestInit): Promise => { + + return customFetch(getListPodTemplatesApiConfigsPodTemplateGetUrl(), { ...options, method: 'GET' @@ -4174,10 +4298,8 @@ export const invalidateListPodTemplatesApiConfigsPodTemplateGet = async ( -/** - * Set Dict of Pod Templates configurations - * @summary Put Pod Templates - */ + + export const getPutPodTemplatesApiConfigsPodTemplatePutUrl = () => { @@ -4186,6 +4308,10 @@ export const getPutPodTemplatesApiConfigsPodTemplatePutUrl = () => { return `/api/configs/pod_template` } +/** + * Set Dict of Pod Templates configurations + * @summary Put Pod Templates + */ export const putPodTemplatesApiConfigsPodTemplatePut = async (putPodTemplatesRequest: PutPodTemplatesRequest, options?: RequestInit): Promise => { return customFetch(getPutPodTemplatesApiConfigsPodTemplatePutUrl(), @@ -4193,8 +4319,7 @@ export const putPodTemplatesApiConfigsPodTemplatePut = async (putPodTemplatesReq ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putPodTemplatesRequest,) + body: JSON.stringify(putPodTemplatesRequest) } );} @@ -4246,10 +4371,6 @@ export const usePutPodTemplatesApiConfigsPodTemplatePut = { @@ -4258,6 +4379,10 @@ export const getReadPodTemplateApiConfigsPodTemplateNameGetUrl = (name: string,) return `/api/configs/pod_template/${name}` } +/** + * Read Pod Template configurations + * @summary Read Pod Template + */ export const readPodTemplateApiConfigsPodTemplateNameGet = async (name: string, options?: RequestInit): Promise => { return customFetch(getReadPodTemplateApiConfigsPodTemplateNameGetUrl(name), @@ -4295,7 +4420,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type ReadPodTemplateApiConfigsPodTemplateNameGetQueryResult = NonNullable>> @@ -4357,10 +4482,8 @@ export const invalidateReadPodTemplateApiConfigsPodTemplateNameGet = async ( -/** - * Put Pod Template configurations - * @summary Put Pod Template - */ + + export const getPutPodTemplateApiConfigsPodTemplateNamePutUrl = (name: string,) => { @@ -4369,6 +4492,10 @@ export const getPutPodTemplateApiConfigsPodTemplateNamePutUrl = (name: string,) return `/api/configs/pod_template/${name}` } +/** + * Put Pod Template configurations + * @summary Put Pod Template + */ export const putPodTemplateApiConfigsPodTemplateNamePut = async (name: string, putPodTemplateRequest: PutPodTemplateRequest, options?: RequestInit): Promise => { @@ -4377,8 +4504,7 @@ export const putPodTemplateApiConfigsPodTemplateNamePut = async (name: string, ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putPodTemplateRequest,) + body: JSON.stringify(putPodTemplateRequest) } );} @@ -4430,10 +4556,6 @@ export const usePutPodTemplateApiConfigsPodTemplateNamePut = { @@ -4442,6 +4564,10 @@ export const getDeletePodTemplateApiConfigsPodTemplateNameDeleteUrl = (name: str return `/api/configs/pod_template/${name}` } +/** + * Delete Pod Template configurations + * @summary Delete Pod Template + */ export const deletePodTemplateApiConfigsPodTemplateNameDelete = async (name: string, configsRequest: ConfigsRequest, options?: RequestInit): Promise => { @@ -4450,8 +4576,7 @@ export const deletePodTemplateApiConfigsPodTemplateNameDelete = async (name: str ...options, method: 'DELETE', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - configsRequest,) + body: JSON.stringify(configsRequest) } );} @@ -4503,10 +4628,6 @@ export const useDeletePodTemplateApiConfigsPodTemplateNameDelete = { @@ -4515,6 +4636,10 @@ export const getListGroupTemplatesApiConfigsGroupTemplateGetUrl = () => { return `/api/configs/group_template` } +/** + * List all Group Template configurations + * @summary List Group Templates + */ export const listGroupTemplatesApiConfigsGroupTemplateGet = async ( options?: RequestInit): Promise => { return customFetch(getListGroupTemplatesApiConfigsGroupTemplateGetUrl(), @@ -4614,10 +4739,8 @@ export const invalidateListGroupTemplatesApiConfigsGroupTemplateGet = async ( -/** - * Set Dict of Group Templates configurations - * @summary Put Group Templates - */ + + export const getPutGroupTemplatesApiConfigsGroupTemplatePutUrl = () => { @@ -4626,6 +4749,10 @@ export const getPutGroupTemplatesApiConfigsGroupTemplatePutUrl = () => { return `/api/configs/group_template` } +/** + * Set Dict of Group Templates configurations + * @summary Put Group Templates + */ export const putGroupTemplatesApiConfigsGroupTemplatePut = async (putGroupTemplatesRequest: PutGroupTemplatesRequest, options?: RequestInit): Promise => { return customFetch(getPutGroupTemplatesApiConfigsGroupTemplatePutUrl(), @@ -4633,8 +4760,7 @@ export const putGroupTemplatesApiConfigsGroupTemplatePut = async (putGroupTempla ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putGroupTemplatesRequest,) + body: JSON.stringify(putGroupTemplatesRequest) } );} @@ -4686,10 +4812,6 @@ export const usePutGroupTemplatesApiConfigsGroupTemplatePut = { @@ -4698,6 +4820,10 @@ export const getReadGroupTemplateApiConfigsGroupTemplateNameGetUrl = (name: stri return `/api/configs/group_template/${name}` } +/** + * Read Group Template configurations + * @summary Read Group Template + */ export const readGroupTemplateApiConfigsGroupTemplateNameGet = async (name: string, options?: RequestInit): Promise => { return customFetch(getReadGroupTemplateApiConfigsGroupTemplateNameGetUrl(name), @@ -4735,7 +4861,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type ReadGroupTemplateApiConfigsGroupTemplateNameGetQueryResult = NonNullable>> @@ -4797,10 +4923,8 @@ export const invalidateReadGroupTemplateApiConfigsGroupTemplateNameGet = async ( -/** - * Put Group Template configurations - * @summary Put Group Template - */ + + export const getPutGroupTemplateApiConfigsGroupTemplateNamePutUrl = (name: string,) => { @@ -4809,6 +4933,10 @@ export const getPutGroupTemplateApiConfigsGroupTemplateNamePutUrl = (name: strin return `/api/configs/group_template/${name}` } +/** + * Put Group Template configurations + * @summary Put Group Template + */ export const putGroupTemplateApiConfigsGroupTemplateNamePut = async (name: string, putGroupTemplateRequest: PutGroupTemplateRequest, options?: RequestInit): Promise => { @@ -4817,8 +4945,7 @@ export const putGroupTemplateApiConfigsGroupTemplateNamePut = async (name: strin ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putGroupTemplateRequest,) + body: JSON.stringify(putGroupTemplateRequest) } );} @@ -4870,10 +4997,6 @@ export const usePutGroupTemplateApiConfigsGroupTemplateNamePut = { @@ -4882,6 +5005,10 @@ export const getDeleteGroupTemplateApiConfigsGroupTemplateNameDeleteUrl = (name: return `/api/configs/group_template/${name}` } +/** + * Delete Group Template configurations + * @summary Delete Group Template + */ export const deleteGroupTemplateApiConfigsGroupTemplateNameDelete = async (name: string, configsRequest: ConfigsRequest, options?: RequestInit): Promise => { @@ -4890,8 +5017,7 @@ export const deleteGroupTemplateApiConfigsGroupTemplateNameDelete = async (name: ...options, method: 'DELETE', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - configsRequest,) + body: JSON.stringify(configsRequest) } );} @@ -4943,10 +5069,6 @@ export const useDeleteGroupTemplateApiConfigsGroupTemplateNameDelete = { @@ -4955,6 +5077,10 @@ export const getListResourceValidationsApiConfigsResourceValidationGetUrl = () = return `/api/configs/resource_validation` } +/** + * List all Resource Validation configurations + * @summary List Resource Validations + */ export const listResourceValidationsApiConfigsResourceValidationGet = async ( options?: RequestInit): Promise => { return customFetch(getListResourceValidationsApiConfigsResourceValidationGetUrl(), @@ -5054,10 +5180,8 @@ export const invalidateListResourceValidationsApiConfigsResourceValidationGet = -/** - * Put Resource Validation configurations - * @summary Put Resource Validations - */ + + export const getPutResourceValidationsApiConfigsResourceValidationPutUrl = () => { @@ -5066,6 +5190,10 @@ export const getPutResourceValidationsApiConfigsResourceValidationPutUrl = () => return `/api/configs/resource_validation` } +/** + * Put Resource Validation configurations + * @summary Put Resource Validations + */ export const putResourceValidationsApiConfigsResourceValidationPut = async (putResourceValidationsRequest: PutResourceValidationsRequest, options?: RequestInit): Promise => { return customFetch(getPutResourceValidationsApiConfigsResourceValidationPutUrl(), @@ -5073,8 +5201,7 @@ export const putResourceValidationsApiConfigsResourceValidationPut = async (putR ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putResourceValidationsRequest,) + body: JSON.stringify(putResourceValidationsRequest) } );} @@ -5126,10 +5253,6 @@ export const usePutResourceValidationsApiConfigsResourceValidationPut = { @@ -5138,6 +5261,10 @@ export const getReadResourceValidationApiConfigsResourceValidationNameGetUrl = ( return `/api/configs/resource_validation/${name}` } +/** + * Read Resource Validation configurations + * @summary Read Resource Validation + */ export const readResourceValidationApiConfigsResourceValidationNameGet = async (name: string, options?: RequestInit): Promise => { return customFetch(getReadResourceValidationApiConfigsResourceValidationNameGetUrl(name), @@ -5175,7 +5302,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type ReadResourceValidationApiConfigsResourceValidationNameGetQueryResult = NonNullable>> @@ -5237,10 +5364,8 @@ export const invalidateReadResourceValidationApiConfigsResourceValidationNameGet -/** - * Put Resource Validation configurations - * @summary Put Resource Validation - */ + + export const getPutResourceValidationApiConfigsResourceValidationNamePutUrl = (name: string,) => { @@ -5249,6 +5374,10 @@ export const getPutResourceValidationApiConfigsResourceValidationNamePutUrl = (n return `/api/configs/resource_validation/${name}` } +/** + * Put Resource Validation configurations + * @summary Put Resource Validation + */ export const putResourceValidationApiConfigsResourceValidationNamePut = async (name: string, putResourceValidationRequest: PutResourceValidationRequest, options?: RequestInit): Promise => { @@ -5257,8 +5386,7 @@ export const putResourceValidationApiConfigsResourceValidationNamePut = async (n ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putResourceValidationRequest,) + body: JSON.stringify(putResourceValidationRequest) } );} @@ -5310,10 +5438,6 @@ export const usePutResourceValidationApiConfigsResourceValidationNamePut = { @@ -5322,6 +5446,10 @@ export const getDeleteResourceValidationApiConfigsResourceValidationNameDeleteUr return `/api/configs/resource_validation/${name}` } +/** + * Delete Resource Validation configurations + * @summary Delete Resource Validation + */ export const deleteResourceValidationApiConfigsResourceValidationNameDelete = async (name: string, configsRequest: ConfigsRequest, options?: RequestInit): Promise => { @@ -5330,8 +5458,7 @@ export const deleteResourceValidationApiConfigsResourceValidationNameDelete = as ...options, method: 'DELETE', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - configsRequest,) + body: JSON.stringify(configsRequest) } );} @@ -5383,10 +5510,6 @@ export const useDeleteResourceValidationApiConfigsResourceValidationNameDelete = return useMutation(getDeleteResourceValidationApiConfigsResourceValidationNameDeleteMutationOptions(options), queryClient); } -/** - * List all Roles - * @summary List Roles - */ export const getListRolesApiConfigsRoleGetUrl = () => { @@ -5395,6 +5518,10 @@ export const getListRolesApiConfigsRoleGetUrl = () => { return `/api/configs/role` } +/** + * List all Roles + * @summary List Roles + */ export const listRolesApiConfigsRoleGet = async ( options?: RequestInit): Promise => { return customFetch(getListRolesApiConfigsRoleGetUrl(), @@ -5494,10 +5621,8 @@ export const invalidateListRolesApiConfigsRoleGet = async ( -/** - * Put Roles - * @summary Put Roles - */ + + export const getPutRolesApiConfigsRolePutUrl = () => { @@ -5506,6 +5631,10 @@ export const getPutRolesApiConfigsRolePutUrl = () => { return `/api/configs/role` } +/** + * Put Roles + * @summary Put Roles + */ export const putRolesApiConfigsRolePut = async (putRolesRequest: PutRolesRequest, options?: RequestInit): Promise => { return customFetch(getPutRolesApiConfigsRolePutUrl(), @@ -5513,8 +5642,7 @@ export const putRolesApiConfigsRolePut = async (putRolesRequest: PutRolesRequest ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putRolesRequest,) + body: JSON.stringify(putRolesRequest) } );} @@ -5566,10 +5694,6 @@ export const usePutRolesApiConfigsRolePut = { @@ -5578,6 +5702,10 @@ export const getReadRoleApiConfigsRoleNameGetUrl = (name: string,) => { return `/api/configs/role/${name}` } +/** + * Read Role + * @summary Read Role + */ export const readRoleApiConfigsRoleNameGet = async (name: string, options?: RequestInit): Promise => { return customFetch(getReadRoleApiConfigsRoleNameGetUrl(name), @@ -5615,7 +5743,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type ReadRoleApiConfigsRoleNameGetQueryResult = NonNullable>> @@ -5677,10 +5805,8 @@ export const invalidateReadRoleApiConfigsRoleNameGet = async ( -/** - * Patch Role configurations - * @summary Put Role - */ + + export const getPutRoleApiConfigsRoleNamePutUrl = (name: string,) => { @@ -5689,6 +5815,10 @@ export const getPutRoleApiConfigsRoleNamePutUrl = (name: string,) => { return `/api/configs/role/${name}` } +/** + * Patch Role configurations + * @summary Put Role + */ export const putRoleApiConfigsRoleNamePut = async (name: string, putRoleRequest: PutRoleRequest, options?: RequestInit): Promise => { @@ -5697,8 +5827,7 @@ export const putRoleApiConfigsRoleNamePut = async (name: string, ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putRoleRequest,) + body: JSON.stringify(putRoleRequest) } );} @@ -5750,10 +5879,6 @@ export const usePutRoleApiConfigsRoleNamePut = { @@ -5762,6 +5887,10 @@ export const getDeleteRoleApiConfigsRoleNameDeleteUrl = (name: string,) => { return `/api/configs/role/${name}` } +/** + * Delete Role + * @summary Delete Role + */ export const deleteRoleApiConfigsRoleNameDelete = async (name: string, configsRequest: ConfigsRequest, options?: RequestInit): Promise => { @@ -5770,8 +5899,7 @@ export const deleteRoleApiConfigsRoleNameDelete = async (name: string, ...options, method: 'DELETE', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - configsRequest,) + body: JSON.stringify(configsRequest) } );} @@ -5823,10 +5951,6 @@ export const useDeleteRoleApiConfigsRoleNameDelete = { @@ -5835,6 +5959,10 @@ export const getListBackendTestsApiConfigsBackendTestGetUrl = () => { return `/api/configs/backend_test` } +/** + * List all backend test configurations + * @summary List Backend Tests + */ export const listBackendTestsApiConfigsBackendTestGet = async ( options?: RequestInit): Promise => { return customFetch(getListBackendTestsApiConfigsBackendTestGetUrl(), @@ -5934,10 +6062,8 @@ export const invalidateListBackendTestsApiConfigsBackendTestGet = async ( -/** - * Put backend test configurations - * @summary Put Backend Tests - */ + + export const getPutBackendTestsApiConfigsBackendTestPutUrl = () => { @@ -5946,6 +6072,10 @@ export const getPutBackendTestsApiConfigsBackendTestPutUrl = () => { return `/api/configs/backend_test` } +/** + * Put backend test configurations + * @summary Put Backend Tests + */ export const putBackendTestsApiConfigsBackendTestPut = async (putBackendTestsRequest: PutBackendTestsRequest, options?: RequestInit): Promise => { return customFetch(getPutBackendTestsApiConfigsBackendTestPutUrl(), @@ -5953,8 +6083,7 @@ export const putBackendTestsApiConfigsBackendTestPut = async (putBackendTestsReq ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putBackendTestsRequest,) + body: JSON.stringify(putBackendTestsRequest) } );} @@ -6006,10 +6135,6 @@ export const usePutBackendTestsApiConfigsBackendTestPut = { @@ -6018,6 +6143,10 @@ export const getReadBackendTestApiConfigsBackendTestNameGetUrl = (name: string,) return `/api/configs/backend_test/${name}` } +/** + * Read backend test configuration + * @summary Read Backend Test + */ export const readBackendTestApiConfigsBackendTestNameGet = async (name: string, options?: RequestInit): Promise => { return customFetch(getReadBackendTestApiConfigsBackendTestNameGetUrl(name), @@ -6055,7 +6184,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type ReadBackendTestApiConfigsBackendTestNameGetQueryResult = NonNullable>> @@ -6117,10 +6246,8 @@ export const invalidateReadBackendTestApiConfigsBackendTestNameGet = async ( -/** - * Put backend test configuration - * @summary Put Backend Test - */ + + export const getPutBackendTestApiConfigsBackendTestNamePutUrl = (name: string,) => { @@ -6129,6 +6256,10 @@ export const getPutBackendTestApiConfigsBackendTestNamePutUrl = (name: string,) return `/api/configs/backend_test/${name}` } +/** + * Put backend test configuration + * @summary Put Backend Test + */ export const putBackendTestApiConfigsBackendTestNamePut = async (name: string, putBackendTestRequest: PutBackendTestRequest, options?: RequestInit): Promise => { @@ -6137,8 +6268,7 @@ export const putBackendTestApiConfigsBackendTestNamePut = async (name: string, ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - putBackendTestRequest,) + body: JSON.stringify(putBackendTestRequest) } );} @@ -6190,10 +6320,6 @@ export const usePutBackendTestApiConfigsBackendTestNamePut = { @@ -6202,6 +6328,10 @@ export const getPatchBackendTestApiConfigsBackendTestNamePatchUrl = (name: strin return `/api/configs/backend_test/${name}` } +/** + * Patch backend test configuration + * @summary Patch Backend Test + */ export const patchBackendTestApiConfigsBackendTestNamePatch = async (name: string, patchBackendTestRequest: PatchBackendTestRequest, options?: RequestInit): Promise => { @@ -6210,8 +6340,7 @@ export const patchBackendTestApiConfigsBackendTestNamePatch = async (name: strin ...options, method: 'PATCH', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - patchBackendTestRequest,) + body: JSON.stringify(patchBackendTestRequest) } );} @@ -6263,10 +6392,6 @@ export const usePatchBackendTestApiConfigsBackendTestNamePatch = { @@ -6275,6 +6400,10 @@ export const getDeleteBackendTestApiConfigsBackendTestNameDeleteUrl = (name: str return `/api/configs/backend_test/${name}` } +/** + * Delete test configuration + * @summary Delete Backend Test + */ export const deleteBackendTestApiConfigsBackendTestNameDelete = async (name: string, configsRequest: ConfigsRequest, options?: RequestInit): Promise => { @@ -6283,8 +6412,7 @@ export const deleteBackendTestApiConfigsBackendTestNameDelete = async (name: str ...options, method: 'DELETE', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - configsRequest,) + body: JSON.stringify(configsRequest) } );} @@ -6336,10 +6464,6 @@ export const useDeleteBackendTestApiConfigsBackendTestNameDelete = { const normalizedParams = new URLSearchParams(); @@ -6348,13 +6472,13 @@ export const getGetConfigsHistoryApiConfigsHistoryGetUrl = (params?: GetConfigsH if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -6363,6 +6487,10 @@ export const getGetConfigsHistoryApiConfigsHistoryGetUrl = (params?: GetConfigsH return stringifiedParams.length > 0 ? `/api/configs/history?${stringifiedParams}` : `/api/configs/history` } +/** + * List history of all configs + * @summary Get Configs History + */ export const getConfigsHistoryApiConfigsHistoryGet = async (params?: GetConfigsHistoryApiConfigsHistoryGetParams, options?: RequestInit): Promise => { return customFetch(getGetConfigsHistoryApiConfigsHistoryGetUrl(params), @@ -6462,10 +6590,8 @@ export const invalidateGetConfigsHistoryApiConfigsHistoryGet = async ( -/** - * Roll back a config to a particular revision. - * @summary Rollback Config - */ + + export const getRollbackConfigApiConfigsHistoryRollbackPostUrl = () => { @@ -6474,6 +6600,10 @@ export const getRollbackConfigApiConfigsHistoryRollbackPostUrl = () => { return `/api/configs/history/rollback` } +/** + * Roll back a config to a particular revision. + * @summary Rollback Config + */ export const rollbackConfigApiConfigsHistoryRollbackPost = async (rollbackConfigRequest: RollbackConfigRequest, options?: RequestInit): Promise => { return customFetch(getRollbackConfigApiConfigsHistoryRollbackPostUrl(), @@ -6481,8 +6611,7 @@ export const rollbackConfigApiConfigsHistoryRollbackPost = async (rollbackConfig ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - rollbackConfigRequest,) + body: JSON.stringify(rollbackConfigRequest) } );} @@ -6534,18 +6663,6 @@ export const useRollbackConfigApiConfigsHistoryRollbackPost = { @@ -6555,6 +6672,18 @@ export const getDeleteConfigHistoryRevisionApiConfigsHistoryConfigTypeRevisionRe return `/api/configs/history/${configType}/revision/${revision}` } +/** + * Delete a specific config history revision. This performs a soft delete of the revision. + * + * Args: + * config_type: Type of config to delete + * revision: Revision number to delete (must be greater than 0) + * username: Username of the person performing the delete + * + * Raises: + * OSMOUserError: If the revision doesn't exist or is the current revision + * @summary Delete Config History Revision + */ export const deleteConfigHistoryRevisionApiConfigsHistoryConfigTypeRevisionRevisionDelete = async (configType: string, revision: number, options?: RequestInit): Promise => { @@ -6615,19 +6744,6 @@ export const useDeleteConfigHistoryRevisionApiConfigsHistoryConfigTypeRevisionRe return useMutation(getDeleteConfigHistoryRevisionApiConfigsHistoryConfigTypeRevisionRevisionDeleteMutationOptions(options), queryClient); } -/** - * Update tags for a specific config history revision. - -Args: - config_type: Type of config to update - revision: Revision number to update (must be greater than 0) - request: Request containing tags to add and delete - username: Username of the person performing the update - -Raises: - OSMOUserError: If the revision doesn't exist or is invalid - * @summary Update Config History Tags - */ export const getUpdateConfigHistoryTagsApiConfigsHistoryConfigTypeRevisionRevisionTagsPostUrl = (configType: string, revision: number,) => { @@ -6637,6 +6753,19 @@ export const getUpdateConfigHistoryTagsApiConfigsHistoryConfigTypeRevisionRevisi return `/api/configs/history/${configType}/revision/${revision}/tags` } +/** + * Update tags for a specific config history revision. + * + * Args: + * config_type: Type of config to update + * revision: Revision number to update (must be greater than 0) + * request: Request containing tags to add and delete + * username: Username of the person performing the update + * + * Raises: + * OSMOUserError: If the revision doesn't exist or is invalid + * @summary Update Config History Tags + */ export const updateConfigHistoryTagsApiConfigsHistoryConfigTypeRevisionRevisionTagsPost = async (configType: string, revision: number, updateConfigTagsRequest: UpdateConfigTagsRequest, options?: RequestInit): Promise => { @@ -6646,8 +6775,7 @@ export const updateConfigHistoryTagsApiConfigsHistoryConfigTypeRevisionRevisionT ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - updateConfigTagsRequest,) + body: JSON.stringify(updateConfigTagsRequest) } );} @@ -6699,28 +6827,13 @@ export const useUpdateConfigHistoryTagsApiConfigsHistoryConfigTypeRevisionRevisi return useMutation(getUpdateConfigHistoryTagsApiConfigsHistoryConfigTypeRevisionRevisionTagsPostMutationOptions(options), queryClient); } -/** - * Returns two config revisions, similar to -GET /api/configs/history/{config_type}/revision/{revision}, but with obfuscated secret strings -that say if a secret string is changed. Intended for use with the `diff` command. - -Args: - request: Request containing config type and revisions to compare - -Returns: - ConfigDiffResponse containing the two revisions - -Raises: - OSMOUserError: If either revision doesn't exist or is invalid - * @summary Get Config Diff - */ export const getGetConfigDiffApiConfigsDiffGetUrl = (params: GetConfigDiffApiConfigsDiffGetParams,) => { const normalizedParams = new URLSearchParams(); Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -6729,6 +6842,21 @@ export const getGetConfigDiffApiConfigsDiffGetUrl = (params: GetConfigDiffApiCon return stringifiedParams.length > 0 ? `/api/configs/diff?${stringifiedParams}` : `/api/configs/diff` } +/** + * Returns two config revisions, similar to + * GET /api/configs/history/{config_type}/revision/{revision}, but with obfuscated secret strings + * that say if a secret string is changed. Intended for use with the `diff` command. + * + * Args: + * request: Request containing config type and revisions to compare + * + * Returns: + * ConfigDiffResponse containing the two revisions + * + * Raises: + * OSMOUserError: If either revision doesn't exist or is invalid + * @summary Get Config Diff + */ export const getConfigDiffApiConfigsDiffGet = async (params: GetConfigDiffApiConfigsDiffGetParams, options?: RequestInit): Promise => { return customFetch(getGetConfigDiffApiConfigsDiffGetUrl(params), @@ -6828,19 +6956,15 @@ export const invalidateGetConfigDiffApiConfigsDiffGet = async ( -/** - * API to fetch for a new access token using a refresh token. -Deprecated: Use POST /api/auth/jwt/refresh_token instead. - * @summary Get New Jwt Token - */ + export const getGetNewJwtTokenApiAuthJwtRefreshTokenGetUrl = (params: GetNewJwtTokenApiAuthJwtRefreshTokenGetParams,) => { const normalizedParams = new URLSearchParams(); Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -6849,6 +6973,12 @@ export const getGetNewJwtTokenApiAuthJwtRefreshTokenGetUrl = (params: GetNewJwtT return stringifiedParams.length > 0 ? `/api/auth/jwt/refresh_token?${stringifiedParams}` : `/api/auth/jwt/refresh_token` } +/** + * API to fetch for a new access token using a refresh token. + * + * Deprecated: Use POST /api/auth/jwt/refresh_token instead. + * @summary Get New Jwt Token + */ export const getNewJwtTokenApiAuthJwtRefreshTokenGet = async (params: GetNewJwtTokenApiAuthJwtRefreshTokenGetParams, options?: RequestInit): Promise => { return customFetch(getGetNewJwtTokenApiAuthJwtRefreshTokenGetUrl(params), @@ -6948,17 +7078,15 @@ export const invalidateGetNewJwtTokenApiAuthJwtRefreshTokenGet = async ( -/** - * API to fetch for a new access token using a refresh token. - * @summary Post New Jwt Token - */ + + export const getPostNewJwtTokenApiAuthJwtRefreshTokenPostUrl = (params: PostNewJwtTokenApiAuthJwtRefreshTokenPostParams,) => { const normalizedParams = new URLSearchParams(); Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -6967,6 +7095,10 @@ export const getPostNewJwtTokenApiAuthJwtRefreshTokenPostUrl = (params: PostNewJ return stringifiedParams.length > 0 ? `/api/auth/jwt/refresh_token?${stringifiedParams}` : `/api/auth/jwt/refresh_token` } +/** + * API to fetch for a new access token using a refresh token. + * @summary Post New Jwt Token + */ export const postNewJwtTokenApiAuthJwtRefreshTokenPost = async (tokenRequest: TokenRequest, params: PostNewJwtTokenApiAuthJwtRefreshTokenPostParams, options?: RequestInit): Promise => { @@ -6975,8 +7107,7 @@ export const postNewJwtTokenApiAuthJwtRefreshTokenPost = async (tokenRequest: To ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - tokenRequest,) + body: JSON.stringify(tokenRequest) } );} @@ -7028,19 +7159,13 @@ export const usePostNewJwtTokenApiAuthJwtRefreshTokenPost = { const normalizedParams = new URLSearchParams(); Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -7049,6 +7174,12 @@ export const getGetJwtTokenFromAccessTokenApiAuthJwtAccessTokenGetUrl = (params: return stringifiedParams.length > 0 ? `/api/auth/jwt/access_token?${stringifiedParams}` : `/api/auth/jwt/access_token` } +/** + * API to create a new jwt token from an access token. + * + * Deprecated: Use POST /api/auth/jwt/access_token instead. + * @summary Get Jwt Token From Access Token + */ export const getJwtTokenFromAccessTokenApiAuthJwtAccessTokenGet = async (params: GetJwtTokenFromAccessTokenApiAuthJwtAccessTokenGetParams, options?: RequestInit): Promise => { return customFetch(getGetJwtTokenFromAccessTokenApiAuthJwtAccessTokenGetUrl(params), @@ -7148,10 +7279,8 @@ export const invalidateGetJwtTokenFromAccessTokenApiAuthJwtAccessTokenGet = asyn -/** - * API to create a new jwt token from an access token. - * @summary Post Jwt Token From Access Token - */ + + export const getPostJwtTokenFromAccessTokenApiAuthJwtAccessTokenPostUrl = () => { @@ -7160,6 +7289,10 @@ export const getPostJwtTokenFromAccessTokenApiAuthJwtAccessTokenPostUrl = () => return `/api/auth/jwt/access_token` } +/** + * API to create a new jwt token from an access token. + * @summary Post Jwt Token From Access Token + */ export const postJwtTokenFromAccessTokenApiAuthJwtAccessTokenPost = async (tokenRequest: TokenRequest, options?: RequestInit): Promise => { return customFetch(getPostJwtTokenFromAccessTokenApiAuthJwtAccessTokenPostUrl(), @@ -7167,8 +7300,7 @@ export const postJwtTokenFromAccessTokenApiAuthJwtAccessTokenPost = async (token ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - tokenRequest,) + body: JSON.stringify(tokenRequest) } );} @@ -7220,15 +7352,6 @@ export const usePostJwtTokenFromAccessTokenApiAuthJwtAccessTokenPost = { const normalizedParams = new URLSearchParams(); @@ -7238,13 +7361,13 @@ export const getCreateAccessTokenApiAuthAccessTokenTokenNamePostUrl = (tokenName if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -7253,6 +7376,15 @@ export const getCreateAccessTokenApiAuthAccessTokenTokenNamePostUrl = (tokenName return stringifiedParams.length > 0 ? `/api/auth/access_token/${tokenName}?${stringifiedParams}` : `/api/auth/access_token/${tokenName}` } +/** + * API to create a new access token. + * + * If roles are specified, all specified roles must be assigned to the user. + * If any role is not assigned to the user, the request fails and no token + * is created. If no roles are specified, the access token inherits all of the user's + * current roles from the user_roles table. + * @summary Create Access Token + */ export const createAccessTokenApiAuthAccessTokenTokenNamePost = async (tokenName: string, params: CreateAccessTokenApiAuthAccessTokenTokenNamePostParams, options?: RequestInit): Promise => { @@ -7313,10 +7445,6 @@ export const useCreateAccessTokenApiAuthAccessTokenTokenNamePost = { @@ -7325,6 +7453,10 @@ export const getDeleteAccessTokenApiAuthAccessTokenTokenNameDeleteUrl = (tokenNa return `/api/auth/access_token/${tokenName}` } +/** + * API to delete an access token. + * @summary Delete Access Token + */ export const deleteAccessTokenApiAuthAccessTokenTokenNameDelete = async (tokenName: string, options?: RequestInit): Promise => { return customFetch(getDeleteAccessTokenApiAuthAccessTokenTokenNameDeleteUrl(tokenName), @@ -7384,17 +7516,6 @@ export const useDeleteAccessTokenApiAuthAccessTokenTokenNameDelete = { @@ -7403,6 +7524,17 @@ export const getListAccessTokenRolesApiAuthAccessTokenTokenNameRolesGetUrl = (to return `/api/auth/access_token/${tokenName}/roles` } +/** + * List all roles assigned to an access token. + * + * Args: + * token_name: The token name + * user_name: Authenticated user (owner of the token) + * + * Returns: + * AccessTokenRolesResponse with list of role assignments + * @summary List Access Token Roles + */ export const listAccessTokenRolesApiAuthAccessTokenTokenNameRolesGet = async (tokenName: string, options?: RequestInit): Promise => { return customFetch(getListAccessTokenRolesApiAuthAccessTokenTokenNameRolesGetUrl(tokenName), @@ -7440,7 +7572,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(tokenName), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: tokenName !== null && tokenName !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type ListAccessTokenRolesApiAuthAccessTokenTokenNameRolesGetQueryResult = NonNullable>> @@ -7502,10 +7634,8 @@ export const invalidateListAccessTokenRolesApiAuthAccessTokenTokenNameRolesGet = -/** - * API to list all access tokens for a user, including their assigned roles. - * @summary List Access Tokens - */ + + export const getListAccessTokensApiAuthAccessTokenGetUrl = () => { @@ -7514,6 +7644,10 @@ export const getListAccessTokensApiAuthAccessTokenGetUrl = () => { return `/api/auth/access_token` } +/** + * API to list all access tokens for a user, including their assigned roles. + * @summary List Access Tokens + */ export const listAccessTokensApiAuthAccessTokenGet = async ( options?: RequestInit): Promise => { return customFetch(getListAccessTokensApiAuthAccessTokenGetUrl(), @@ -7613,29 +7747,8 @@ export const invalidateListAccessTokensApiAuthAccessTokenGet = async ( -/** - * Admin API to create an access token for a specific user. - -This endpoint allows administrators to create an access token -on behalf of any user in the system. -If roles are specified, all specified roles must be assigned to the target -user. If any role is not assigned to the user, the request fails and no -token is created. If no roles are specified, the access token inherits all of the -target user's current roles from the user_roles table. -Args: - user_id: The user ID to create the token for - token_name: Name for the access token - expires_at: Expiration date in YYYY-MM-DD format - description: Optional description for the token - roles: Optional list of roles to assign (must all be assigned to user) - admin_user: Authenticated admin user making the request - -Returns: - The generated access token string - * @summary Admin Create Access Token - */ export const getAdminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePostUrl = (userId: string, tokenName: string, params: AdminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePostParams,) => { @@ -7646,13 +7759,13 @@ export const getAdminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePostU if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -7661,6 +7774,29 @@ export const getAdminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePostU return stringifiedParams.length > 0 ? `/api/auth/user/${userId}/access_token/${tokenName}?${stringifiedParams}` : `/api/auth/user/${userId}/access_token/${tokenName}` } +/** + * Admin API to create an access token for a specific user. + * + * This endpoint allows administrators to create an access token + * on behalf of any user in the system. + * + * If roles are specified, all specified roles must be assigned to the target + * user. If any role is not assigned to the user, the request fails and no + * token is created. If no roles are specified, the access token inherits all of the + * target user's current roles from the user_roles table. + * + * Args: + * user_id: The user ID to create the token for + * token_name: Name for the access token + * expires_at: Expiration date in YYYY-MM-DD format + * description: Optional description for the token + * roles: Optional list of roles to assign (must all be assigned to user) + * admin_user: Authenticated admin user making the request + * + * Returns: + * The generated access token string + * @summary Admin Create Access Token + */ export const adminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePost = async (userId: string, tokenName: string, params: AdminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePostParams, options?: RequestInit): Promise => { @@ -7722,14 +7858,6 @@ export const useAdminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePost return useMutation(getAdminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePostMutationOptions(options), queryClient); } -/** - * Admin API to delete an access token for a specific user. - -Args: - user_id: The user ID who owns the token - token_name: Name of the token to delete - * @summary Admin Delete Access Token - */ export const getAdminDeleteAccessTokenApiAuthUserUserIdAccessTokenTokenNameDeleteUrl = (userId: string, tokenName: string,) => { @@ -7739,6 +7867,14 @@ export const getAdminDeleteAccessTokenApiAuthUserUserIdAccessTokenTokenNameDelet return `/api/auth/user/${userId}/access_token/${tokenName}` } +/** + * Admin API to delete an access token for a specific user. + * + * Args: + * user_id: The user ID who owns the token + * token_name: Name of the token to delete + * @summary Admin Delete Access Token + */ export const adminDeleteAccessTokenApiAuthUserUserIdAccessTokenTokenNameDelete = async (userId: string, tokenName: string, options?: RequestInit): Promise => { @@ -7799,16 +7935,6 @@ export const useAdminDeleteAccessTokenApiAuthUserUserIdAccessTokenTokenNameDelet return useMutation(getAdminDeleteAccessTokenApiAuthUserUserIdAccessTokenTokenNameDeleteMutationOptions(options), queryClient); } -/** - * Admin API to list all access tokens for a specific user, including their assigned roles. - -Args: - user_id: The user ID to list tokens for - -Returns: - List of AccessTokenWithRoles objects - * @summary Admin List Access Tokens - */ export const getAdminListAccessTokensApiAuthUserUserIdAccessTokenGetUrl = (userId: string,) => { @@ -7817,6 +7943,16 @@ export const getAdminListAccessTokensApiAuthUserUserIdAccessTokenGetUrl = (userI return `/api/auth/user/${userId}/access_token` } +/** + * Admin API to list all access tokens for a specific user, including their assigned roles. + * + * Args: + * user_id: The user ID to list tokens for + * + * Returns: + * List of AccessTokenWithRoles objects + * @summary Admin List Access Tokens + */ export const adminListAccessTokensApiAuthUserUserIdAccessTokenGet = async (userId: string, options?: RequestInit): Promise => { return customFetch(getAdminListAccessTokensApiAuthUserUserIdAccessTokenGetUrl(userId), @@ -7854,7 +7990,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(userId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: userId !== null && userId !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type AdminListAccessTokensApiAuthUserUserIdAccessTokenGetQueryResult = NonNullable>> @@ -7916,20 +8052,8 @@ export const invalidateAdminListAccessTokensApiAuthUserUserIdAccessTokenGet = as -/** - * List all users with optional filtering. -Args: - start_index: Pagination start (1-based, default: 1) - count: Results per page (default: 100, max: 1000) - id_prefix: Filter users whose ID starts with this prefix - roles: List of role names. Returns users who have ANY of these roles. - Use multiple query params: ?roles=admin&roles=user -Returns: - UserListResponse with paginated user list - * @summary List Users - */ export const getListUsersApiAuthUserGetUrl = (params?: ListUsersApiAuthUserGetParams,) => { const normalizedParams = new URLSearchParams(); @@ -7938,13 +8062,13 @@ export const getListUsersApiAuthUserGetUrl = (params?: ListUsersApiAuthUserGetPa if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -7953,6 +8077,20 @@ export const getListUsersApiAuthUserGetUrl = (params?: ListUsersApiAuthUserGetPa return stringifiedParams.length > 0 ? `/api/auth/user?${stringifiedParams}` : `/api/auth/user` } +/** + * List all users with optional filtering. + * + * Args: + * start_index: Pagination start (1-based, default: 1) + * count: Results per page (default: 100, max: 1000) + * id_prefix: Filter users whose ID starts with this prefix + * roles: List of role names. Returns users who have ANY of these roles. + * Use multiple query params: ?roles=admin&roles=user + * + * Returns: + * UserListResponse with paginated user list + * @summary List Users + */ export const listUsersApiAuthUserGet = async (params?: ListUsersApiAuthUserGetParams, options?: RequestInit): Promise => { return customFetch(getListUsersApiAuthUserGetUrl(params), @@ -8052,17 +8190,8 @@ export const invalidateListUsersApiAuthUserGet = async ( -/** - * Create a new user. -Args: - request: CreateUserRequest with user details - created_by: Authenticated user making the request -Returns: - Created User object - * @summary Create User - */ export const getCreateUserApiAuthUserPostUrl = () => { @@ -8071,6 +8200,17 @@ export const getCreateUserApiAuthUserPostUrl = () => { return `/api/auth/user` } +/** + * Create a new user. + * + * Args: + * request: CreateUserRequest with user details + * created_by: Authenticated user making the request + * + * Returns: + * Created User object + * @summary Create User + */ export const createUserApiAuthUserPost = async (createUserRequest: CreateUserRequest, options?: RequestInit): Promise => { return customFetch(getCreateUserApiAuthUserPostUrl(), @@ -8078,8 +8218,7 @@ export const createUserApiAuthUserPost = async (createUserRequest: CreateUserReq ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - createUserRequest,) + body: JSON.stringify(createUserRequest) } );} @@ -8131,16 +8270,6 @@ export const useCreateUserApiAuthUserPost = { @@ -8149,6 +8278,16 @@ export const getGetUserApiAuthUserUserIdGetUrl = (userId: string,) => { return `/api/auth/user/${userId}` } +/** + * Get a specific user's details including their roles. + * + * Args: + * user_id: The user ID to fetch + * + * Returns: + * UserWithRoles object + * @summary Get User + */ export const getUserApiAuthUserUserIdGet = async (userId: string, options?: RequestInit): Promise => { return customFetch(getGetUserApiAuthUserUserIdGetUrl(userId), @@ -8186,7 +8325,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(userId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: userId !== null && userId !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type GetUserApiAuthUserUserIdGetQueryResult = NonNullable>> @@ -8248,13 +8387,8 @@ export const invalidateGetUserApiAuthUserUserIdGet = async ( -/** - * Delete a user and all associated role assignments and PATs. -Args: - user_id: The user ID to delete - * @summary Delete User - */ + export const getDeleteUserApiAuthUserUserIdDeleteUrl = (userId: string,) => { @@ -8263,6 +8397,13 @@ export const getDeleteUserApiAuthUserUserIdDeleteUrl = (userId: string,) => { return `/api/auth/user/${userId}` } +/** + * Delete a user and all associated role assignments and PATs. + * + * Args: + * user_id: The user ID to delete + * @summary Delete User + */ export const deleteUserApiAuthUserUserIdDelete = async (userId: string, options?: RequestInit): Promise => { return customFetch(getDeleteUserApiAuthUserUserIdDeleteUrl(userId), @@ -8322,16 +8463,6 @@ export const useDeleteUserApiAuthUserUserIdDelete = { @@ -8340,6 +8471,16 @@ export const getListUserRolesApiAuthUserUserIdRolesGetUrl = (userId: string,) => return `/api/auth/user/${userId}/roles` } +/** + * List all roles assigned to a user. + * + * Args: + * user_id: The user ID + * + * Returns: + * UserRolesResponse with list of role assignments + * @summary List User Roles + */ export const listUserRolesApiAuthUserUserIdRolesGet = async (userId: string, options?: RequestInit): Promise => { return customFetch(getListUserRolesApiAuthUserUserIdRolesGetUrl(userId), @@ -8377,7 +8518,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(userId), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: userId !== null && userId !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type ListUserRolesApiAuthUserUserIdRolesGetQueryResult = NonNullable>> @@ -8439,18 +8580,8 @@ export const invalidateListUserRolesApiAuthUserUserIdRolesGet = async ( -/** - * Assign a role to a user. -Args: - user_id: The user ID - request: AssignRoleRequest with role_name - assigned_by: Authenticated user making the request -Returns: - UserRoleAssignment with assignment details - * @summary Assign Role To User - */ export const getAssignRoleToUserApiAuthUserUserIdRolesPostUrl = (userId: string,) => { @@ -8459,6 +8590,18 @@ export const getAssignRoleToUserApiAuthUserUserIdRolesPostUrl = (userId: string, return `/api/auth/user/${userId}/roles` } +/** + * Assign a role to a user. + * + * Args: + * user_id: The user ID + * request: AssignRoleRequest with role_name + * assigned_by: Authenticated user making the request + * + * Returns: + * UserRoleAssignment with assignment details + * @summary Assign Role To User + */ export const assignRoleToUserApiAuthUserUserIdRolesPost = async (userId: string, assignRoleRequest: AssignRoleRequest, options?: RequestInit): Promise => { @@ -8467,8 +8610,7 @@ export const assignRoleToUserApiAuthUserUserIdRolesPost = async (userId: string, ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - assignRoleRequest,) + body: JSON.stringify(assignRoleRequest) } );} @@ -8520,17 +8662,6 @@ export const useAssignRoleToUserApiAuthUserUserIdRolesPost = { @@ -8540,6 +8671,17 @@ export const getRemoveRoleFromUserApiAuthUserUserIdRolesRoleNameDeleteUrl = (use return `/api/auth/user/${userId}/roles/${roleName}` } +/** + * Remove a role from a user and all their PATs. + * + * When a role is removed from a user, it is automatically removed from all PATs + * owned by that user via the FK cascade from access_token_roles to user_roles. + * + * Args: + * user_id: The user ID + * role_name: The role to remove + * @summary Remove Role From User + */ export const removeRoleFromUserApiAuthUserUserIdRolesRoleNameDelete = async (userId: string, roleName: string, options?: RequestInit): Promise => { @@ -8600,16 +8742,6 @@ export const useRemoveRoleFromUserApiAuthUserUserIdRolesRoleNameDelete = { @@ -8618,6 +8750,16 @@ export const getListUsersWithRoleApiAuthRolesRoleNameUsersGetUrl = (roleName: st return `/api/auth/roles/${roleName}/users` } +/** + * List all users who have a specific role. + * + * Args: + * role_name: The role name + * + * Returns: + * RoleUsersResponse with list of users + * @summary List Users With Role + */ export const listUsersWithRoleApiAuthRolesRoleNameUsersGet = async (roleName: string, options?: RequestInit): Promise => { return customFetch(getListUsersWithRoleApiAuthRolesRoleNameUsersGetUrl(roleName), @@ -8655,7 +8797,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(roleName), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: roleName !== null && roleName !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type ListUsersWithRoleApiAuthRolesRoleNameUsersGetQueryResult = NonNullable>> @@ -8717,18 +8859,8 @@ export const invalidateListUsersWithRoleApiAuthRolesRoleNameUsersGet = async ( -/** - * Bulk assign a role to multiple users. -Args: - role_name: The role to assign - request: BulkAssignRequest with list of user_ids - assigned_by: Authenticated user making the request -Returns: - BulkAssignResponse with results - * @summary Bulk Assign Role - */ export const getBulkAssignRoleApiAuthRolesRoleNameUsersPostUrl = (roleName: string,) => { @@ -8737,6 +8869,18 @@ export const getBulkAssignRoleApiAuthRolesRoleNameUsersPostUrl = (roleName: stri return `/api/auth/roles/${roleName}/users` } +/** + * Bulk assign a role to multiple users. + * + * Args: + * role_name: The role to assign + * request: BulkAssignRequest with list of user_ids + * assigned_by: Authenticated user making the request + * + * Returns: + * BulkAssignResponse with results + * @summary Bulk Assign Role + */ export const bulkAssignRoleApiAuthRolesRoleNameUsersPost = async (roleName: string, bulkAssignRequest: BulkAssignRequest, options?: RequestInit): Promise => { @@ -8745,8 +8889,7 @@ export const bulkAssignRoleApiAuthRolesRoleNameUsersPost = async (roleName: stri ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - bulkAssignRequest,) + body: JSON.stringify(bulkAssignRequest) } );} @@ -8798,9 +8941,6 @@ export const useBulkAssignRoleApiAuthRolesRoleNameUsersPost = { const normalizedParams = new URLSearchParams(); @@ -8809,13 +8949,13 @@ export const getListAppsApiAppGetUrl = (params?: ListAppsApiAppGetParams,) => { if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -8824,6 +8964,9 @@ export const getListAppsApiAppGetUrl = (params?: ListAppsApiAppGetParams,) => { return stringifiedParams.length > 0 ? `/api/app?${stringifiedParams}` : `/api/app` } +/** + * @summary List Apps + */ export const listAppsApiAppGet = async (params?: ListAppsApiAppGetParams, options?: RequestInit): Promise => { return customFetch(getListAppsApiAppGetUrl(params), @@ -8923,9 +9066,8 @@ export const invalidateListAppsApiAppGet = async ( -/** - * @summary Get App - */ + + export const getGetAppApiAppUserNameGetUrl = (name: string, params?: GetAppApiAppUserNameGetParams,) => { const normalizedParams = new URLSearchParams(); @@ -8933,7 +9075,7 @@ export const getGetAppApiAppUserNameGetUrl = (name: string, Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -8942,6 +9084,9 @@ export const getGetAppApiAppUserNameGetUrl = (name: string, return stringifiedParams.length > 0 ? `/api/app/user/${name}?${stringifiedParams}` : `/api/app/user/${name}` } +/** + * @summary Get App + */ export const getAppApiAppUserNameGet = async (name: string, params?: GetAppApiAppUserNameGetParams, options?: RequestInit): Promise => { @@ -8982,7 +9127,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type GetAppApiAppUserNameGetQueryResult = NonNullable>> @@ -9049,9 +9194,8 @@ export const invalidateGetAppApiAppUserNameGet = async ( -/** - * @summary Create App - */ + + export const getCreateAppApiAppUserNamePostUrl = (name: string, params: CreateAppApiAppUserNamePostParams,) => { const normalizedParams = new URLSearchParams(); @@ -9059,7 +9203,7 @@ export const getCreateAppApiAppUserNamePostUrl = (name: string, Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -9068,6 +9212,9 @@ export const getCreateAppApiAppUserNamePostUrl = (name: string, return stringifiedParams.length > 0 ? `/api/app/user/${name}?${stringifiedParams}` : `/api/app/user/${name}` } +/** + * @summary Create App + */ export const createAppApiAppUserNamePost = async (name: string, createAppApiAppUserNamePostBody: string, params: CreateAppApiAppUserNamePostParams, options?: RequestInit): Promise => { @@ -9077,8 +9224,7 @@ export const createAppApiAppUserNamePost = async (name: string, ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - createAppApiAppUserNamePostBody,) + body: JSON.stringify(createAppApiAppUserNamePostBody) } );} @@ -9130,9 +9276,6 @@ export const useCreateAppApiAppUserNamePost = { @@ -9141,6 +9284,9 @@ export const getUpdateAppApiAppUserNamePatchUrl = (name: string,) => { return `/api/app/user/${name}` } +/** + * @summary Update App + */ export const updateAppApiAppUserNamePatch = async (name: string, updateAppApiAppUserNamePatchBody: string, options?: RequestInit): Promise => { @@ -9149,8 +9295,7 @@ export const updateAppApiAppUserNamePatch = async (name: string, ...options, method: 'PATCH', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - updateAppApiAppUserNamePatchBody,) + body: JSON.stringify(updateAppApiAppUserNamePatchBody) } );} @@ -9202,9 +9347,6 @@ export const useUpdateAppApiAppUserNamePatch = { const normalizedParams = new URLSearchParams(); @@ -9212,7 +9354,7 @@ export const getDeleteAppApiAppUserNameDeleteUrl = (name: string, Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -9221,6 +9363,9 @@ export const getDeleteAppApiAppUserNameDeleteUrl = (name: string, return stringifiedParams.length > 0 ? `/api/app/user/${name}?${stringifiedParams}` : `/api/app/user/${name}` } +/** + * @summary Delete App + */ export const deleteAppApiAppUserNameDelete = async (name: string, params?: DeleteAppApiAppUserNameDeleteParams, options?: RequestInit): Promise => { @@ -9281,9 +9426,6 @@ export const useDeleteAppApiAppUserNameDelete = { const normalizedParams = new URLSearchParams(); @@ -9291,7 +9433,7 @@ export const getGetAppContentApiAppUserNameSpecGetUrl = (name: string, Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -9300,6 +9442,9 @@ export const getGetAppContentApiAppUserNameSpecGetUrl = (name: string, return stringifiedParams.length > 0 ? `/api/app/user/${name}/spec?${stringifiedParams}` : `/api/app/user/${name}/spec` } +/** + * @summary Get App Content + */ export const getAppContentApiAppUserNameSpecGet = async (name: string, params?: GetAppContentApiAppUserNameSpecGetParams, options?: RequestInit): Promise => { @@ -9340,7 +9485,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type GetAppContentApiAppUserNameSpecGetQueryResult = NonNullable>> @@ -9407,9 +9552,8 @@ export const invalidateGetAppContentApiAppUserNameSpecGet = async ( -/** - * @summary Rename App - */ + + export const getRenameAppApiAppUserNameRenamePostUrl = (name: string,) => { @@ -9418,6 +9562,9 @@ export const getRenameAppApiAppUserNameRenamePostUrl = (name: string,) => { return `/api/app/user/${name}/rename` } +/** + * @summary Rename App + */ export const renameAppApiAppUserNameRenamePost = async (name: string, renameAppApiAppUserNameRenamePostBody: string, options?: RequestInit): Promise => { @@ -9426,8 +9573,7 @@ export const renameAppApiAppUserNameRenamePost = async (name: string, ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - renameAppApiAppUserNameRenamePostBody,) + body: JSON.stringify(renameAppApiAppUserNameRenamePostBody) } );} @@ -9479,10 +9625,6 @@ export const useRenameAppApiAppUserNameRenamePost = { const normalizedParams = new URLSearchParams(); @@ -9490,7 +9632,7 @@ export const getCancelWorkflowApiWorkflowNameCancelPostUrl = (name: string, Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -9499,6 +9641,10 @@ export const getCancelWorkflowApiWorkflowNameCancelPostUrl = (name: string, return stringifiedParams.length > 0 ? `/api/workflow/${name}/cancel?${stringifiedParams}` : `/api/workflow/${name}/cancel` } +/** + * Cancels the workflow. + * @summary Cancel Workflow + */ export const cancelWorkflowApiWorkflowNameCancelPost = async (name: string, params?: CancelWorkflowApiWorkflowNameCancelPostParams, options?: RequestInit): Promise => { @@ -9559,24 +9705,21 @@ export const useCancelWorkflowApiWorkflowNameCancelPost = { const normalizedParams = new URLSearchParams(); Object.entries(params || {}).forEach(([key, value]) => { - const explodeParameters = ["users","statuses","pools","tags","priority"]; + const explodeParameters = ["users","statuses","pools","tags","priority","label","no_label"]; if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -9585,6 +9728,9 @@ export const getListWorkflowApiWorkflowGetUrl = (params?: ListWorkflowApiWorkflo return stringifiedParams.length > 0 ? `/api/workflow?${stringifiedParams}` : `/api/workflow` } +/** + * @summary List Workflow + */ export const listWorkflowApiWorkflowGet = async (params?: ListWorkflowApiWorkflowGetParams, options?: RequestInit): Promise => { return customFetch(getListWorkflowApiWorkflowGetUrl(params), @@ -9684,10 +9830,8 @@ export const invalidateListWorkflowApiWorkflowGet = async ( -/** - * Returns the task (with the latest retry_id) with the given name in the workflow. - * @summary Get Workflow Task - */ + + export const getGetWorkflowTaskApiWorkflowNameTaskTaskNameGetUrl = (name: string, taskName: string,) => { @@ -9697,6 +9841,10 @@ export const getGetWorkflowTaskApiWorkflowNameTaskTaskNameGetUrl = (name: string return `/api/workflow/${name}/task/${taskName}` } +/** + * Returns the task (with the latest retry_id) with the given name in the workflow. + * @summary Get Workflow Task + */ export const getWorkflowTaskApiWorkflowNameTaskTaskNameGet = async (name: string, taskName: string, options?: RequestInit): Promise => { @@ -9737,7 +9885,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name && taskName), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined && taskName !== null && taskName !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type GetWorkflowTaskApiWorkflowNameTaskTaskNameGetQueryResult = NonNullable>> @@ -9804,9 +9952,8 @@ export const invalidateGetWorkflowTaskApiWorkflowNameTaskTaskNameGet = async ( -/** - * @summary List Task - */ + + export const getListTaskApiTaskGetUrl = (params?: ListTaskApiTaskGetParams,) => { const normalizedParams = new URLSearchParams(); @@ -9815,13 +9962,13 @@ export const getListTaskApiTaskGetUrl = (params?: ListTaskApiTaskGetParams,) => if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -9830,6 +9977,9 @@ export const getListTaskApiTaskGetUrl = (params?: ListTaskApiTaskGetParams,) => return stringifiedParams.length > 0 ? `/api/task?${stringifiedParams}` : `/api/task` } +/** + * @summary List Task + */ export const listTaskApiTaskGet = async (params?: ListTaskApiTaskGetParams, options?: RequestInit): Promise => { return customFetch(getListTaskApiTaskGetUrl(params), @@ -9929,10 +10079,8 @@ export const invalidateListTaskApiTaskGet = async ( -/** - * Returns the workflow with the given name in the database. - * @summary Get Workflow - */ + + export const getGetWorkflowApiWorkflowNameGetUrl = (name: string, params?: GetWorkflowApiWorkflowNameGetParams,) => { const normalizedParams = new URLSearchParams(); @@ -9940,7 +10088,7 @@ export const getGetWorkflowApiWorkflowNameGetUrl = (name: string, Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -9949,6 +10097,10 @@ export const getGetWorkflowApiWorkflowNameGetUrl = (name: string, return stringifiedParams.length > 0 ? `/api/workflow/${name}?${stringifiedParams}` : `/api/workflow/${name}` } +/** + * Returns the workflow with the given name in the database. + * @summary Get Workflow + */ export const getWorkflowApiWorkflowNameGet = async (name: string, params?: GetWorkflowApiWorkflowNameGetParams, options?: RequestInit): Promise => { @@ -9989,7 +10141,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type GetWorkflowApiWorkflowNameGetQueryResult = NonNullable>> @@ -10056,10 +10208,8 @@ export const invalidateGetWorkflowApiWorkflowNameGet = async ( -/** - * Returns the workflow logs. - * @summary Get Workflow Logs - */ + + export const getGetWorkflowLogsApiWorkflowNameLogsGetUrl = (name: string, params?: GetWorkflowLogsApiWorkflowNameLogsGetParams,) => { const normalizedParams = new URLSearchParams(); @@ -10067,7 +10217,7 @@ export const getGetWorkflowLogsApiWorkflowNameLogsGetUrl = (name: string, Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -10076,6 +10226,10 @@ export const getGetWorkflowLogsApiWorkflowNameLogsGetUrl = (name: string, return stringifiedParams.length > 0 ? `/api/workflow/${name}/logs?${stringifiedParams}` : `/api/workflow/${name}/logs` } +/** + * Returns the workflow logs. + * @summary Get Workflow Logs + */ export const getWorkflowLogsApiWorkflowNameLogsGet = async (name: string, params?: GetWorkflowLogsApiWorkflowNameLogsGetParams, options?: RequestInit): Promise => { @@ -10116,7 +10270,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type GetWorkflowLogsApiWorkflowNameLogsGetQueryResult = NonNullable>> @@ -10183,10 +10337,8 @@ export const invalidateGetWorkflowLogsApiWorkflowNameLogsGet = async ( -/** - * Returns the workflow pod conditions. - * @summary Get Workflow Pod Conditions - */ + + export const getGetWorkflowPodConditionsApiWorkflowNameEventsGetUrl = (name: string, params?: GetWorkflowPodConditionsApiWorkflowNameEventsGetParams,) => { const normalizedParams = new URLSearchParams(); @@ -10194,7 +10346,7 @@ export const getGetWorkflowPodConditionsApiWorkflowNameEventsGetUrl = (name: str Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -10203,6 +10355,10 @@ export const getGetWorkflowPodConditionsApiWorkflowNameEventsGetUrl = (name: str return stringifiedParams.length > 0 ? `/api/workflow/${name}/events?${stringifiedParams}` : `/api/workflow/${name}/events` } +/** + * Returns the workflow pod conditions. + * @summary Get Workflow Pod Conditions + */ export const getWorkflowPodConditionsApiWorkflowNameEventsGet = async (name: string, params?: GetWorkflowPodConditionsApiWorkflowNameEventsGetParams, options?: RequestInit): Promise => { @@ -10243,7 +10399,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type GetWorkflowPodConditionsApiWorkflowNameEventsGetQueryResult = NonNullable>> @@ -10310,10 +10466,8 @@ export const invalidateGetWorkflowPodConditionsApiWorkflowNameEventsGet = async -/** - * Returns the workflow error logs. - * @summary Get Workflow Error Logs - */ + + export const getGetWorkflowErrorLogsApiWorkflowNameErrorLogsGetUrl = (name: string, params?: GetWorkflowErrorLogsApiWorkflowNameErrorLogsGetParams,) => { const normalizedParams = new URLSearchParams(); @@ -10321,7 +10475,7 @@ export const getGetWorkflowErrorLogsApiWorkflowNameErrorLogsGetUrl = (name: stri Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -10330,6 +10484,10 @@ export const getGetWorkflowErrorLogsApiWorkflowNameErrorLogsGetUrl = (name: stri return stringifiedParams.length > 0 ? `/api/workflow/${name}/error_logs?${stringifiedParams}` : `/api/workflow/${name}/error_logs` } +/** + * Returns the workflow error logs. + * @summary Get Workflow Error Logs + */ export const getWorkflowErrorLogsApiWorkflowNameErrorLogsGet = async (name: string, params?: GetWorkflowErrorLogsApiWorkflowNameErrorLogsGetParams, options?: RequestInit): Promise => { @@ -10370,7 +10528,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type GetWorkflowErrorLogsApiWorkflowNameErrorLogsGetQueryResult = NonNullable>> @@ -10437,10 +10595,8 @@ export const invalidateGetWorkflowErrorLogsApiWorkflowNameErrorLogsGet = async ( -/** - * Returns the workflow spec. - * @summary Get Workflow Spec - */ + + export const getGetWorkflowSpecApiWorkflowNameSpecGetUrl = (name: string, params?: GetWorkflowSpecApiWorkflowNameSpecGetParams,) => { const normalizedParams = new URLSearchParams(); @@ -10448,7 +10604,7 @@ export const getGetWorkflowSpecApiWorkflowNameSpecGetUrl = (name: string, Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -10457,6 +10613,10 @@ export const getGetWorkflowSpecApiWorkflowNameSpecGetUrl = (name: string, return stringifiedParams.length > 0 ? `/api/workflow/${name}/spec?${stringifiedParams}` : `/api/workflow/${name}/spec` } +/** + * Returns the workflow spec. + * @summary Get Workflow Spec + */ export const getWorkflowSpecApiWorkflowNameSpecGet = async (name: string, params?: GetWorkflowSpecApiWorkflowNameSpecGetParams, options?: RequestInit): Promise => { @@ -10497,7 +10657,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type GetWorkflowSpecApiWorkflowNameSpecGetQueryResult = NonNullable>> @@ -10564,10 +10724,8 @@ export const invalidateGetWorkflowSpecApiWorkflowNameSpecGet = async ( -/** - * Returns the workflow spec. - * @summary Tag Workflow - */ + + export const getTagWorkflowApiWorkflowNameTagPostUrl = (name: string, params?: TagWorkflowApiWorkflowNameTagPostParams,) => { const normalizedParams = new URLSearchParams(); @@ -10577,13 +10735,13 @@ export const getTagWorkflowApiWorkflowNameTagPostUrl = (name: string, if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -10592,6 +10750,10 @@ export const getTagWorkflowApiWorkflowNameTagPostUrl = (name: string, return stringifiedParams.length > 0 ? `/api/workflow/${name}/tag?${stringifiedParams}` : `/api/workflow/${name}/tag` } +/** + * Returns the workflow spec. + * @summary Tag Workflow + */ export const tagWorkflowApiWorkflowNameTagPost = async (name: string, params?: TagWorkflowApiWorkflowNameTagPostParams, options?: RequestInit): Promise => { @@ -10652,10 +10814,6 @@ export const useTagWorkflowApiWorkflowNameTagPost = { @@ -10664,7 +10822,7 @@ export const getExecIntoGroupApiWorkflowNameExecGroupGroupNamePostUrl = (name: s Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -10673,6 +10831,10 @@ export const getExecIntoGroupApiWorkflowNameExecGroupGroupNamePostUrl = (name: s return stringifiedParams.length > 0 ? `/api/workflow/${name}/exec/group/${groupName}?${stringifiedParams}` : `/api/workflow/${name}/exec/group/${groupName}` } +/** + * Send command to all tasks in a group. + * @summary Exec Into Group + */ export const execIntoGroupApiWorkflowNameExecGroupGroupNamePost = async (name: string, groupName: string, params: ExecIntoGroupApiWorkflowNameExecGroupGroupNamePostParams, options?: RequestInit): Promise => { @@ -10734,10 +10896,6 @@ export const useExecIntoGroupApiWorkflowNameExecGroupGroupNamePost = { @@ -10746,7 +10904,7 @@ export const getExecIntoTaskApiWorkflowNameExecTaskTaskNamePostUrl = (name: stri Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -10755,6 +10913,10 @@ export const getExecIntoTaskApiWorkflowNameExecTaskTaskNamePostUrl = (name: stri return stringifiedParams.length > 0 ? `/api/workflow/${name}/exec/task/${taskName}?${stringifiedParams}` : `/api/workflow/${name}/exec/task/${taskName}` } +/** + * Exec into a task container. + * @summary Exec Into Task + */ export const execIntoTaskApiWorkflowNameExecTaskTaskNamePost = async (name: string, taskName: string, params: ExecIntoTaskApiWorkflowNameExecTaskTaskNamePostParams, options?: RequestInit): Promise => { @@ -10816,10 +10978,6 @@ export const useExecIntoTaskApiWorkflowNameExecTaskTaskNamePost = { @@ -10830,13 +10988,13 @@ export const getPortForwardTaskApiWorkflowNamePortforwardTaskNamePostUrl = (name if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -10845,6 +11003,10 @@ export const getPortForwardTaskApiWorkflowNamePortforwardTaskNamePostUrl = (name return stringifiedParams.length > 0 ? `/api/workflow/${name}/portforward/${taskName}?${stringifiedParams}` : `/api/workflow/${name}/portforward/${taskName}` } +/** + * Portforward into a task container. + * @summary Port Forward Task + */ export const portForwardTaskApiWorkflowNamePortforwardTaskNamePost = async (name: string, taskName: string, params?: PortForwardTaskApiWorkflowNamePortforwardTaskNamePostParams, options?: RequestInit): Promise => { @@ -10906,10 +11068,6 @@ export const usePortForwardTaskApiWorkflowNamePortforwardTaskNamePost = { @@ -10918,7 +11076,7 @@ export const getPortForwardWebserverApiWorkflowNameWebserverTaskNamePostUrl = (n Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -10927,6 +11085,10 @@ export const getPortForwardWebserverApiWorkflowNameWebserverTaskNamePostUrl = (n return stringifiedParams.length > 0 ? `/api/workflow/${name}/webserver/${taskName}?${stringifiedParams}` : `/api/workflow/${name}/webserver/${taskName}` } +/** + * Hold a webserver connection to a task container. + * @summary Port Forward Webserver + */ export const portForwardWebserverApiWorkflowNameWebserverTaskNamePost = async (name: string, taskName: string, params: PortForwardWebserverApiWorkflowNameWebserverTaskNamePostParams, options?: RequestInit): Promise => { @@ -10988,10 +11150,6 @@ export const usePortForwardWebserverApiWorkflowNameWebserverTaskNamePost = { @@ -11001,6 +11159,10 @@ export const getRsyncTaskApiWorkflowNameRsyncTaskTaskNamePostUrl = (name: string return `/api/workflow/${name}/rsync/task/${taskName}` } +/** + * Rsync into a task container. + * @summary Rsync Task + */ export const rsyncTaskApiWorkflowNameRsyncTaskTaskNamePost = async (name: string, taskName: string, options?: RequestInit): Promise => { @@ -11061,10 +11223,6 @@ export const useRsyncTaskApiWorkflowNameRsyncTaskTaskNamePost = { @@ -11073,6 +11231,10 @@ export const getGetUserCredentialApiCredentialsGetUrl = () => { return `/api/credentials` } +/** + * Get default/all user credentials + * @summary Get User Credential + */ export const getUserCredentialApiCredentialsGet = async ( options?: RequestInit): Promise => { return customFetch(getGetUserCredentialApiCredentialsGetUrl(), @@ -11172,10 +11334,8 @@ export const invalidateGetUserCredentialApiCredentialsGet = async ( -/** - * Post/Update user credentials - * @summary Set User Credential - */ + + export const getSetUserCredentialApiCredentialsCredNamePostUrl = (credName: string,) => { @@ -11184,6 +11344,10 @@ export const getSetUserCredentialApiCredentialsCredNamePostUrl = (credName: stri return `/api/credentials/${credName}` } +/** + * Post/Update user credentials + * @summary Set User Credential + */ export const setUserCredentialApiCredentialsCredNamePost = async (credName: string, credentialOptions: CredentialOptions, options?: RequestInit): Promise => { @@ -11192,8 +11356,7 @@ export const setUserCredentialApiCredentialsCredNamePost = async (credName: stri ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - credentialOptions,) + body: JSON.stringify(credentialOptions) } );} @@ -11245,10 +11408,6 @@ export const useSetUserCredentialApiCredentialsCredNamePost = { @@ -11257,6 +11416,10 @@ export const getDeleteUsersCredentialApiCredentialsCredNameDeleteUrl = (credName return `/api/credentials/${credName}` } +/** + * Delete user credentials given the secret_id + * @summary Delete Users Credential + */ export const deleteUsersCredentialApiCredentialsCredNameDelete = async (credName: string, options?: RequestInit): Promise => { return customFetch(getDeleteUsersCredentialApiCredentialsCredNameDeleteUrl(credName), @@ -11316,10 +11479,6 @@ export const useDeleteUsersCredentialApiCredentialsCredNameDelete = { const normalizedParams = new URLSearchParams(); @@ -11328,13 +11487,13 @@ export const getGetResourcesApiResourcesGetUrl = (params?: GetResourcesApiResour if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -11343,6 +11502,10 @@ export const getGetResourcesApiResourcesGetUrl = (params?: GetResourcesApiResour return stringifiedParams.length > 0 ? `/api/resources?${stringifiedParams}` : `/api/resources` } +/** + * Returns the information of resources available in different pools. + * @summary Get Resources + */ export const getResourcesApiResourcesGet = async (params?: GetResourcesApiResourcesGetParams, options?: RequestInit): Promise => { return customFetch(getGetResourcesApiResourcesGetUrl(params), @@ -11442,10 +11605,8 @@ export const invalidateGetResourcesApiResourcesGet = async ( -/** - * Returns the request resource's information. - * @summary Get One Resource - */ + + export const getGetOneResourceApiResourcesNameGetUrl = (name: string,) => { @@ -11454,6 +11615,10 @@ export const getGetOneResourceApiResourcesNameGetUrl = (name: string,) => { return `/api/resources/${name}` } +/** + * Returns the request resource's information. + * @summary Get One Resource + */ export const getOneResourceApiResourcesNameGet = async (name: string, options?: RequestInit): Promise => { return customFetch(getGetOneResourceApiResourcesNameGetUrl(name), @@ -11491,7 +11656,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {}; - return { queryKey, queryFn, enabled: !!(name), ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } + return { queryKey, queryFn, enabled: name !== null && name !== undefined, ...queryOptions} as UseQueryOptions>, TError, TData> & { queryKey: DataTag } } export type GetOneResourceApiResourcesNameGetQueryResult = NonNullable>> @@ -11553,14 +11718,8 @@ export const invalidateGetOneResourceApiResourcesNameGet = async ( -/** - * Returns information regarding pools to users. -If all_pools is set to true, all pools' information will be returned in API response. -Otherwise, only information from pools that the user has access to will be returned -in the response. - * @summary Get Pools - */ + export const getGetPoolsApiPoolGetUrl = (params?: GetPoolsApiPoolGetParams,) => { const normalizedParams = new URLSearchParams(); @@ -11569,13 +11728,13 @@ export const getGetPoolsApiPoolGetUrl = (params?: GetPoolsApiPoolGetParams,) => if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -11584,6 +11743,14 @@ export const getGetPoolsApiPoolGetUrl = (params?: GetPoolsApiPoolGetParams,) => return stringifiedParams.length > 0 ? `/api/pool?${stringifiedParams}` : `/api/pool` } +/** + * Returns information regarding pools to users. + * + * If all_pools is set to true, all pools' information will be returned in API response. + * Otherwise, only information from pools that the user has access to will be returned + * in the response. + * @summary Get Pools + */ export const getPoolsApiPoolGet = async (params?: GetPoolsApiPoolGetParams, options?: RequestInit): Promise => { return customFetch(getGetPoolsApiPoolGetUrl(params), @@ -11683,9 +11850,8 @@ export const invalidateGetPoolsApiPoolGet = async ( -/** - * @summary Get Pool Quotas - */ + + export const getGetPoolQuotasApiPoolQuotaGetUrl = (params?: GetPoolQuotasApiPoolQuotaGetParams,) => { const normalizedParams = new URLSearchParams(); @@ -11694,13 +11860,13 @@ export const getGetPoolQuotasApiPoolQuotaGetUrl = (params?: GetPoolQuotasApiPool if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -11709,6 +11875,9 @@ export const getGetPoolQuotasApiPoolQuotaGetUrl = (params?: GetPoolQuotasApiPool return stringifiedParams.length > 0 ? `/api/pool_quota?${stringifiedParams}` : `/api/pool_quota` } +/** + * @summary Get Pool Quotas + */ export const getPoolQuotasApiPoolQuotaGet = async (params?: GetPoolQuotasApiPoolQuotaGetParams, options?: RequestInit): Promise => { return customFetch(getGetPoolQuotasApiPoolQuotaGetUrl(params), @@ -11808,26 +11977,24 @@ export const invalidateGetPoolQuotasApiPoolQuotaGet = async ( -/** - * This api validates that a workflow is well formed and valid and then submits it. - * @summary Submit Workflow - */ + + export const getSubmitWorkflowApiPoolPoolNameWorkflowPostUrl = (poolName: string, params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams,) => { const normalizedParams = new URLSearchParams(); Object.entries(params || {}).forEach(([key, value]) => { - const explodeParameters = ["env_vars"]; + const explodeParameters = ["env_vars","label"]; if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? 'null' : v.toString()); + normalizedParams.append(key, v === null ? 'null' : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -11836,8 +12003,12 @@ export const getSubmitWorkflowApiPoolPoolNameWorkflowPostUrl = (poolName: string return stringifiedParams.length > 0 ? `/api/pool/${poolName}/workflow?${stringifiedParams}` : `/api/pool/${poolName}/workflow` } +/** + * This api validates that a workflow is well formed and valid and then submits it. + * @summary Submit Workflow + */ export const submitWorkflowApiPoolPoolNameWorkflowPost = async (poolName: string, - templateSpecNull: TemplateSpec | null, + templateSpecNull?: TemplateSpec | null, params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams, options?: RequestInit): Promise => { return customFetch(getSubmitWorkflowApiPoolPoolNameWorkflowPostUrl(poolName,params), @@ -11845,8 +12016,7 @@ export const submitWorkflowApiPoolPoolNameWorkflowPost = async (poolName: string ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - templateSpecNull,) + body: JSON.stringify(templateSpecNull) } );} @@ -11854,8 +12024,8 @@ export const submitWorkflowApiPoolPoolNameWorkflowPost = async (poolName: string export const getSubmitWorkflowApiPoolPoolNameWorkflowPostMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{poolName: string;data: TemplateSpec | null;params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams}, TContext>, request?: SecondParameter} -): UseMutationOptions>, TError,{poolName: string;data: TemplateSpec | null;params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams}, TContext> => { + TContext = unknown>(options?: { mutation?:UseMutationOptions>, TError,{poolName: string;data?: TemplateSpec | null;params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams}, TContext>, request?: SecondParameter} +): UseMutationOptions>, TError,{poolName: string;data?: TemplateSpec | null;params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams}, TContext> => { const mutationKey = ['submitWorkflowApiPoolPoolNameWorkflowPost']; const {mutation: mutationOptions, request: requestOptions} = options ? @@ -11867,7 +12037,7 @@ const {mutation: mutationOptions, request: requestOptions} = options ? - const mutationFn: MutationFunction>, {poolName: string;data: TemplateSpec | null;params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams}> = (props) => { + const mutationFn: MutationFunction>, {poolName: string;data?: TemplateSpec | null;params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams}> = (props) => { const {poolName,data,params} = props ?? {}; return submitWorkflowApiPoolPoolNameWorkflowPost(poolName,data,params,requestOptions) @@ -11881,27 +12051,23 @@ const {mutation: mutationOptions, request: requestOptions} = options ? return { mutationFn, ...mutationOptions }} export type SubmitWorkflowApiPoolPoolNameWorkflowPostMutationResult = NonNullable>> - export type SubmitWorkflowApiPoolPoolNameWorkflowPostMutationBody = TemplateSpec | null + export type SubmitWorkflowApiPoolPoolNameWorkflowPostMutationBody = TemplateSpec | null | undefined export type SubmitWorkflowApiPoolPoolNameWorkflowPostMutationError = HTTPValidationError /** * @summary Submit Workflow */ export const useSubmitWorkflowApiPoolPoolNameWorkflowPost = (options?: { mutation?:UseMutationOptions>, TError,{poolName: string;data: TemplateSpec | null;params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams}, TContext>, request?: SecondParameter} + TContext = unknown>(options?: { mutation?:UseMutationOptions>, TError,{poolName: string;data?: TemplateSpec | null;params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams}, TContext>, request?: SecondParameter} , queryClient?: QueryClient): UseMutationResult< Awaited>, TError, - {poolName: string;data: TemplateSpec | null;params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams}, + {poolName: string;data?: TemplateSpec | null;params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams}, TContext > => { return useMutation(getSubmitWorkflowApiPoolPoolNameWorkflowPostMutationOptions(options), queryClient); } -/** - * This api restarts a failed workflow and then submits it. - * @summary Restart Workflow - */ export const getRestartWorkflowApiPoolPoolNameWorkflowWorkflowIdRestartPostUrl = (poolName: string, workflowId: string,) => { @@ -11911,6 +12077,10 @@ export const getRestartWorkflowApiPoolPoolNameWorkflowWorkflowIdRestartPostUrl = return `/api/pool/${poolName}/workflow/${workflowId}/restart` } +/** + * This api restarts a failed workflow and then submits it. + * @summary Restart Workflow + */ export const restartWorkflowApiPoolPoolNameWorkflowWorkflowIdRestartPost = async (poolName: string, workflowId: string, options?: RequestInit): Promise => { @@ -11971,9 +12141,6 @@ export const useRestartWorkflowApiPoolPoolNameWorkflowWorkflowIdRestartPost = { @@ -11982,6 +12149,9 @@ export const getGetNotificationSettingsApiProfileSettingsGetUrl = () => { return `/api/profile/settings` } +/** + * @summary Get Notification Settings + */ export const getNotificationSettingsApiProfileSettingsGet = async ( options?: RequestInit): Promise => { return customFetch(getGetNotificationSettingsApiProfileSettingsGetUrl(), @@ -12081,16 +12251,15 @@ export const invalidateGetNotificationSettingsApiProfileSettingsGet = async ( -/** - * @summary Set Notification Settings - */ + + export const getSetNotificationSettingsApiProfileSettingsPostUrl = (params?: SetNotificationSettingsApiProfileSettingsPostParams,) => { const normalizedParams = new URLSearchParams(); Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? 'null' : value.toString()) + normalizedParams.append(key, value === null ? 'null' : String(value)) } }); @@ -12099,6 +12268,9 @@ export const getSetNotificationSettingsApiProfileSettingsPostUrl = (params?: Set return stringifiedParams.length > 0 ? `/api/profile/settings?${stringifiedParams}` : `/api/profile/settings` } +/** + * @summary Set Notification Settings + */ export const setNotificationSettingsApiProfileSettingsPost = async (userProfile: UserProfile, params?: SetNotificationSettingsApiProfileSettingsPostParams, options?: RequestInit): Promise => { @@ -12107,8 +12279,7 @@ export const setNotificationSettingsApiProfileSettingsPost = async (userProfile: ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, - body: JSON.stringify( - userProfile,) + body: JSON.stringify(userProfile) } );} @@ -12160,9 +12331,6 @@ export const useSetNotificationSettingsApiProfileSettingsPost = { @@ -12171,6 +12339,9 @@ export const getGetOsmoClientVersionClientVersionGetUrl = () => { return `/client/version` } +/** + * @summary Get Osmo Client Version + */ export const getOsmoClientVersionClientVersionGet = async ( options?: RequestInit): Promise => { return customFetch(getGetOsmoClientVersionClientVersionGetUrl(), @@ -12270,11 +12441,8 @@ export const invalidateGetOsmoClientVersionClientVersionGet = async ( -/** - * To be used for the readiness probe, but not liveness probe. That way, if this method is -slow, no new traffic gets routed, instead of killing the service. - * @summary Health - */ + + export const getHealthHealthGetUrl = () => { @@ -12283,6 +12451,11 @@ export const getHealthHealthGetUrl = () => { return `/health` } +/** + * To be used for the readiness probe, but not liveness probe. That way, if this method is + * slow, no new traffic gets routed, instead of killing the service. + * @summary Health + */ export const healthHealthGet = async ( options?: RequestInit): Promise => { return customFetch(getHealthHealthGetUrl(), @@ -12382,9 +12555,8 @@ export const invalidateHealthHealthGet = async ( -/** - * @summary Get Version - */ + + export const getGetVersionApiVersionGetUrl = () => { @@ -12393,6 +12565,9 @@ export const getGetVersionApiVersionGetUrl = () => { return `/api/version` } +/** + * @summary Get Version + */ export const getVersionApiVersionGet = async ( options?: RequestInit): Promise => { return customFetch(getGetVersionApiVersionGetUrl(), @@ -12492,10 +12667,8 @@ export const invalidateGetVersionApiVersionGet = async ( -/** - * Returns the values of all users who have submitted a workflow. - * @summary Get Users - */ + + export const getGetUsersApiUsersGetUrl = () => { @@ -12504,6 +12677,10 @@ export const getGetUsersApiUsersGetUrl = () => { return `/api/users` } +/** + * Returns the values of all users who have submitted a workflow. + * @summary Get Users + */ export const getUsersApiUsersGet = async ( options?: RequestInit): Promise => { return customFetch(getGetUsersApiUsersGetUrl(), @@ -12603,10 +12780,8 @@ export const invalidateGetUsersApiUsersGet = async ( -/** - * Returns all workflow tags. - * @summary Get Available Workflow Tags - */ + + export const getGetAvailableWorkflowTagsApiTagGetUrl = () => { @@ -12615,6 +12790,10 @@ export const getGetAvailableWorkflowTagsApiTagGetUrl = () => { return `/api/tag` } +/** + * Returns all workflow tags. + * @summary Get Available Workflow Tags + */ export const getAvailableWorkflowTagsApiTagGet = async ( options?: RequestInit): Promise => { return customFetch(getGetAvailableWorkflowTagsApiTagGetUrl(), @@ -12714,10 +12893,8 @@ export const invalidateGetAvailableWorkflowTagsApiTagGet = async ( -/** - * Get all the workflow plugins configurations - * @summary Get Workflow Plugins Configs - */ + + export const getGetWorkflowPluginsConfigsApiPluginsConfigsGetUrl = () => { @@ -12726,6 +12903,10 @@ export const getGetWorkflowPluginsConfigsApiPluginsConfigsGetUrl = () => { return `/api/plugins/configs` } +/** + * Get all the workflow plugins configurations + * @summary Get Workflow Plugins Configs + */ export const getWorkflowPluginsConfigsApiPluginsConfigsGet = async ( options?: RequestInit): Promise => { return customFetch(getGetWorkflowPluginsConfigsApiPluginsConfigsGetUrl(), diff --git a/src/ui/src/lib/api/server/workflows.ts b/src/ui/src/lib/api/server/workflows.ts index db209cfa6e..b819114de1 100644 --- a/src/ui/src/lib/api/server/workflows.ts +++ b/src/ui/src/lib/api/server/workflows.ts @@ -66,6 +66,10 @@ export interface WorkflowsQueryParams { all_pools?: boolean; /** ISO date string — only return workflows submitted after this time */ submitted_after?: string; + /** Exact or pattern key=value workflow label filters */ + label?: string[]; + /** Workflow label keys that must be absent */ + no_label?: string[]; } // ============================================================================= @@ -100,6 +104,8 @@ export const fetchWorkflows = cache(async (params: WorkflowsQueryParams = {}): P all_users: params.all_users, all_pools: params.all_pools, submitted_after: params.submitted_after, + label: params.label, + no_label: params.no_label, }; return listWorkflowApiWorkflowGet(apiParams); @@ -217,6 +223,8 @@ export async function prefetchWorkflowsList( const statusFilters = filterChips.filter((c) => c.field === "status").map((c) => c.value as WorkflowStatus); const poolFilters = filterChips.filter((c) => c.field === "pool").map((c) => c.value); const userFilters = filterChips.filter((c) => c.field === "user").map((c) => c.value); + const labelFilters = filterChips.filter((c) => c.field === "label").map((c) => c.value); + const missingLabelFilters = filterChips.filter((c) => c.field === "no_label").map((c) => c.value); const hasUserChips = userFilters.length > 0; const effectiveShowAllUsers = hasUserChips ? false : showAllUsers; @@ -236,6 +244,8 @@ export async function prefetchWorkflowsList( pools: poolFilters.length > 0 ? poolFilters : undefined, users: userFilters.length > 0 ? userFilters : undefined, submitted_after: submittedAfter, + label: labelFilters.length > 0 ? labelFilters : undefined, + no_label: missingLabelFilters.length > 0 ? missingLabelFilters : undefined, }); return { diff --git a/src/ui/src/lib/workflow-labels.test.ts b/src/ui/src/lib/workflow-labels.test.ts new file mode 100644 index 0000000000..ec04d7c53f --- /dev/null +++ b/src/ui/src/lib/workflow-labels.test.ts @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { describe, expect, it } from "vitest"; +import { + MAX_WORKFLOW_LABELS, + formatWorkflowLabels, + getChangedWorkflowLabelAssignments, + sortedWorkflowLabelEntries, + validateWorkflowLabelDrafts, + type WorkflowLabelDraft, +} from "@/lib/workflow-labels"; + +const draft = (key: string, value: string): WorkflowLabelDraft => ({ key, value }); + +describe("workflow label drafts", () => { + it("sends only labels changed from a resubmitted workflow", () => { + expect( + getChangedWorkflowLabelAssignments( + [draft("project", "robotics"), draft("team", "simulation"), draft("run", "42")], + { + project: "robotics", + team: "robotics", + }, + ), + ).toEqual(["team=simulation", "run=42"]); + }); + + it("rejects incomplete and duplicate overrides before submission", () => { + expect(validateWorkflowLabelDrafts([draft("team", "")])).toMatch(/key and value/i); + expect(validateWorkflowLabelDrafts([draft("team", "one"), draft("team", "two")])).toMatch(/duplicate/i); + }); + + it("enforces the shared 16-label UI cap", () => { + const labels = Array.from({ length: MAX_WORKFLOW_LABELS + 1 }, (_, index) => draft(`key${index}`, "value")); + + expect(validateWorkflowLabelDrafts(labels)).toContain(String(MAX_WORKFLOW_LABELS)); + }); + + it("formats canonical labels deterministically", () => { + expect(formatWorkflowLabels({ zeta: "last", alpha: "first" })).toBe("alpha=first, zeta=last"); + expect(formatWorkflowLabels({})).toBe("—"); + }); + + it("sorts label entries deterministically and tolerates missing maps", () => { + expect(sortedWorkflowLabelEntries({ zeta: "last", alpha: "first" })).toEqual([ + ["alpha", "first"], + ["zeta", "last"], + ]); + expect(sortedWorkflowLabelEntries(undefined)).toEqual([]); + }); +}); diff --git a/src/ui/src/lib/workflow-labels.ts b/src/ui/src/lib/workflow-labels.ts new file mode 100644 index 0000000000..b317ea51ca --- /dev/null +++ b/src/ui/src/lib/workflow-labels.ts @@ -0,0 +1,67 @@ +/** + * Workflow label formatting, draft editing, and validation helpers + * shared by the detail, resubmit, and submit surfaces. + */ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +// Mirrors MAX_WORKFLOW_LABELS in src/lib/utils/validation.py. +export const MAX_WORKFLOW_LABELS = 16; + +export interface WorkflowLabelDraft { + key: string; + value: string; +} + +export function validateWorkflowLabelDrafts(labels: WorkflowLabelDraft[]): string | null { + if (labels.length > MAX_WORKFLOW_LABELS) { + return `A workflow can have at most ${MAX_WORKFLOW_LABELS} label overrides.`; + } + + const keys = new Set(); + for (const label of labels) { + const key = label.key.trim(); + const value = label.value.trim(); + if (!key || !value) { + return "Every workflow label needs both a key and value."; + } + if (keys.has(key)) { + return `Duplicate workflow label key: ${key}`; + } + keys.add(key); + } + return null; +} + +export function getChangedWorkflowLabelAssignments( + labels: WorkflowLabelDraft[], + originalLabels: Record, +): string[] { + return labels + .map(({ key, value }) => ({ key: key.trim(), value: value.trim() })) + .filter(({ key, value }) => originalLabels[key] !== value) + .map(({ key, value }) => `${key}=${value}`); +} + +export function sortedWorkflowLabelEntries(labels: Record | null | undefined): [string, string][] { + return Object.entries(labels ?? {}).sort(([left], [right]) => left.localeCompare(right)); +} + +export function formatWorkflowLabels(labels: Record | null | undefined): string { + const entries = sortedWorkflowLabelEntries(labels); + if (entries.length === 0) return "—"; + return entries.map(([key, value]) => `${key}=${value}`).join(", "); +} diff --git a/src/ui/src/mocks/generated-mocks.ts b/src/ui/src/mocks/generated-mocks.ts index f6a720d66b..9b0619ed00 100644 --- a/src/ui/src/mocks/generated-mocks.ts +++ b/src/ui/src/mocks/generated-mocks.ts @@ -1,5 +1,5 @@ /** - * Generated by orval v8.5.3 🍺 + * Generated by orval v8.17.0 🍺 * Do not edit manually. * FastAPI * OpenAPI spec version: 0.1.0 @@ -217,10 +217,10 @@ export interface ConfigDiffResponse { /** * Type of configs supported by config history */ -export type SrcLibUtilsConfigHistoryConfigHistoryType = - (typeof SrcLibUtilsConfigHistoryConfigHistoryType)[keyof typeof SrcLibUtilsConfigHistoryConfigHistoryType]; +export type ConfigHistoryType = (typeof ConfigHistoryType)[keyof typeof ConfigHistoryType]; -export const SrcLibUtilsConfigHistoryConfigHistoryType = { +export const ConfigHistoryType = { + DATASET: "DATASET", SERVICE: "SERVICE", WORKFLOW: "WORKFLOW", BACKEND: "BACKEND", @@ -236,7 +236,7 @@ export const SrcLibUtilsConfigHistoryConfigHistoryType = { * Object storing config history. */ export interface ConfigHistory { - config_type: SrcLibUtilsConfigHistoryConfigHistoryType; + config_type: ConfigHistoryType; name: string; revision: number; username: string; @@ -291,6 +291,19 @@ export interface UserRegistryCredential { auth: string; } +/** + * S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. + */ +export type UserDataCredentialAddressingStyle = + | (typeof UserDataCredentialAddressingStyle)[keyof typeof UserDataCredentialAddressingStyle] + | null; + +export const UserDataCredentialAddressingStyle = { + virtual: "virtual", + path: "path", + auto: "auto", +} as const; + /** * Authentication information for a data service. */ @@ -302,7 +315,7 @@ export interface UserDataCredential { /** HTTP service URL for S3-compatible providers (e.g., http://minio:9000, https://s3-compat.example.com). Leave unset for native AWS S3, GCS, Azure, etc. */ override_url?: string | null; /** S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. */ - addressing_style?: "virtual" | "path" | "auto" | null; + addressing_style?: UserDataCredentialAddressingStyle; /** The authentication key for a data backend */ access_key_id: string; /** The authentication secret for a data backend */ @@ -334,6 +347,19 @@ export interface CredentialOptions { generic_credential?: UserCredential | null; } +/** + * S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. + */ +export type StaticDataCredentialAddressingStyle = + | (typeof StaticDataCredentialAddressingStyle)[keyof typeof StaticDataCredentialAddressingStyle] + | null; + +export const StaticDataCredentialAddressingStyle = { + virtual: "virtual", + path: "path", + auto: "auto", +} as const; + /** * Static data credentials (i.e. credentials with access_key_id and access_key) for a data backend. */ @@ -345,7 +371,7 @@ export interface StaticDataCredential { /** HTTP service URL for S3-compatible providers (e.g., http://minio:9000, https://s3-compat.example.com). Leave unset for native AWS S3, GCS, Azure, etc. */ override_url?: string | null; /** S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. */ - addressing_style?: "virtual" | "path" | "auto" | null; + addressing_style?: StaticDataCredentialAddressingStyle; /** The authentication key for a data backend */ access_key_id: string; /** The encrypted authentication secret for a data backend */ @@ -353,13 +379,26 @@ export interface StaticDataCredential { } /** - * Data credential that delegates resolution to the underlying SDK. + * S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. + */ +export type DefaultDataCredentialAddressingStyle = + | (typeof DefaultDataCredentialAddressingStyle)[keyof typeof DefaultDataCredentialAddressingStyle] + | null; -Uses the SDK's default credential chain (e.g., Azure's DefaultAzureCredential, -boto3's credential resolution) which may include environment variables, -workload identity, instance metadata, and other provider-specific methods. +export const DefaultDataCredentialAddressingStyle = { + virtual: "virtual", + path: "path", + auto: "auto", +} as const; -Intentionally left empty as all credential resolution is handled by the SDK. +/** + * Data credential that delegates resolution to the underlying SDK. + * + * Uses the SDK's default credential chain (e.g., Azure's DefaultAzureCredential, + * boto3's credential resolution) which may include environment variables, + * workload identity, instance metadata, and other provider-specific methods. + * + * Intentionally left empty as all credential resolution is handled by the SDK. */ export interface DefaultDataCredential { /** The OSMO storage URI for the data service (e.g., s3://bucket). For S3-compatible services with HTTP endpoints, set 'override_url' separately rather than pasting the full HTTPS URL here. */ @@ -369,7 +408,7 @@ export interface DefaultDataCredential { /** HTTP service URL for S3-compatible providers (e.g., http://minio:9000, https://s3-compat.example.com). Leave unset for native AWS S3, GCS, Azure, etc. */ override_url?: string | null; /** S3 request addressing style for S3-compatible providers. Use 'virtual' for providers such as CoreWeave CAIOS that reject path-style requests, or 'path' for localstack/MinIO-style endpoints. */ - addressing_style?: "virtual" | "path" | "auto" | null; + addressing_style?: DefaultDataCredentialAddressingStyle; } /** @@ -422,7 +461,7 @@ export const PoolStatus = { /** * Resources like GPU or CPU that have a discrete number. For guarantee and maximum, a value of -1 -indicates that there is no limit. + * indicates that there is no limit. */ export interface PoolResourceCountable { guarantee?: number; @@ -593,10 +632,14 @@ export interface GroupQueryResponse { tasks?: TaskQueryResponse[]; } +export type ValidationErrorCtx = { [key: string]: unknown }; + export interface ValidationError { loc: (string | number)[]; msg: string; type: string; + input?: unknown; + ctx?: ValidationErrorCtx; } export interface HTTPValidationError { @@ -612,6 +655,48 @@ export interface JwtTokenResponse { error?: string | null; } +/** + * Per-key policy strictness: 'off' skips checking, 'warn' surfaces + * missing or unlisted values as submission warnings, and 'enforce' + * rejects them. + */ +export type LabelEnforcement = (typeof LabelEnforcement)[keyof typeof LabelEnforcement]; + +export const LabelEnforcement = { + off: "off", + warn: "warn", + enforce: "enforce", +} as const; + +/** + * Configuration for one admin-designated workflow label key. + * + * An empty allow_list accepts any well-formed value; enforcement then + * applies only to the key being present. + */ +export interface LabelPolicy { + key: string; + allow_list?: string[]; + enforcement?: LabelEnforcement; + assert_message?: string; +} + +/** + * Curated workflow label policy; empty by default, so no policy + * applies until configured. + */ +export interface LabelsConfigInput { + policy?: LabelPolicy[]; +} + +/** + * Curated workflow label policy; empty by default, so no policy + * applies until configured. + */ +export interface LabelsConfigOutput { + policy?: LabelPolicy[]; +} + /** * Object storing info for all backends. */ @@ -755,6 +840,23 @@ export interface NotificationConfig { smtp_settings?: SMTPConfig; } +/** + * Type of configs supported by config history mutations. + */ +export type OperableConfigHistoryType = (typeof OperableConfigHistoryType)[keyof typeof OperableConfigHistoryType]; + +export const OperableConfigHistoryType = { + SERVICE: "SERVICE", + WORKFLOW: "WORKFLOW", + BACKEND: "BACKEND", + POOL: "POOL", + POD_TEMPLATE: "POD_TEMPLATE", + GROUP_TEMPLATE: "GROUP_TEMPLATE", + RESOURCE_VALIDATION: "RESOURCE_VALIDATION", + BACKEND_TEST: "BACKEND_TEST", + ROLE: "ROLE", +} as const; + export type OperatorType = (typeof OperatorType)[keyof typeof OperatorType]; export const OperatorType = { @@ -779,7 +881,7 @@ export interface RegistryCredential { /** * Dynamic Config for storing the image URLs for service images and the credentials needed -to pull them. + * to pull them. */ export interface OsmoImageConfig { init?: string; @@ -1156,7 +1258,7 @@ export interface TokenIdentity { /** * Profile and identity info. When token header is set, roles/pools are the -token's; otherwise they are the user's. JSON is self-explanatory for CLI. + * token's; otherwise they are the user's. JSON is self-explanatory for CLI. */ export interface ProfileResponse { profile: UserProfile; @@ -1286,13 +1388,13 @@ export interface PutResourceValidationsRequest { /** * Single Role Policy Entry. - -Contains a list of actions (semantic format "resource:Action") and optional -resources the policy applies to. If effect is Deny and the policy matches, -access is denied even if another policy allows it. - -Actions are validated via regex; API/DB still use [{"action": "..."}] for -compatibility with the Go authz_sidecar. + * + * Contains a list of actions (semantic format "resource:Action") and optional + * resources the policy applies to. If effect is Deny and the policy matches, + * access is denied even if another policy allows it. + * + * Actions are validated via regex; API/DB still use [{"action": "..."}] for + * compatibility with the Go authz_sidecar. */ export interface RolePolicy { effect?: PolicyEffect; @@ -1302,10 +1404,10 @@ export interface RolePolicy { /** * Sync mode for role assignments. - -- FORCE: Always apply this role to all users (e.g., for system roles) -- IMPORT: Role is imported from IDP claims or user_roles table (default) -- IGNORE: Ignore this role in IDP sync (role is managed manually) + * + * - FORCE: Always apply this role to all users (e.g., for system roles) + * - IMPORT: Role is imported from IDP claims or user_roles table (default) + * - IGNORE: Ignore this role in IDP sync (role is managed manually) */ export type SyncMode = (typeof SyncMode)[keyof typeof SyncMode]; @@ -1317,9 +1419,9 @@ export const SyncMode = { /** * Single Role Entry. - -Note: Authorization checking is now handled by the authz_sidecar (Go service). -This Python class is only used for role CRUD operations. + * + * Note: Authorization checking is now handled by the authz_sidecar (Go service). + * This Python class is only used for role CRUD operations. */ export interface RoleInput { name: string; @@ -1378,7 +1480,7 @@ export interface WorkflowInfo { /** * Stores workflow limits per user. Default is None, which means no limit. -If a limit is set, it must be greater than 0. + * If a limit is set, it must be greater than 0. */ export interface UserWorkflowLimitConfig { max_num_workflows?: number | null; @@ -1401,6 +1503,7 @@ export interface WorkflowConfigInput { credential_config?: CredentialConfig; user_workflow_limits?: UserWorkflowLimitConfig; plugins_config?: PluginsConfigInput; + labels_config?: LabelsConfigInput; max_num_tasks?: number; max_num_ports_per_task?: number; max_retry_per_task?: number; @@ -1497,9 +1600,9 @@ export interface ResourcesResponse { /** * Single Role Entry. - -Note: Authorization checking is now handled by the authz_sidecar (Go service). -This Python class is only used for role CRUD operations. + * + * Note: Authorization checking is now handled by the authz_sidecar (Go service). + * This Python class is only used for role CRUD operations. */ export interface RoleOutput { name: string; @@ -1526,7 +1629,7 @@ export interface RoleUsersResponse { export interface RollbackConfigRequest { description?: string | null; tags?: string[] | null; - config_type: SrcLibUtilsConfigHistoryConfigHistoryType; + config_type: OperableConfigHistoryType; /** * Revision to roll back to * @exclusiveMinimum 0 @@ -1563,6 +1666,7 @@ export interface SubmitResponse { logs?: string | null; spec?: string | null; dashboard_url?: string | null; + warnings?: string[]; } /** @@ -1696,6 +1800,7 @@ export interface WorkflowConfigOutput { credential_config?: CredentialConfig; user_workflow_limits?: UserWorkflowLimitConfig; plugins_config?: PluginsConfigOutput; + labels_config?: LabelsConfigOutput; max_num_tasks?: number; max_num_ports_per_task?: number; max_retry_per_task?: number; @@ -1731,6 +1836,8 @@ export const WorkflowPriority = { LOW: "LOW", } as const; +export type WorkflowQueryResponseLabels = { [key: string]: string }; + /** * Represents the status of a workflow. */ @@ -1790,6 +1897,8 @@ export interface WorkflowQueryResponse { app_version?: number | null; plugins: WorkflowPlugins; priority: string; + labels?: WorkflowQueryResponseLabels; + warnings?: string[]; } export interface SrcServiceCoreAppObjectsListEntry { @@ -1806,6 +1915,8 @@ export interface SrcServiceCoreAppObjectsListResponse { more_entries: boolean; } +export type SrcServiceCoreWorkflowObjectsListEntryLabels = { [key: string]: string }; + /** * Entry for list API results. */ @@ -1829,6 +1940,7 @@ export interface SrcServiceCoreWorkflowObjectsListEntry { app_name?: string | null; app_version?: number | null; priority: string; + labels?: SrcServiceCoreWorkflowObjectsListEntryLabels; } export interface SrcServiceCoreWorkflowObjectsListResponse { @@ -1893,7 +2005,7 @@ export type GetConfigsHistoryApiConfigsHistoryGetParams = { /** * Filter by config types */ - config_types?: SrcLibUtilsConfigHistoryConfigHistoryType[] | null; + config_types?: ConfigHistoryType[] | null; /** * Filter by config name */ @@ -1925,7 +2037,7 @@ export type GetConfigsHistoryApiConfigsHistoryGetParams = { }; export type GetConfigDiffApiConfigsDiffGetParams = { - config_type: SrcLibUtilsConfigHistoryConfigHistoryType; + config_type: ConfigHistoryType; /** * First revision to compare * @exclusiveMinimum 0 @@ -2026,6 +2138,14 @@ export type ListWorkflowApiWorkflowGetParams = { tags?: string[] | null; app?: string | null; priority?: WorkflowPriority[] | null; + /** + * Workflow label selector: key=value with optional * wildcards and (a|b) alternatives, for example key=(team_*|osmo_*) or key=team_(a|b). Repeat for AND semantics. + */ + label?: string[] | null; + /** + * Label key that must be absent from the workflow; workflows without any labels match. Repeat for AND semantics. + */ + no_label?: string[] | null; }; export type ListTaskApiTaskGetParams = { @@ -2123,6 +2243,7 @@ export type SubmitWorkflowApiPoolPoolNameWorkflowPostParams = { validation_only?: boolean; priority?: WorkflowPriority; env_vars?: string[]; + label?: string[]; }; export type SetNotificationSettingsApiProfileSettingsPostParams = { @@ -2133,10 +2254,6 @@ export type HealthHealthGet200 = { [key: string]: string }; export type GetAvailableWorkflowTagsApiTagGet200 = { [key: string]: string[] }; -/** - * Read all the service configurations - * @summary Read Service Configs - */ export type readServiceConfigsApiConfigsServiceGetResponse200 = { data: ServiceConfigOutput; status: 200; @@ -2152,6 +2269,10 @@ export const getReadServiceConfigsApiConfigsServiceGetUrl = () => { return `/api/configs/service`; }; +/** + * Read all the service configurations + * @summary Read Service Configs + */ export const readServiceConfigsApiConfigsServiceGet = async ( options?: RequestInit, ): Promise => { @@ -2166,10 +2287,6 @@ export const readServiceConfigsApiConfigsServiceGet = async ( return { data, status: res.status, headers: res.headers } as readServiceConfigsApiConfigsServiceGetResponse; }; -/** - * Put service configurations - * @summary Put Service Configs - */ export type putServiceConfigsApiConfigsServicePutResponse200 = { data: PutServiceConfigsApiConfigsServicePut200; status: 200; @@ -2195,6 +2312,10 @@ export const getPutServiceConfigsApiConfigsServicePutUrl = () => { return `/api/configs/service`; }; +/** + * Put service configurations + * @summary Put Service Configs + */ export const putServiceConfigsApiConfigsServicePut = async ( putServiceRequest: PutServiceRequest, options?: RequestInit, @@ -2212,10 +2333,6 @@ export const putServiceConfigsApiConfigsServicePut = async ( return { data, status: res.status, headers: res.headers } as putServiceConfigsApiConfigsServicePutResponse; }; -/** - * Patch service configurations - * @summary Patch Service Configs - */ export type patchServiceConfigsApiConfigsServicePatchResponse200 = { data: PatchServiceConfigsApiConfigsServicePatch200; status: 200; @@ -2243,6 +2360,10 @@ export const getPatchServiceConfigsApiConfigsServicePatchUrl = () => { return `/api/configs/service`; }; +/** + * Patch service configurations + * @summary Patch Service Configs + */ export const patchServiceConfigsApiConfigsServicePatch = async ( patchConfigRequest: PatchConfigRequest, options?: RequestInit, @@ -2260,10 +2381,6 @@ export const patchServiceConfigsApiConfigsServicePatch = async ( return { data, status: res.status, headers: res.headers } as patchServiceConfigsApiConfigsServicePatchResponse; }; -/** - * Read all the workflow configurations - * @summary Read Workflow Configs - */ export type readWorkflowConfigsApiConfigsWorkflowGetResponse200 = { data: WorkflowConfigOutput; status: 200; @@ -2279,6 +2396,10 @@ export const getReadWorkflowConfigsApiConfigsWorkflowGetUrl = () => { return `/api/configs/workflow`; }; +/** + * Read all the workflow configurations + * @summary Read Workflow Configs + */ export const readWorkflowConfigsApiConfigsWorkflowGet = async ( options?: RequestInit, ): Promise => { @@ -2293,10 +2414,6 @@ export const readWorkflowConfigsApiConfigsWorkflowGet = async ( return { data, status: res.status, headers: res.headers } as readWorkflowConfigsApiConfigsWorkflowGetResponse; }; -/** - * Put workflow configurations - * @summary Put Workflow Configs - */ export type putWorkflowConfigsApiConfigsWorkflowPutResponse200 = { data: PutWorkflowConfigsApiConfigsWorkflowPut200; status: 200; @@ -2324,6 +2441,10 @@ export const getPutWorkflowConfigsApiConfigsWorkflowPutUrl = () => { return `/api/configs/workflow`; }; +/** + * Put workflow configurations + * @summary Put Workflow Configs + */ export const putWorkflowConfigsApiConfigsWorkflowPut = async ( putWorkflowRequest: PutWorkflowRequest, options?: RequestInit, @@ -2341,10 +2462,6 @@ export const putWorkflowConfigsApiConfigsWorkflowPut = async ( return { data, status: res.status, headers: res.headers } as putWorkflowConfigsApiConfigsWorkflowPutResponse; }; -/** - * Patch workflow configurations - * @summary Patch Workflow Configs - */ export type patchWorkflowConfigsApiConfigsWorkflowPatchResponse200 = { data: PatchWorkflowConfigsApiConfigsWorkflowPatch200; status: 200; @@ -2372,6 +2489,10 @@ export const getPatchWorkflowConfigsApiConfigsWorkflowPatchUrl = () => { return `/api/configs/workflow`; }; +/** + * Patch workflow configurations + * @summary Patch Workflow Configs + */ export const patchWorkflowConfigsApiConfigsWorkflowPatch = async ( patchConfigRequest: PatchConfigRequest, options?: RequestInit, @@ -2389,10 +2510,6 @@ export const patchWorkflowConfigsApiConfigsWorkflowPatch = async ( return { data, status: res.status, headers: res.headers } as patchWorkflowConfigsApiConfigsWorkflowPatchResponse; }; -/** - * List all backends. - * @summary List Backends - */ export type listBackendsApiConfigsBackendGetResponse200 = { data: ListBackendsResponse; status: 200; @@ -2407,6 +2524,10 @@ export const getListBackendsApiConfigsBackendGetUrl = () => { return `/api/configs/backend`; }; +/** + * List all backends. + * @summary List Backends + */ export const listBackendsApiConfigsBackendGet = async ( options?: RequestInit, ): Promise => { @@ -2421,10 +2542,6 @@ export const listBackendsApiConfigsBackendGet = async ( return { data, status: res.status, headers: res.headers } as listBackendsApiConfigsBackendGetResponse; }; -/** - * Override the config for a specific backend. - * @summary Update Backend - */ export type updateBackendApiConfigsBackendNamePostResponse200 = { data: unknown; status: 200; @@ -2451,6 +2568,10 @@ export const getUpdateBackendApiConfigsBackendNamePostUrl = (name: string) => { return `/api/configs/backend/${name}`; }; +/** + * Override the config for a specific backend. + * @summary Update Backend + */ export const updateBackendApiConfigsBackendNamePost = async ( name: string, postBackendRequest: PostBackendRequest, @@ -2469,10 +2590,6 @@ export const updateBackendApiConfigsBackendNamePost = async ( return { data, status: res.status, headers: res.headers } as updateBackendApiConfigsBackendNamePostResponse; }; -/** - * Get info for a specific backend. - * @summary Get Backend - */ export type getBackendApiConfigsBackendNameGetResponse200 = { data: Backend; status: 200; @@ -2498,6 +2615,10 @@ export const getGetBackendApiConfigsBackendNameGetUrl = (name: string) => { return `/api/configs/backend/${name}`; }; +/** + * Get info for a specific backend. + * @summary Get Backend + */ export const getBackendApiConfigsBackendNameGet = async ( name: string, options?: RequestInit, @@ -2513,10 +2634,6 @@ export const getBackendApiConfigsBackendNameGet = async ( return { data, status: res.status, headers: res.headers } as getBackendApiConfigsBackendNameGetResponse; }; -/** - * Remove a backend. - * @summary Delete Backend - */ export type deleteBackendApiConfigsBackendNameDeleteResponse200 = { data: unknown; status: 200; @@ -2544,6 +2661,10 @@ export const getDeleteBackendApiConfigsBackendNameDeleteUrl = (name: string) => return `/api/configs/backend/${name}`; }; +/** + * Remove a backend. + * @summary Delete Backend + */ export const deleteBackendApiConfigsBackendNameDelete = async ( name: string, deleteBackendRequest: DeleteBackendRequest, @@ -2562,10 +2683,6 @@ export const deleteBackendApiConfigsBackendNameDelete = async ( return { data, status: res.status, headers: res.headers } as deleteBackendApiConfigsBackendNameDeleteResponse; }; -/** - * List all Pools - * @summary List Pools - */ export type listPoolsApiConfigsPoolGetResponse200 = { data: VerbosePoolConfig | EditablePoolConfig; status: 200; @@ -2592,7 +2709,7 @@ export const getListPoolsApiConfigsPoolGetUrl = (params?: ListPoolsApiConfigsPoo Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -2601,6 +2718,10 @@ export const getListPoolsApiConfigsPoolGetUrl = (params?: ListPoolsApiConfigsPoo return stringifiedParams.length > 0 ? `/api/configs/pool?${stringifiedParams}` : `/api/configs/pool`; }; +/** + * List all Pools + * @summary List Pools + */ export const listPoolsApiConfigsPoolGet = async ( params?: ListPoolsApiConfigsPoolGetParams, options?: RequestInit, @@ -2616,10 +2737,6 @@ export const listPoolsApiConfigsPoolGet = async ( return { data, status: res.status, headers: res.headers } as listPoolsApiConfigsPoolGetResponse; }; -/** - * Put Pool configurations - * @summary Put Pools - */ export type putPoolsApiConfigsPoolPutResponse200 = { data: unknown; status: 200; @@ -2645,6 +2762,10 @@ export const getPutPoolsApiConfigsPoolPutUrl = () => { return `/api/configs/pool`; }; +/** + * Put Pool configurations + * @summary Put Pools + */ export const putPoolsApiConfigsPoolPut = async ( putPoolsRequest: PutPoolsRequest, options?: RequestInit, @@ -2662,13 +2783,6 @@ export const putPoolsApiConfigsPoolPut = async ( return { data, status: res.status, headers: res.headers } as putPoolsApiConfigsPoolPutResponse; }; -/** - * Read Pool configuration - -Return type Any to prevent unwanted artifacts between Pool and PoolEditable outputs -Should return Pool or PoolEditable objects - * @summary Read Pool - */ export type readPoolApiConfigsPoolNameGetResponse200 = { data: PoolOutput | PoolEditable; status: 200; @@ -2695,7 +2809,7 @@ export const getReadPoolApiConfigsPoolNameGetUrl = (name: string, params?: ReadP Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -2704,6 +2818,13 @@ export const getReadPoolApiConfigsPoolNameGetUrl = (name: string, params?: ReadP return stringifiedParams.length > 0 ? `/api/configs/pool/${name}?${stringifiedParams}` : `/api/configs/pool/${name}`; }; +/** + * Read Pool configuration + * + * Return type Any to prevent unwanted artifacts between Pool and PoolEditable outputs + * Should return Pool or PoolEditable objects + * @summary Read Pool + */ export const readPoolApiConfigsPoolNameGet = async ( name: string, params?: ReadPoolApiConfigsPoolNameGetParams, @@ -2720,10 +2841,6 @@ export const readPoolApiConfigsPoolNameGet = async ( return { data, status: res.status, headers: res.headers } as readPoolApiConfigsPoolNameGetResponse; }; -/** - * Put Pool configurations - * @summary Put Pool - */ export type putPoolApiConfigsPoolNamePutResponse200 = { data: unknown; status: 200; @@ -2749,6 +2866,10 @@ export const getPutPoolApiConfigsPoolNamePutUrl = (name: string) => { return `/api/configs/pool/${name}`; }; +/** + * Put Pool configurations + * @summary Put Pool + */ export const putPoolApiConfigsPoolNamePut = async ( name: string, putPoolRequest: PutPoolRequest, @@ -2767,10 +2888,6 @@ export const putPoolApiConfigsPoolNamePut = async ( return { data, status: res.status, headers: res.headers } as putPoolApiConfigsPoolNamePutResponse; }; -/** - * Patch Pool configurations - * @summary Patch Pool - */ export type patchPoolApiConfigsPoolNamePatchResponse200 = { data: unknown; status: 200; @@ -2796,6 +2913,10 @@ export const getPatchPoolApiConfigsPoolNamePatchUrl = (name: string) => { return `/api/configs/pool/${name}`; }; +/** + * Patch Pool configurations + * @summary Patch Pool + */ export const patchPoolApiConfigsPoolNamePatch = async ( name: string, patchPoolRequest: PatchPoolRequest, @@ -2814,10 +2935,6 @@ export const patchPoolApiConfigsPoolNamePatch = async ( return { data, status: res.status, headers: res.headers } as patchPoolApiConfigsPoolNamePatchResponse; }; -/** - * Delete Pool configurations - * @summary Delete Pool - */ export type deletePoolApiConfigsPoolNameDeleteResponse200 = { data: unknown; status: 200; @@ -2843,6 +2960,10 @@ export const getDeletePoolApiConfigsPoolNameDeleteUrl = (name: string) => { return `/api/configs/pool/${name}`; }; +/** + * Delete Pool configurations + * @summary Delete Pool + */ export const deletePoolApiConfigsPoolNameDelete = async ( name: string, configsRequest: ConfigsRequest, @@ -2861,10 +2982,6 @@ export const deletePoolApiConfigsPoolNameDelete = async ( return { data, status: res.status, headers: res.headers } as deletePoolApiConfigsPoolNameDeleteResponse; }; -/** - * Rename Pool - * @summary Rename Pool - */ export type renamePoolApiConfigsPoolNameRenamePutResponse200 = { data: unknown; status: 200; @@ -2890,6 +3007,10 @@ export const getRenamePoolApiConfigsPoolNameRenamePutUrl = (name: string) => { return `/api/configs/pool/${name}/rename`; }; +/** + * Rename Pool + * @summary Rename Pool + */ export const renamePoolApiConfigsPoolNameRenamePut = async ( name: string, renamePoolRequest: RenamePoolRequest, @@ -2908,10 +3029,6 @@ export const renamePoolApiConfigsPoolNameRenamePut = async ( return { data, status: res.status, headers: res.headers } as renamePoolApiConfigsPoolNameRenamePutResponse; }; -/** - * List all Platforms - * @summary List Platforms In Pool - */ export type listPlatformsInPoolApiConfigsPoolNamePlatformGetResponse200 = { data: ListPlatformsInPoolApiConfigsPoolNamePlatformGet200; status: 200; @@ -2943,7 +3060,7 @@ export const getListPlatformsInPoolApiConfigsPoolNamePlatformGetUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -2954,6 +3071,10 @@ export const getListPlatformsInPoolApiConfigsPoolNamePlatformGetUrl = ( : `/api/configs/pool/${name}/platform`; }; +/** + * List all Platforms + * @summary List Platforms In Pool + */ export const listPlatformsInPoolApiConfigsPoolNamePlatformGet = async ( name: string, params?: ListPlatformsInPoolApiConfigsPoolNamePlatformGetParams, @@ -2970,10 +3091,6 @@ export const listPlatformsInPoolApiConfigsPoolNamePlatformGet = async ( return { data, status: res.status, headers: res.headers } as listPlatformsInPoolApiConfigsPoolNamePlatformGetResponse; }; -/** - * Read Platform - * @summary Read Platform In Pool - */ export type readPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGetResponse200 = { data: PlatformMinimal | PlatformEditable | PlatformOutput; status: 200; @@ -3006,7 +3123,7 @@ export const getReadPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGetUrl = Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -3017,6 +3134,10 @@ export const getReadPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGetUrl = : `/api/configs/pool/${name}/platform/${platformName}`; }; +/** + * Read Platform + * @summary Read Platform In Pool + */ export const readPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGet = async ( name: string, platformName: string, @@ -3043,10 +3164,6 @@ export const readPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGet = async } as readPlatformInPoolApiConfigsPoolNamePlatformPlatformNameGetResponse; }; -/** - * Put Platform configurations - * @summary Put Platform In Pool - */ export type putPlatformInPoolApiConfigsPoolNamePlatformPlatformNamePutResponse200 = { data: unknown; status: 200; @@ -3077,6 +3194,10 @@ export const getPutPlatformInPoolApiConfigsPoolNamePlatformPlatformNamePutUrl = return `/api/configs/pool/${name}/platform/${platformName}`; }; +/** + * Put Platform configurations + * @summary Put Platform In Pool + */ export const putPlatformInPoolApiConfigsPoolNamePlatformPlatformNamePut = async ( name: string, platformName: string, @@ -3100,10 +3221,6 @@ export const putPlatformInPoolApiConfigsPoolNamePlatformPlatformNamePut = async } as putPlatformInPoolApiConfigsPoolNamePlatformPlatformNamePutResponse; }; -/** - * Rename Platform - * @summary Rename Platform In Pool - */ export type renamePlatformInPoolApiConfigsPoolNamePlatformPlatformNameRenamePutResponse200 = { data: unknown; status: 200; @@ -3134,6 +3251,10 @@ export const getRenamePlatformInPoolApiConfigsPoolNamePlatformPlatformNameRename return `/api/configs/pool/${name}/platform/${platformName}/rename`; }; +/** + * Rename Platform + * @summary Rename Platform In Pool + */ export const renamePlatformInPoolApiConfigsPoolNamePlatformPlatformNameRenamePut = async ( name: string, platformName: string, @@ -3162,10 +3283,6 @@ export const renamePlatformInPoolApiConfigsPoolNamePlatformPlatformNameRenamePut } as renamePlatformInPoolApiConfigsPoolNamePlatformPlatformNameRenamePutResponse; }; -/** - * List all Pod Template configurations - * @summary List Pod Templates - */ export type listPodTemplatesApiConfigsPodTemplateGetResponse200 = { data: ListPodTemplatesApiConfigsPodTemplateGet200; status: 200; @@ -3181,6 +3298,10 @@ export const getListPodTemplatesApiConfigsPodTemplateGetUrl = () => { return `/api/configs/pod_template`; }; +/** + * List all Pod Template configurations + * @summary List Pod Templates + */ export const listPodTemplatesApiConfigsPodTemplateGet = async ( options?: RequestInit, ): Promise => { @@ -3195,10 +3316,6 @@ export const listPodTemplatesApiConfigsPodTemplateGet = async ( return { data, status: res.status, headers: res.headers } as listPodTemplatesApiConfigsPodTemplateGetResponse; }; -/** - * Set Dict of Pod Templates configurations - * @summary Put Pod Templates - */ export type putPodTemplatesApiConfigsPodTemplatePutResponse200 = { data: unknown; status: 200; @@ -3226,6 +3343,10 @@ export const getPutPodTemplatesApiConfigsPodTemplatePutUrl = () => { return `/api/configs/pod_template`; }; +/** + * Set Dict of Pod Templates configurations + * @summary Put Pod Templates + */ export const putPodTemplatesApiConfigsPodTemplatePut = async ( putPodTemplatesRequest: PutPodTemplatesRequest, options?: RequestInit, @@ -3243,10 +3364,6 @@ export const putPodTemplatesApiConfigsPodTemplatePut = async ( return { data, status: res.status, headers: res.headers } as putPodTemplatesApiConfigsPodTemplatePutResponse; }; -/** - * Read Pod Template configurations - * @summary Read Pod Template - */ export type readPodTemplateApiConfigsPodTemplateNameGetResponse200 = { data: ReadPodTemplateApiConfigsPodTemplateNameGet200; status: 200; @@ -3274,6 +3391,10 @@ export const getReadPodTemplateApiConfigsPodTemplateNameGetUrl = (name: string) return `/api/configs/pod_template/${name}`; }; +/** + * Read Pod Template configurations + * @summary Read Pod Template + */ export const readPodTemplateApiConfigsPodTemplateNameGet = async ( name: string, options?: RequestInit, @@ -3289,10 +3410,6 @@ export const readPodTemplateApiConfigsPodTemplateNameGet = async ( return { data, status: res.status, headers: res.headers } as readPodTemplateApiConfigsPodTemplateNameGetResponse; }; -/** - * Put Pod Template configurations - * @summary Put Pod Template - */ export type putPodTemplateApiConfigsPodTemplateNamePutResponse200 = { data: unknown; status: 200; @@ -3320,6 +3437,10 @@ export const getPutPodTemplateApiConfigsPodTemplateNamePutUrl = (name: string) = return `/api/configs/pod_template/${name}`; }; +/** + * Put Pod Template configurations + * @summary Put Pod Template + */ export const putPodTemplateApiConfigsPodTemplateNamePut = async ( name: string, putPodTemplateRequest: PutPodTemplateRequest, @@ -3338,10 +3459,6 @@ export const putPodTemplateApiConfigsPodTemplateNamePut = async ( return { data, status: res.status, headers: res.headers } as putPodTemplateApiConfigsPodTemplateNamePutResponse; }; -/** - * Delete Pod Template configurations - * @summary Delete Pod Template - */ export type deletePodTemplateApiConfigsPodTemplateNameDeleteResponse200 = { data: unknown; status: 200; @@ -3369,6 +3486,10 @@ export const getDeletePodTemplateApiConfigsPodTemplateNameDeleteUrl = (name: str return `/api/configs/pod_template/${name}`; }; +/** + * Delete Pod Template configurations + * @summary Delete Pod Template + */ export const deletePodTemplateApiConfigsPodTemplateNameDelete = async ( name: string, configsRequest: ConfigsRequest, @@ -3387,10 +3508,6 @@ export const deletePodTemplateApiConfigsPodTemplateNameDelete = async ( return { data, status: res.status, headers: res.headers } as deletePodTemplateApiConfigsPodTemplateNameDeleteResponse; }; -/** - * List all Group Template configurations - * @summary List Group Templates - */ export type listGroupTemplatesApiConfigsGroupTemplateGetResponse200 = { data: ListGroupTemplatesApiConfigsGroupTemplateGet200; status: 200; @@ -3407,6 +3524,10 @@ export const getListGroupTemplatesApiConfigsGroupTemplateGetUrl = () => { return `/api/configs/group_template`; }; +/** + * List all Group Template configurations + * @summary List Group Templates + */ export const listGroupTemplatesApiConfigsGroupTemplateGet = async ( options?: RequestInit, ): Promise => { @@ -3421,10 +3542,6 @@ export const listGroupTemplatesApiConfigsGroupTemplateGet = async ( return { data, status: res.status, headers: res.headers } as listGroupTemplatesApiConfigsGroupTemplateGetResponse; }; -/** - * Set Dict of Group Templates configurations - * @summary Put Group Templates - */ export type putGroupTemplatesApiConfigsGroupTemplatePutResponse200 = { data: unknown; status: 200; @@ -3452,6 +3569,10 @@ export const getPutGroupTemplatesApiConfigsGroupTemplatePutUrl = () => { return `/api/configs/group_template`; }; +/** + * Set Dict of Group Templates configurations + * @summary Put Group Templates + */ export const putGroupTemplatesApiConfigsGroupTemplatePut = async ( putGroupTemplatesRequest: PutGroupTemplatesRequest, options?: RequestInit, @@ -3469,10 +3590,6 @@ export const putGroupTemplatesApiConfigsGroupTemplatePut = async ( return { data, status: res.status, headers: res.headers } as putGroupTemplatesApiConfigsGroupTemplatePutResponse; }; -/** - * Read Group Template configurations - * @summary Read Group Template - */ export type readGroupTemplateApiConfigsGroupTemplateNameGetResponse200 = { data: ReadGroupTemplateApiConfigsGroupTemplateNameGet200; status: 200; @@ -3500,6 +3617,10 @@ export const getReadGroupTemplateApiConfigsGroupTemplateNameGetUrl = (name: stri return `/api/configs/group_template/${name}`; }; +/** + * Read Group Template configurations + * @summary Read Group Template + */ export const readGroupTemplateApiConfigsGroupTemplateNameGet = async ( name: string, options?: RequestInit, @@ -3515,10 +3636,6 @@ export const readGroupTemplateApiConfigsGroupTemplateNameGet = async ( return { data, status: res.status, headers: res.headers } as readGroupTemplateApiConfigsGroupTemplateNameGetResponse; }; -/** - * Put Group Template configurations - * @summary Put Group Template - */ export type putGroupTemplateApiConfigsGroupTemplateNamePutResponse200 = { data: unknown; status: 200; @@ -3546,6 +3663,10 @@ export const getPutGroupTemplateApiConfigsGroupTemplateNamePutUrl = (name: strin return `/api/configs/group_template/${name}`; }; +/** + * Put Group Template configurations + * @summary Put Group Template + */ export const putGroupTemplateApiConfigsGroupTemplateNamePut = async ( name: string, putGroupTemplateRequest: PutGroupTemplateRequest, @@ -3564,10 +3685,6 @@ export const putGroupTemplateApiConfigsGroupTemplateNamePut = async ( return { data, status: res.status, headers: res.headers } as putGroupTemplateApiConfigsGroupTemplateNamePutResponse; }; -/** - * Delete Group Template configurations - * @summary Delete Group Template - */ export type deleteGroupTemplateApiConfigsGroupTemplateNameDeleteResponse200 = { data: unknown; status: 200; @@ -3595,6 +3712,10 @@ export const getDeleteGroupTemplateApiConfigsGroupTemplateNameDeleteUrl = (name: return `/api/configs/group_template/${name}`; }; +/** + * Delete Group Template configurations + * @summary Delete Group Template + */ export const deleteGroupTemplateApiConfigsGroupTemplateNameDelete = async ( name: string, configsRequest: ConfigsRequest, @@ -3617,10 +3738,6 @@ export const deleteGroupTemplateApiConfigsGroupTemplateNameDelete = async ( } as deleteGroupTemplateApiConfigsGroupTemplateNameDeleteResponse; }; -/** - * List all Resource Validation configurations - * @summary List Resource Validations - */ export type listResourceValidationsApiConfigsResourceValidationGetResponse200 = { data: ListResourceValidationsApiConfigsResourceValidationGet200; status: 200; @@ -3637,6 +3754,10 @@ export const getListResourceValidationsApiConfigsResourceValidationGetUrl = () = return `/api/configs/resource_validation`; }; +/** + * List all Resource Validation configurations + * @summary List Resource Validations + */ export const listResourceValidationsApiConfigsResourceValidationGet = async ( options?: RequestInit, ): Promise => { @@ -3655,10 +3776,6 @@ export const listResourceValidationsApiConfigsResourceValidationGet = async ( } as listResourceValidationsApiConfigsResourceValidationGetResponse; }; -/** - * Put Resource Validation configurations - * @summary Put Resource Validations - */ export type putResourceValidationsApiConfigsResourceValidationPutResponse200 = { data: unknown; status: 200; @@ -3686,6 +3803,10 @@ export const getPutResourceValidationsApiConfigsResourceValidationPutUrl = () => return `/api/configs/resource_validation`; }; +/** + * Put Resource Validation configurations + * @summary Put Resource Validations + */ export const putResourceValidationsApiConfigsResourceValidationPut = async ( putResourceValidationsRequest: PutResourceValidationsRequest, options?: RequestInit, @@ -3707,10 +3828,6 @@ export const putResourceValidationsApiConfigsResourceValidationPut = async ( } as putResourceValidationsApiConfigsResourceValidationPutResponse; }; -/** - * Read Resource Validation configurations - * @summary Read Resource Validation - */ export type readResourceValidationApiConfigsResourceValidationNameGetResponse200 = { data: ResourceAssertion[]; status: 200; @@ -3738,6 +3855,10 @@ export const getReadResourceValidationApiConfigsResourceValidationNameGetUrl = ( return `/api/configs/resource_validation/${name}`; }; +/** + * Read Resource Validation configurations + * @summary Read Resource Validation + */ export const readResourceValidationApiConfigsResourceValidationNameGet = async ( name: string, options?: RequestInit, @@ -3757,10 +3878,6 @@ export const readResourceValidationApiConfigsResourceValidationNameGet = async ( } as readResourceValidationApiConfigsResourceValidationNameGetResponse; }; -/** - * Put Resource Validation configurations - * @summary Put Resource Validation - */ export type putResourceValidationApiConfigsResourceValidationNamePutResponse200 = { data: unknown; status: 200; @@ -3788,6 +3905,10 @@ export const getPutResourceValidationApiConfigsResourceValidationNamePutUrl = (n return `/api/configs/resource_validation/${name}`; }; +/** + * Put Resource Validation configurations + * @summary Put Resource Validation + */ export const putResourceValidationApiConfigsResourceValidationNamePut = async ( name: string, putResourceValidationRequest: PutResourceValidationRequest, @@ -3810,10 +3931,6 @@ export const putResourceValidationApiConfigsResourceValidationNamePut = async ( } as putResourceValidationApiConfigsResourceValidationNamePutResponse; }; -/** - * Delete Resource Validation configurations - * @summary Delete Resource Validation - */ export type deleteResourceValidationApiConfigsResourceValidationNameDeleteResponse200 = { data: unknown; status: 200; @@ -3841,6 +3958,10 @@ export const getDeleteResourceValidationApiConfigsResourceValidationNameDeleteUr return `/api/configs/resource_validation/${name}`; }; +/** + * Delete Resource Validation configurations + * @summary Delete Resource Validation + */ export const deleteResourceValidationApiConfigsResourceValidationNameDelete = async ( name: string, configsRequest: ConfigsRequest, @@ -3865,10 +3986,6 @@ export const deleteResourceValidationApiConfigsResourceValidationNameDelete = as } as deleteResourceValidationApiConfigsResourceValidationNameDeleteResponse; }; -/** - * List all Roles - * @summary List Roles - */ export type listRolesApiConfigsRoleGetResponse200 = { data: RoleOutput[]; status: 200; @@ -3883,6 +4000,10 @@ export const getListRolesApiConfigsRoleGetUrl = () => { return `/api/configs/role`; }; +/** + * List all Roles + * @summary List Roles + */ export const listRolesApiConfigsRoleGet = async ( options?: RequestInit, ): Promise => { @@ -3897,10 +4018,6 @@ export const listRolesApiConfigsRoleGet = async ( return { data, status: res.status, headers: res.headers } as listRolesApiConfigsRoleGetResponse; }; -/** - * Put Roles - * @summary Put Roles - */ export type putRolesApiConfigsRolePutResponse200 = { data: unknown; status: 200; @@ -3926,6 +4043,10 @@ export const getPutRolesApiConfigsRolePutUrl = () => { return `/api/configs/role`; }; +/** + * Put Roles + * @summary Put Roles + */ export const putRolesApiConfigsRolePut = async ( putRolesRequest: PutRolesRequest, options?: RequestInit, @@ -3943,10 +4064,6 @@ export const putRolesApiConfigsRolePut = async ( return { data, status: res.status, headers: res.headers } as putRolesApiConfigsRolePutResponse; }; -/** - * Read Role - * @summary Read Role - */ export type readRoleApiConfigsRoleNameGetResponse200 = { data: RoleOutput; status: 200; @@ -3972,6 +4089,10 @@ export const getReadRoleApiConfigsRoleNameGetUrl = (name: string) => { return `/api/configs/role/${name}`; }; +/** + * Read Role + * @summary Read Role + */ export const readRoleApiConfigsRoleNameGet = async ( name: string, options?: RequestInit, @@ -3987,10 +4108,6 @@ export const readRoleApiConfigsRoleNameGet = async ( return { data, status: res.status, headers: res.headers } as readRoleApiConfigsRoleNameGetResponse; }; -/** - * Patch Role configurations - * @summary Put Role - */ export type putRoleApiConfigsRoleNamePutResponse200 = { data: unknown; status: 200; @@ -4016,6 +4133,10 @@ export const getPutRoleApiConfigsRoleNamePutUrl = (name: string) => { return `/api/configs/role/${name}`; }; +/** + * Patch Role configurations + * @summary Put Role + */ export const putRoleApiConfigsRoleNamePut = async ( name: string, putRoleRequest: PutRoleRequest, @@ -4034,10 +4155,6 @@ export const putRoleApiConfigsRoleNamePut = async ( return { data, status: res.status, headers: res.headers } as putRoleApiConfigsRoleNamePutResponse; }; -/** - * Delete Role - * @summary Delete Role - */ export type deleteRoleApiConfigsRoleNameDeleteResponse200 = { data: unknown; status: 200; @@ -4063,6 +4180,10 @@ export const getDeleteRoleApiConfigsRoleNameDeleteUrl = (name: string) => { return `/api/configs/role/${name}`; }; +/** + * Delete Role + * @summary Delete Role + */ export const deleteRoleApiConfigsRoleNameDelete = async ( name: string, configsRequest: ConfigsRequest, @@ -4081,10 +4202,6 @@ export const deleteRoleApiConfigsRoleNameDelete = async ( return { data, status: res.status, headers: res.headers } as deleteRoleApiConfigsRoleNameDeleteResponse; }; -/** - * List all backend test configurations - * @summary List Backend Tests - */ export type listBackendTestsApiConfigsBackendTestGetResponse200 = { data: ListBackendTestsApiConfigsBackendTestGet200; status: 200; @@ -4100,6 +4217,10 @@ export const getListBackendTestsApiConfigsBackendTestGetUrl = () => { return `/api/configs/backend_test`; }; +/** + * List all backend test configurations + * @summary List Backend Tests + */ export const listBackendTestsApiConfigsBackendTestGet = async ( options?: RequestInit, ): Promise => { @@ -4114,10 +4235,6 @@ export const listBackendTestsApiConfigsBackendTestGet = async ( return { data, status: res.status, headers: res.headers } as listBackendTestsApiConfigsBackendTestGetResponse; }; -/** - * Put backend test configurations - * @summary Put Backend Tests - */ export type putBackendTestsApiConfigsBackendTestPutResponse200 = { data: unknown; status: 200; @@ -4145,6 +4262,10 @@ export const getPutBackendTestsApiConfigsBackendTestPutUrl = () => { return `/api/configs/backend_test`; }; +/** + * Put backend test configurations + * @summary Put Backend Tests + */ export const putBackendTestsApiConfigsBackendTestPut = async ( putBackendTestsRequest: PutBackendTestsRequest, options?: RequestInit, @@ -4162,10 +4283,6 @@ export const putBackendTestsApiConfigsBackendTestPut = async ( return { data, status: res.status, headers: res.headers } as putBackendTestsApiConfigsBackendTestPutResponse; }; -/** - * Read backend test configuration - * @summary Read Backend Test - */ export type readBackendTestApiConfigsBackendTestNameGetResponse200 = { data: BackendTests; status: 200; @@ -4193,6 +4310,10 @@ export const getReadBackendTestApiConfigsBackendTestNameGetUrl = (name: string) return `/api/configs/backend_test/${name}`; }; +/** + * Read backend test configuration + * @summary Read Backend Test + */ export const readBackendTestApiConfigsBackendTestNameGet = async ( name: string, options?: RequestInit, @@ -4208,10 +4329,6 @@ export const readBackendTestApiConfigsBackendTestNameGet = async ( return { data, status: res.status, headers: res.headers } as readBackendTestApiConfigsBackendTestNameGetResponse; }; -/** - * Put backend test configuration - * @summary Put Backend Test - */ export type putBackendTestApiConfigsBackendTestNamePutResponse200 = { data: unknown; status: 200; @@ -4239,6 +4356,10 @@ export const getPutBackendTestApiConfigsBackendTestNamePutUrl = (name: string) = return `/api/configs/backend_test/${name}`; }; +/** + * Put backend test configuration + * @summary Put Backend Test + */ export const putBackendTestApiConfigsBackendTestNamePut = async ( name: string, putBackendTestRequest: PutBackendTestRequest, @@ -4257,10 +4378,6 @@ export const putBackendTestApiConfigsBackendTestNamePut = async ( return { data, status: res.status, headers: res.headers } as putBackendTestApiConfigsBackendTestNamePutResponse; }; -/** - * Patch backend test configuration - * @summary Patch Backend Test - */ export type patchBackendTestApiConfigsBackendTestNamePatchResponse200 = { data: unknown; status: 200; @@ -4288,6 +4405,10 @@ export const getPatchBackendTestApiConfigsBackendTestNamePatchUrl = (name: strin return `/api/configs/backend_test/${name}`; }; +/** + * Patch backend test configuration + * @summary Patch Backend Test + */ export const patchBackendTestApiConfigsBackendTestNamePatch = async ( name: string, patchBackendTestRequest: PatchBackendTestRequest, @@ -4306,10 +4427,6 @@ export const patchBackendTestApiConfigsBackendTestNamePatch = async ( return { data, status: res.status, headers: res.headers } as patchBackendTestApiConfigsBackendTestNamePatchResponse; }; -/** - * Delete test configuration - * @summary Delete Backend Test - */ export type deleteBackendTestApiConfigsBackendTestNameDeleteResponse200 = { data: unknown; status: 200; @@ -4337,6 +4454,10 @@ export const getDeleteBackendTestApiConfigsBackendTestNameDeleteUrl = (name: str return `/api/configs/backend_test/${name}`; }; +/** + * Delete test configuration + * @summary Delete Backend Test + */ export const deleteBackendTestApiConfigsBackendTestNameDelete = async ( name: string, configsRequest: ConfigsRequest, @@ -4355,10 +4476,6 @@ export const deleteBackendTestApiConfigsBackendTestNameDelete = async ( return { data, status: res.status, headers: res.headers } as deleteBackendTestApiConfigsBackendTestNameDeleteResponse; }; -/** - * List history of all configs - * @summary Get Configs History - */ export type getConfigsHistoryApiConfigsHistoryGetResponse200 = { data: GetConfigsHistoryResponse; status: 200; @@ -4388,13 +4505,13 @@ export const getGetConfigsHistoryApiConfigsHistoryGetUrl = (params?: GetConfigsH if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -4403,6 +4520,10 @@ export const getGetConfigsHistoryApiConfigsHistoryGetUrl = (params?: GetConfigsH return stringifiedParams.length > 0 ? `/api/configs/history?${stringifiedParams}` : `/api/configs/history`; }; +/** + * List history of all configs + * @summary Get Configs History + */ export const getConfigsHistoryApiConfigsHistoryGet = async ( params?: GetConfigsHistoryApiConfigsHistoryGetParams, options?: RequestInit, @@ -4418,10 +4539,6 @@ export const getConfigsHistoryApiConfigsHistoryGet = async ( return { data, status: res.status, headers: res.headers } as getConfigsHistoryApiConfigsHistoryGetResponse; }; -/** - * Roll back a config to a particular revision. - * @summary Rollback Config - */ export type rollbackConfigApiConfigsHistoryRollbackPostResponse200 = { data: unknown; status: 200; @@ -4449,6 +4566,10 @@ export const getRollbackConfigApiConfigsHistoryRollbackPostUrl = () => { return `/api/configs/history/rollback`; }; +/** + * Roll back a config to a particular revision. + * @summary Rollback Config + */ export const rollbackConfigApiConfigsHistoryRollbackPost = async ( rollbackConfigRequest: RollbackConfigRequest, options?: RequestInit, @@ -4466,18 +4587,6 @@ export const rollbackConfigApiConfigsHistoryRollbackPost = async ( return { data, status: res.status, headers: res.headers } as rollbackConfigApiConfigsHistoryRollbackPostResponse; }; -/** - * Delete a specific config history revision. This performs a soft delete of the revision. - -Args: - config_type: Type of config to delete - revision: Revision number to delete (must be greater than 0) - username: Username of the person performing the delete - -Raises: - OSMOUserError: If the revision doesn't exist or is the current revision - * @summary Delete Config History Revision - */ export type deleteConfigHistoryRevisionApiConfigsHistoryConfigTypeRevisionRevisionDeleteResponse200 = { data: unknown; status: 200; @@ -4508,6 +4617,18 @@ export const getDeleteConfigHistoryRevisionApiConfigsHistoryConfigTypeRevisionRe return `/api/configs/history/${configType}/revision/${revision}`; }; +/** + * Delete a specific config history revision. This performs a soft delete of the revision. + * + * Args: + * config_type: Type of config to delete + * revision: Revision number to delete (must be greater than 0) + * username: Username of the person performing the delete + * + * Raises: + * OSMOUserError: If the revision doesn't exist or is the current revision + * @summary Delete Config History Revision + */ export const deleteConfigHistoryRevisionApiConfigsHistoryConfigTypeRevisionRevisionDelete = async ( configType: string, revision: number, @@ -4533,19 +4654,6 @@ export const deleteConfigHistoryRevisionApiConfigsHistoryConfigTypeRevisionRevis } as deleteConfigHistoryRevisionApiConfigsHistoryConfigTypeRevisionRevisionDeleteResponse; }; -/** - * Update tags for a specific config history revision. - -Args: - config_type: Type of config to update - revision: Revision number to update (must be greater than 0) - request: Request containing tags to add and delete - username: Username of the person performing the update - -Raises: - OSMOUserError: If the revision doesn't exist or is invalid - * @summary Update Config History Tags - */ export type updateConfigHistoryTagsApiConfigsHistoryConfigTypeRevisionRevisionTagsPostResponse200 = { data: unknown; status: 200; @@ -4576,6 +4684,19 @@ export const getUpdateConfigHistoryTagsApiConfigsHistoryConfigTypeRevisionRevisi return `/api/configs/history/${configType}/revision/${revision}/tags`; }; +/** + * Update tags for a specific config history revision. + * + * Args: + * config_type: Type of config to update + * revision: Revision number to update (must be greater than 0) + * request: Request containing tags to add and delete + * username: Username of the person performing the update + * + * Raises: + * OSMOUserError: If the revision doesn't exist or is invalid + * @summary Update Config History Tags + */ export const updateConfigHistoryTagsApiConfigsHistoryConfigTypeRevisionRevisionTagsPost = async ( configType: string, revision: number, @@ -4604,21 +4725,6 @@ export const updateConfigHistoryTagsApiConfigsHistoryConfigTypeRevisionRevisionT } as updateConfigHistoryTagsApiConfigsHistoryConfigTypeRevisionRevisionTagsPostResponse; }; -/** - * Returns two config revisions, similar to -GET /api/configs/history/{config_type}/revision/{revision}, but with obfuscated secret strings -that say if a secret string is changed. Intended for use with the `diff` command. - -Args: - request: Request containing config type and revisions to compare - -Returns: - ConfigDiffResponse containing the two revisions - -Raises: - OSMOUserError: If either revision doesn't exist or is invalid - * @summary Get Config Diff - */ export type getConfigDiffApiConfigsDiffGetResponse200 = { data: ConfigDiffResponse; status: 200; @@ -4645,7 +4751,7 @@ export const getGetConfigDiffApiConfigsDiffGetUrl = (params: GetConfigDiffApiCon Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -4654,6 +4760,21 @@ export const getGetConfigDiffApiConfigsDiffGetUrl = (params: GetConfigDiffApiCon return stringifiedParams.length > 0 ? `/api/configs/diff?${stringifiedParams}` : `/api/configs/diff`; }; +/** + * Returns two config revisions, similar to + * GET /api/configs/history/{config_type}/revision/{revision}, but with obfuscated secret strings + * that say if a secret string is changed. Intended for use with the `diff` command. + * + * Args: + * request: Request containing config type and revisions to compare + * + * Returns: + * ConfigDiffResponse containing the two revisions + * + * Raises: + * OSMOUserError: If either revision doesn't exist or is invalid + * @summary Get Config Diff + */ export const getConfigDiffApiConfigsDiffGet = async ( params: GetConfigDiffApiConfigsDiffGetParams, options?: RequestInit, @@ -4669,12 +4790,6 @@ export const getConfigDiffApiConfigsDiffGet = async ( return { data, status: res.status, headers: res.headers } as getConfigDiffApiConfigsDiffGetResponse; }; -/** - * API to fetch for a new access token using a refresh token. - -Deprecated: Use POST /api/auth/jwt/refresh_token instead. - * @summary Get New Jwt Token - */ export type getNewJwtTokenApiAuthJwtRefreshTokenGetResponse200 = { data: JwtTokenResponse; status: 200; @@ -4705,7 +4820,7 @@ export const getGetNewJwtTokenApiAuthJwtRefreshTokenGetUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -4716,6 +4831,12 @@ export const getGetNewJwtTokenApiAuthJwtRefreshTokenGetUrl = ( : `/api/auth/jwt/refresh_token`; }; +/** + * API to fetch for a new access token using a refresh token. + * + * Deprecated: Use POST /api/auth/jwt/refresh_token instead. + * @summary Get New Jwt Token + */ export const getNewJwtTokenApiAuthJwtRefreshTokenGet = async ( params: GetNewJwtTokenApiAuthJwtRefreshTokenGetParams, options?: RequestInit, @@ -4731,10 +4852,6 @@ export const getNewJwtTokenApiAuthJwtRefreshTokenGet = async ( return { data, status: res.status, headers: res.headers } as getNewJwtTokenApiAuthJwtRefreshTokenGetResponse; }; -/** - * API to fetch for a new access token using a refresh token. - * @summary Post New Jwt Token - */ export type postNewJwtTokenApiAuthJwtRefreshTokenPostResponse200 = { data: JwtTokenResponse; status: 200; @@ -4765,7 +4882,7 @@ export const getPostNewJwtTokenApiAuthJwtRefreshTokenPostUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -4776,6 +4893,10 @@ export const getPostNewJwtTokenApiAuthJwtRefreshTokenPostUrl = ( : `/api/auth/jwt/refresh_token`; }; +/** + * API to fetch for a new access token using a refresh token. + * @summary Post New Jwt Token + */ export const postNewJwtTokenApiAuthJwtRefreshTokenPost = async ( tokenRequest: TokenRequest, params: PostNewJwtTokenApiAuthJwtRefreshTokenPostParams, @@ -4794,12 +4915,6 @@ export const postNewJwtTokenApiAuthJwtRefreshTokenPost = async ( return { data, status: res.status, headers: res.headers } as postNewJwtTokenApiAuthJwtRefreshTokenPostResponse; }; -/** - * API to create a new jwt token from an access token. - -Deprecated: Use POST /api/auth/jwt/access_token instead. - * @summary Get Jwt Token From Access Token - */ export type getJwtTokenFromAccessTokenApiAuthJwtAccessTokenGetResponse200 = { data: JwtTokenResponse; status: 200; @@ -4830,7 +4945,7 @@ export const getGetJwtTokenFromAccessTokenApiAuthJwtAccessTokenGetUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -4841,6 +4956,12 @@ export const getGetJwtTokenFromAccessTokenApiAuthJwtAccessTokenGetUrl = ( : `/api/auth/jwt/access_token`; }; +/** + * API to create a new jwt token from an access token. + * + * Deprecated: Use POST /api/auth/jwt/access_token instead. + * @summary Get Jwt Token From Access Token + */ export const getJwtTokenFromAccessTokenApiAuthJwtAccessTokenGet = async ( params: GetJwtTokenFromAccessTokenApiAuthJwtAccessTokenGetParams, options?: RequestInit, @@ -4860,10 +4981,6 @@ export const getJwtTokenFromAccessTokenApiAuthJwtAccessTokenGet = async ( } as getJwtTokenFromAccessTokenApiAuthJwtAccessTokenGetResponse; }; -/** - * API to create a new jwt token from an access token. - * @summary Post Jwt Token From Access Token - */ export type postJwtTokenFromAccessTokenApiAuthJwtAccessTokenPostResponse200 = { data: JwtTokenResponse; status: 200; @@ -4891,6 +5008,10 @@ export const getPostJwtTokenFromAccessTokenApiAuthJwtAccessTokenPostUrl = () => return `/api/auth/jwt/access_token`; }; +/** + * API to create a new jwt token from an access token. + * @summary Post Jwt Token From Access Token + */ export const postJwtTokenFromAccessTokenApiAuthJwtAccessTokenPost = async ( tokenRequest: TokenRequest, options?: RequestInit, @@ -4912,15 +5033,6 @@ export const postJwtTokenFromAccessTokenApiAuthJwtAccessTokenPost = async ( } as postJwtTokenFromAccessTokenApiAuthJwtAccessTokenPostResponse; }; -/** - * API to create a new access token. - -If roles are specified, all specified roles must be assigned to the user. -If any role is not assigned to the user, the request fails and no token -is created. If no roles are specified, the access token inherits all of the user's -current roles from the user_roles table. - * @summary Create Access Token - */ export type createAccessTokenApiAuthAccessTokenTokenNamePostResponse200 = { data: string; status: 200; @@ -4955,13 +5067,13 @@ export const getCreateAccessTokenApiAuthAccessTokenTokenNamePostUrl = ( if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -4972,6 +5084,15 @@ export const getCreateAccessTokenApiAuthAccessTokenTokenNamePostUrl = ( : `/api/auth/access_token/${tokenName}`; }; +/** + * API to create a new access token. + * + * If roles are specified, all specified roles must be assigned to the user. + * If any role is not assigned to the user, the request fails and no token + * is created. If no roles are specified, the access token inherits all of the user's + * current roles from the user_roles table. + * @summary Create Access Token + */ export const createAccessTokenApiAuthAccessTokenTokenNamePost = async ( tokenName: string, params: CreateAccessTokenApiAuthAccessTokenTokenNamePostParams, @@ -4988,10 +5109,6 @@ export const createAccessTokenApiAuthAccessTokenTokenNamePost = async ( return { data, status: res.status, headers: res.headers } as createAccessTokenApiAuthAccessTokenTokenNamePostResponse; }; -/** - * API to delete an access token. - * @summary Delete Access Token - */ export type deleteAccessTokenApiAuthAccessTokenTokenNameDeleteResponse200 = { data: unknown; status: 200; @@ -5019,6 +5136,10 @@ export const getDeleteAccessTokenApiAuthAccessTokenTokenNameDeleteUrl = (tokenNa return `/api/auth/access_token/${tokenName}`; }; +/** + * API to delete an access token. + * @summary Delete Access Token + */ export const deleteAccessTokenApiAuthAccessTokenTokenNameDelete = async ( tokenName: string, options?: RequestInit, @@ -5038,17 +5159,6 @@ export const deleteAccessTokenApiAuthAccessTokenTokenNameDelete = async ( } as deleteAccessTokenApiAuthAccessTokenTokenNameDeleteResponse; }; -/** - * List all roles assigned to an access token. - -Args: - token_name: The token name - user_name: Authenticated user (owner of the token) - -Returns: - AccessTokenRolesResponse with list of role assignments - * @summary List Access Token Roles - */ export type listAccessTokenRolesApiAuthAccessTokenTokenNameRolesGetResponse200 = { data: AccessTokenRolesResponse; status: 200; @@ -5076,6 +5186,17 @@ export const getListAccessTokenRolesApiAuthAccessTokenTokenNameRolesGetUrl = (to return `/api/auth/access_token/${tokenName}/roles`; }; +/** + * List all roles assigned to an access token. + * + * Args: + * token_name: The token name + * user_name: Authenticated user (owner of the token) + * + * Returns: + * AccessTokenRolesResponse with list of role assignments + * @summary List Access Token Roles + */ export const listAccessTokenRolesApiAuthAccessTokenTokenNameRolesGet = async ( tokenName: string, options?: RequestInit, @@ -5095,10 +5216,6 @@ export const listAccessTokenRolesApiAuthAccessTokenTokenNameRolesGet = async ( } as listAccessTokenRolesApiAuthAccessTokenTokenNameRolesGetResponse; }; -/** - * API to list all access tokens for a user, including their assigned roles. - * @summary List Access Tokens - */ export type listAccessTokensApiAuthAccessTokenGetResponse200 = { data: AccessTokenWithRoles[]; status: 200; @@ -5124,6 +5241,10 @@ export const getListAccessTokensApiAuthAccessTokenGetUrl = () => { return `/api/auth/access_token`; }; +/** + * API to list all access tokens for a user, including their assigned roles. + * @summary List Access Tokens + */ export const listAccessTokensApiAuthAccessTokenGet = async ( options?: RequestInit, ): Promise => { @@ -5138,29 +5259,6 @@ export const listAccessTokensApiAuthAccessTokenGet = async ( return { data, status: res.status, headers: res.headers } as listAccessTokensApiAuthAccessTokenGetResponse; }; -/** - * Admin API to create an access token for a specific user. - -This endpoint allows administrators to create an access token -on behalf of any user in the system. - -If roles are specified, all specified roles must be assigned to the target -user. If any role is not assigned to the user, the request fails and no -token is created. If no roles are specified, the access token inherits all of the -target user's current roles from the user_roles table. - -Args: - user_id: The user ID to create the token for - token_name: Name for the access token - expires_at: Expiration date in YYYY-MM-DD format - description: Optional description for the token - roles: Optional list of roles to assign (must all be assigned to user) - admin_user: Authenticated admin user making the request - -Returns: - The generated access token string - * @summary Admin Create Access Token - */ export type adminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePostResponse200 = { data: string; status: 200; @@ -5196,13 +5294,13 @@ export const getAdminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePostU if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -5213,6 +5311,29 @@ export const getAdminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePostU : `/api/auth/user/${userId}/access_token/${tokenName}`; }; +/** + * Admin API to create an access token for a specific user. + * + * This endpoint allows administrators to create an access token + * on behalf of any user in the system. + * + * If roles are specified, all specified roles must be assigned to the target + * user. If any role is not assigned to the user, the request fails and no + * token is created. If no roles are specified, the access token inherits all of the + * target user's current roles from the user_roles table. + * + * Args: + * user_id: The user ID to create the token for + * token_name: Name for the access token + * expires_at: Expiration date in YYYY-MM-DD format + * description: Optional description for the token + * roles: Optional list of roles to assign (must all be assigned to user) + * admin_user: Authenticated admin user making the request + * + * Returns: + * The generated access token string + * @summary Admin Create Access Token + */ export const adminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePost = async ( userId: string, tokenName: string, @@ -5239,14 +5360,6 @@ export const adminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePost = a } as adminCreateAccessTokenApiAuthUserUserIdAccessTokenTokenNamePostResponse; }; -/** - * Admin API to delete an access token for a specific user. - -Args: - user_id: The user ID who owns the token - token_name: Name of the token to delete - * @summary Admin Delete Access Token - */ export type adminDeleteAccessTokenApiAuthUserUserIdAccessTokenTokenNameDeleteResponse200 = { data: unknown; status: 200; @@ -5277,6 +5390,14 @@ export const getAdminDeleteAccessTokenApiAuthUserUserIdAccessTokenTokenNameDelet return `/api/auth/user/${userId}/access_token/${tokenName}`; }; +/** + * Admin API to delete an access token for a specific user. + * + * Args: + * user_id: The user ID who owns the token + * token_name: Name of the token to delete + * @summary Admin Delete Access Token + */ export const adminDeleteAccessTokenApiAuthUserUserIdAccessTokenTokenNameDelete = async ( userId: string, tokenName: string, @@ -5299,16 +5420,6 @@ export const adminDeleteAccessTokenApiAuthUserUserIdAccessTokenTokenNameDelete = } as adminDeleteAccessTokenApiAuthUserUserIdAccessTokenTokenNameDeleteResponse; }; -/** - * Admin API to list all access tokens for a specific user, including their assigned roles. - -Args: - user_id: The user ID to list tokens for - -Returns: - List of AccessTokenWithRoles objects - * @summary Admin List Access Tokens - */ export type adminListAccessTokensApiAuthUserUserIdAccessTokenGetResponse200 = { data: AccessTokenWithRoles[]; status: 200; @@ -5336,6 +5447,16 @@ export const getAdminListAccessTokensApiAuthUserUserIdAccessTokenGetUrl = (userI return `/api/auth/user/${userId}/access_token`; }; +/** + * Admin API to list all access tokens for a specific user, including their assigned roles. + * + * Args: + * user_id: The user ID to list tokens for + * + * Returns: + * List of AccessTokenWithRoles objects + * @summary Admin List Access Tokens + */ export const adminListAccessTokensApiAuthUserUserIdAccessTokenGet = async ( userId: string, options?: RequestInit, @@ -5355,20 +5476,6 @@ export const adminListAccessTokensApiAuthUserUserIdAccessTokenGet = async ( } as adminListAccessTokensApiAuthUserUserIdAccessTokenGetResponse; }; -/** - * List all users with optional filtering. - -Args: - start_index: Pagination start (1-based, default: 1) - count: Results per page (default: 100, max: 1000) - id_prefix: Filter users whose ID starts with this prefix - roles: List of role names. Returns users who have ANY of these roles. - Use multiple query params: ?roles=admin&roles=user - -Returns: - UserListResponse with paginated user list - * @summary List Users - */ export type listUsersApiAuthUserGetResponse200 = { data: UserListResponse; status: 200; @@ -5398,13 +5505,13 @@ export const getListUsersApiAuthUserGetUrl = (params?: ListUsersApiAuthUserGetPa if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -5413,6 +5520,20 @@ export const getListUsersApiAuthUserGetUrl = (params?: ListUsersApiAuthUserGetPa return stringifiedParams.length > 0 ? `/api/auth/user?${stringifiedParams}` : `/api/auth/user`; }; +/** + * List all users with optional filtering. + * + * Args: + * start_index: Pagination start (1-based, default: 1) + * count: Results per page (default: 100, max: 1000) + * id_prefix: Filter users whose ID starts with this prefix + * roles: List of role names. Returns users who have ANY of these roles. + * Use multiple query params: ?roles=admin&roles=user + * + * Returns: + * UserListResponse with paginated user list + * @summary List Users + */ export const listUsersApiAuthUserGet = async ( params?: ListUsersApiAuthUserGetParams, options?: RequestInit, @@ -5428,17 +5549,6 @@ export const listUsersApiAuthUserGet = async ( return { data, status: res.status, headers: res.headers } as listUsersApiAuthUserGetResponse; }; -/** - * Create a new user. - -Args: - request: CreateUserRequest with user details - created_by: Authenticated user making the request - -Returns: - Created User object - * @summary Create User - */ export type createUserApiAuthUserPostResponse200 = { data: User; status: 200; @@ -5464,6 +5574,17 @@ export const getCreateUserApiAuthUserPostUrl = () => { return `/api/auth/user`; }; +/** + * Create a new user. + * + * Args: + * request: CreateUserRequest with user details + * created_by: Authenticated user making the request + * + * Returns: + * Created User object + * @summary Create User + */ export const createUserApiAuthUserPost = async ( createUserRequest: CreateUserRequest, options?: RequestInit, @@ -5481,16 +5602,6 @@ export const createUserApiAuthUserPost = async ( return { data, status: res.status, headers: res.headers } as createUserApiAuthUserPostResponse; }; -/** - * Get a specific user's details including their roles. - -Args: - user_id: The user ID to fetch - -Returns: - UserWithRoles object - * @summary Get User - */ export type getUserApiAuthUserUserIdGetResponse200 = { data: UserWithRoles; status: 200; @@ -5516,6 +5627,16 @@ export const getGetUserApiAuthUserUserIdGetUrl = (userId: string) => { return `/api/auth/user/${userId}`; }; +/** + * Get a specific user's details including their roles. + * + * Args: + * user_id: The user ID to fetch + * + * Returns: + * UserWithRoles object + * @summary Get User + */ export const getUserApiAuthUserUserIdGet = async ( userId: string, options?: RequestInit, @@ -5531,13 +5652,6 @@ export const getUserApiAuthUserUserIdGet = async ( return { data, status: res.status, headers: res.headers } as getUserApiAuthUserUserIdGetResponse; }; -/** - * Delete a user and all associated role assignments and PATs. - -Args: - user_id: The user ID to delete - * @summary Delete User - */ export type deleteUserApiAuthUserUserIdDeleteResponse200 = { data: unknown; status: 200; @@ -5563,6 +5677,13 @@ export const getDeleteUserApiAuthUserUserIdDeleteUrl = (userId: string) => { return `/api/auth/user/${userId}`; }; +/** + * Delete a user and all associated role assignments and PATs. + * + * Args: + * user_id: The user ID to delete + * @summary Delete User + */ export const deleteUserApiAuthUserUserIdDelete = async ( userId: string, options?: RequestInit, @@ -5578,16 +5699,6 @@ export const deleteUserApiAuthUserUserIdDelete = async ( return { data, status: res.status, headers: res.headers } as deleteUserApiAuthUserUserIdDeleteResponse; }; -/** - * List all roles assigned to a user. - -Args: - user_id: The user ID - -Returns: - UserRolesResponse with list of role assignments - * @summary List User Roles - */ export type listUserRolesApiAuthUserUserIdRolesGetResponse200 = { data: UserRolesResponse; status: 200; @@ -5614,6 +5725,16 @@ export const getListUserRolesApiAuthUserUserIdRolesGetUrl = (userId: string) => return `/api/auth/user/${userId}/roles`; }; +/** + * List all roles assigned to a user. + * + * Args: + * user_id: The user ID + * + * Returns: + * UserRolesResponse with list of role assignments + * @summary List User Roles + */ export const listUserRolesApiAuthUserUserIdRolesGet = async ( userId: string, options?: RequestInit, @@ -5629,18 +5750,6 @@ export const listUserRolesApiAuthUserUserIdRolesGet = async ( return { data, status: res.status, headers: res.headers } as listUserRolesApiAuthUserUserIdRolesGetResponse; }; -/** - * Assign a role to a user. - -Args: - user_id: The user ID - request: AssignRoleRequest with role_name - assigned_by: Authenticated user making the request - -Returns: - UserRoleAssignment with assignment details - * @summary Assign Role To User - */ export type assignRoleToUserApiAuthUserUserIdRolesPostResponse200 = { data: UserRoleAssignment; status: 200; @@ -5668,6 +5777,18 @@ export const getAssignRoleToUserApiAuthUserUserIdRolesPostUrl = (userId: string) return `/api/auth/user/${userId}/roles`; }; +/** + * Assign a role to a user. + * + * Args: + * user_id: The user ID + * request: AssignRoleRequest with role_name + * assigned_by: Authenticated user making the request + * + * Returns: + * UserRoleAssignment with assignment details + * @summary Assign Role To User + */ export const assignRoleToUserApiAuthUserUserIdRolesPost = async ( userId: string, assignRoleRequest: AssignRoleRequest, @@ -5686,17 +5807,6 @@ export const assignRoleToUserApiAuthUserUserIdRolesPost = async ( return { data, status: res.status, headers: res.headers } as assignRoleToUserApiAuthUserUserIdRolesPostResponse; }; -/** - * Remove a role from a user and all their PATs. - -When a role is removed from a user, it is automatically removed from all PATs -owned by that user via the FK cascade from access_token_roles to user_roles. - -Args: - user_id: The user ID - role_name: The role to remove - * @summary Remove Role From User - */ export type removeRoleFromUserApiAuthUserUserIdRolesRoleNameDeleteResponse200 = { data: unknown; status: 200; @@ -5724,6 +5834,17 @@ export const getRemoveRoleFromUserApiAuthUserUserIdRolesRoleNameDeleteUrl = (use return `/api/auth/user/${userId}/roles/${roleName}`; }; +/** + * Remove a role from a user and all their PATs. + * + * When a role is removed from a user, it is automatically removed from all PATs + * owned by that user via the FK cascade from access_token_roles to user_roles. + * + * Args: + * user_id: The user ID + * role_name: The role to remove + * @summary Remove Role From User + */ export const removeRoleFromUserApiAuthUserUserIdRolesRoleNameDelete = async ( userId: string, roleName: string, @@ -5744,16 +5865,6 @@ export const removeRoleFromUserApiAuthUserUserIdRolesRoleNameDelete = async ( } as removeRoleFromUserApiAuthUserUserIdRolesRoleNameDeleteResponse; }; -/** - * List all users who have a specific role. - -Args: - role_name: The role name - -Returns: - RoleUsersResponse with list of users - * @summary List Users With Role - */ export type listUsersWithRoleApiAuthRolesRoleNameUsersGetResponse200 = { data: RoleUsersResponse; status: 200; @@ -5781,6 +5892,16 @@ export const getListUsersWithRoleApiAuthRolesRoleNameUsersGetUrl = (roleName: st return `/api/auth/roles/${roleName}/users`; }; +/** + * List all users who have a specific role. + * + * Args: + * role_name: The role name + * + * Returns: + * RoleUsersResponse with list of users + * @summary List Users With Role + */ export const listUsersWithRoleApiAuthRolesRoleNameUsersGet = async ( roleName: string, options?: RequestInit, @@ -5796,18 +5917,6 @@ export const listUsersWithRoleApiAuthRolesRoleNameUsersGet = async ( return { data, status: res.status, headers: res.headers } as listUsersWithRoleApiAuthRolesRoleNameUsersGetResponse; }; -/** - * Bulk assign a role to multiple users. - -Args: - role_name: The role to assign - request: BulkAssignRequest with list of user_ids - assigned_by: Authenticated user making the request - -Returns: - BulkAssignResponse with results - * @summary Bulk Assign Role - */ export type bulkAssignRoleApiAuthRolesRoleNameUsersPostResponse200 = { data: BulkAssignResponse; status: 200; @@ -5835,6 +5944,18 @@ export const getBulkAssignRoleApiAuthRolesRoleNameUsersPostUrl = (roleName: stri return `/api/auth/roles/${roleName}/users`; }; +/** + * Bulk assign a role to multiple users. + * + * Args: + * role_name: The role to assign + * request: BulkAssignRequest with list of user_ids + * assigned_by: Authenticated user making the request + * + * Returns: + * BulkAssignResponse with results + * @summary Bulk Assign Role + */ export const bulkAssignRoleApiAuthRolesRoleNameUsersPost = async ( roleName: string, bulkAssignRequest: BulkAssignRequest, @@ -5853,9 +5974,6 @@ export const bulkAssignRoleApiAuthRolesRoleNameUsersPost = async ( return { data, status: res.status, headers: res.headers } as bulkAssignRoleApiAuthRolesRoleNameUsersPostResponse; }; -/** - * @summary List Apps - */ export type listAppsApiAppGetResponse200 = { data: SrcServiceCoreAppObjectsListResponse; status: 200; @@ -5883,13 +6001,13 @@ export const getListAppsApiAppGetUrl = (params?: ListAppsApiAppGetParams) => { if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -5898,6 +6016,9 @@ export const getListAppsApiAppGetUrl = (params?: ListAppsApiAppGetParams) => { return stringifiedParams.length > 0 ? `/api/app?${stringifiedParams}` : `/api/app`; }; +/** + * @summary List Apps + */ export const listAppsApiAppGet = async ( params?: ListAppsApiAppGetParams, options?: RequestInit, @@ -5913,9 +6034,6 @@ export const listAppsApiAppGet = async ( return { data, status: res.status, headers: res.headers } as listAppsApiAppGetResponse; }; -/** - * @summary Get App - */ export type getAppApiAppUserNameGetResponse200 = { data: GetAppResponse; status: 200; @@ -5942,7 +6060,7 @@ export const getGetAppApiAppUserNameGetUrl = (name: string, params?: GetAppApiAp Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -5951,6 +6069,9 @@ export const getGetAppApiAppUserNameGetUrl = (name: string, params?: GetAppApiAp return stringifiedParams.length > 0 ? `/api/app/user/${name}?${stringifiedParams}` : `/api/app/user/${name}`; }; +/** + * @summary Get App + */ export const getAppApiAppUserNameGet = async ( name: string, params?: GetAppApiAppUserNameGetParams, @@ -5967,9 +6088,6 @@ export const getAppApiAppUserNameGet = async ( return { data, status: res.status, headers: res.headers } as getAppApiAppUserNameGetResponse; }; -/** - * @summary Create App - */ export type createAppApiAppUserNamePostResponse200 = { data: unknown; status: 200; @@ -5996,7 +6114,7 @@ export const getCreateAppApiAppUserNamePostUrl = (name: string, params: CreateAp Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6005,6 +6123,9 @@ export const getCreateAppApiAppUserNamePostUrl = (name: string, params: CreateAp return stringifiedParams.length > 0 ? `/api/app/user/${name}?${stringifiedParams}` : `/api/app/user/${name}`; }; +/** + * @summary Create App + */ export const createAppApiAppUserNamePost = async ( name: string, createAppApiAppUserNamePostBody: string, @@ -6024,9 +6145,6 @@ export const createAppApiAppUserNamePost = async ( return { data, status: res.status, headers: res.headers } as createAppApiAppUserNamePostResponse; }; -/** - * @summary Update App - */ export type updateAppApiAppUserNamePatchResponse200 = { data: EditResponse; status: 200; @@ -6052,6 +6170,9 @@ export const getUpdateAppApiAppUserNamePatchUrl = (name: string) => { return `/api/app/user/${name}`; }; +/** + * @summary Update App + */ export const updateAppApiAppUserNamePatch = async ( name: string, updateAppApiAppUserNamePatchBody: string, @@ -6070,9 +6191,6 @@ export const updateAppApiAppUserNamePatch = async ( return { data, status: res.status, headers: res.headers } as updateAppApiAppUserNamePatchResponse; }; -/** - * @summary Delete App - */ export type deleteAppApiAppUserNameDeleteResponse200 = { data: DeleteAppApiAppUserNameDelete200; status: 200; @@ -6099,7 +6217,7 @@ export const getDeleteAppApiAppUserNameDeleteUrl = (name: string, params?: Delet Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6108,6 +6226,9 @@ export const getDeleteAppApiAppUserNameDeleteUrl = (name: string, params?: Delet return stringifiedParams.length > 0 ? `/api/app/user/${name}?${stringifiedParams}` : `/api/app/user/${name}`; }; +/** + * @summary Delete App + */ export const deleteAppApiAppUserNameDelete = async ( name: string, params?: DeleteAppApiAppUserNameDeleteParams, @@ -6124,9 +6245,6 @@ export const deleteAppApiAppUserNameDelete = async ( return { data, status: res.status, headers: res.headers } as deleteAppApiAppUserNameDeleteResponse; }; -/** - * @summary Get App Content - */ export type getAppContentApiAppUserNameSpecGetResponse200 = { data: void; status: 200; @@ -6156,7 +6274,7 @@ export const getGetAppContentApiAppUserNameSpecGetUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6167,6 +6285,9 @@ export const getGetAppContentApiAppUserNameSpecGetUrl = ( : `/api/app/user/${name}/spec`; }; +/** + * @summary Get App Content + */ export const getAppContentApiAppUserNameSpecGet = async ( name: string, params?: GetAppContentApiAppUserNameSpecGetParams, @@ -6179,13 +6300,10 @@ export const getAppContentApiAppUserNameSpecGet = async ( const body = [204, 205, 304].includes(res.status) ? null : await res.text(); - const data: getAppContentApiAppUserNameSpecGetResponse["data"] = body ? JSON.parse(body) : {}; + const data: getAppContentApiAppUserNameSpecGetResponse["data"] = body ? JSON.parse(body) : undefined; return { data, status: res.status, headers: res.headers } as getAppContentApiAppUserNameSpecGetResponse; }; -/** - * @summary Rename App - */ export type renameAppApiAppUserNameRenamePostResponse200 = { data: string; status: 200; @@ -6211,6 +6329,9 @@ export const getRenameAppApiAppUserNameRenamePostUrl = (name: string) => { return `/api/app/user/${name}/rename`; }; +/** + * @summary Rename App + */ export const renameAppApiAppUserNameRenamePost = async ( name: string, renameAppApiAppUserNameRenamePostBody: string, @@ -6229,10 +6350,6 @@ export const renameAppApiAppUserNameRenamePost = async ( return { data, status: res.status, headers: res.headers } as renameAppApiAppUserNameRenamePostResponse; }; -/** - * Cancels the workflow. - * @summary Cancel Workflow - */ export type cancelWorkflowApiWorkflowNameCancelPostResponse200 = { data: CancelResponse; status: 200; @@ -6264,7 +6381,7 @@ export const getCancelWorkflowApiWorkflowNameCancelPostUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6275,6 +6392,10 @@ export const getCancelWorkflowApiWorkflowNameCancelPostUrl = ( : `/api/workflow/${name}/cancel`; }; +/** + * Cancels the workflow. + * @summary Cancel Workflow + */ export const cancelWorkflowApiWorkflowNameCancelPost = async ( name: string, params?: CancelWorkflowApiWorkflowNameCancelPostParams, @@ -6291,9 +6412,6 @@ export const cancelWorkflowApiWorkflowNameCancelPost = async ( return { data, status: res.status, headers: res.headers } as cancelWorkflowApiWorkflowNameCancelPostResponse; }; -/** - * @summary List Workflow - */ export type listWorkflowApiWorkflowGetResponse200 = { data: SrcServiceCoreWorkflowObjectsListResponse; status: 200; @@ -6319,17 +6437,17 @@ export const getListWorkflowApiWorkflowGetUrl = (params?: ListWorkflowApiWorkflo const normalizedParams = new URLSearchParams(); Object.entries(params || {}).forEach(([key, value]) => { - const explodeParameters = ["users", "statuses", "pools", "tags", "priority"]; + const explodeParameters = ["users", "statuses", "pools", "tags", "priority", "label", "no_label"]; if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6338,6 +6456,9 @@ export const getListWorkflowApiWorkflowGetUrl = (params?: ListWorkflowApiWorkflo return stringifiedParams.length > 0 ? `/api/workflow?${stringifiedParams}` : `/api/workflow`; }; +/** + * @summary List Workflow + */ export const listWorkflowApiWorkflowGet = async ( params?: ListWorkflowApiWorkflowGetParams, options?: RequestInit, @@ -6352,11 +6473,7 @@ export const listWorkflowApiWorkflowGet = async ( const data: listWorkflowApiWorkflowGetResponse["data"] = body ? JSON.parse(body) : {}; return { data, status: res.status, headers: res.headers } as listWorkflowApiWorkflowGetResponse; }; - -/** - * Returns the task (with the latest retry_id) with the given name in the workflow. - * @summary Get Workflow Task - */ + export type getWorkflowTaskApiWorkflowNameTaskTaskNameGetResponse200 = { data: TaskEntry; status: 200; @@ -6384,6 +6501,10 @@ export const getGetWorkflowTaskApiWorkflowNameTaskTaskNameGetUrl = (name: string return `/api/workflow/${name}/task/${taskName}`; }; +/** + * Returns the task (with the latest retry_id) with the given name in the workflow. + * @summary Get Workflow Task + */ export const getWorkflowTaskApiWorkflowNameTaskTaskNameGet = async ( name: string, taskName: string, @@ -6400,9 +6521,6 @@ export const getWorkflowTaskApiWorkflowNameTaskTaskNameGet = async ( return { data, status: res.status, headers: res.headers } as getWorkflowTaskApiWorkflowNameTaskTaskNameGetResponse; }; -/** - * @summary List Task - */ export type listTaskApiTaskGetResponse200 = { data: ListTaskSummaryResponse | ListTaskResponse | ListTaskAggregatedResponse; status: 200; @@ -6430,13 +6548,13 @@ export const getListTaskApiTaskGetUrl = (params?: ListTaskApiTaskGetParams) => { if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6445,6 +6563,9 @@ export const getListTaskApiTaskGetUrl = (params?: ListTaskApiTaskGetParams) => { return stringifiedParams.length > 0 ? `/api/task?${stringifiedParams}` : `/api/task`; }; +/** + * @summary List Task + */ export const listTaskApiTaskGet = async ( params?: ListTaskApiTaskGetParams, options?: RequestInit, @@ -6460,10 +6581,6 @@ export const listTaskApiTaskGet = async ( return { data, status: res.status, headers: res.headers } as listTaskApiTaskGetResponse; }; -/** - * Returns the workflow with the given name in the database. - * @summary Get Workflow - */ export type getWorkflowApiWorkflowNameGetResponse200 = { data: WorkflowQueryResponse; status: 200; @@ -6490,7 +6607,7 @@ export const getGetWorkflowApiWorkflowNameGetUrl = (name: string, params?: GetWo Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6499,6 +6616,10 @@ export const getGetWorkflowApiWorkflowNameGetUrl = (name: string, params?: GetWo return stringifiedParams.length > 0 ? `/api/workflow/${name}?${stringifiedParams}` : `/api/workflow/${name}`; }; +/** + * Returns the workflow with the given name in the database. + * @summary Get Workflow + */ export const getWorkflowApiWorkflowNameGet = async ( name: string, params?: GetWorkflowApiWorkflowNameGetParams, @@ -6515,10 +6636,6 @@ export const getWorkflowApiWorkflowNameGet = async ( return { data, status: res.status, headers: res.headers } as getWorkflowApiWorkflowNameGetResponse; }; -/** - * Returns the workflow logs. - * @summary Get Workflow Logs - */ export type getWorkflowLogsApiWorkflowNameLogsGetResponse200 = { data: string; status: 200; @@ -6548,7 +6665,7 @@ export const getGetWorkflowLogsApiWorkflowNameLogsGetUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6559,6 +6676,10 @@ export const getGetWorkflowLogsApiWorkflowNameLogsGetUrl = ( : `/api/workflow/${name}/logs`; }; +/** + * Returns the workflow logs. + * @summary Get Workflow Logs + */ export const getWorkflowLogsApiWorkflowNameLogsGet = async ( name: string, params?: GetWorkflowLogsApiWorkflowNameLogsGetParams, @@ -6569,16 +6690,17 @@ export const getWorkflowLogsApiWorkflowNameLogsGet = async ( method: "GET", }); + const contentType = (res.headers.get("content-type") ?? "").toLowerCase(); const body = [204, 205, 304].includes(res.status) ? null : await res.text(); - const data: getWorkflowLogsApiWorkflowNameLogsGetResponse["data"] = body ? JSON.parse(body) : {}; + const data: getWorkflowLogsApiWorkflowNameLogsGetResponse["data"] = body + ? contentType.includes("json") + ? JSON.parse(body) + : body + : {}; return { data, status: res.status, headers: res.headers } as getWorkflowLogsApiWorkflowNameLogsGetResponse; }; -/** - * Returns the workflow pod conditions. - * @summary Get Workflow Pod Conditions - */ export type getWorkflowPodConditionsApiWorkflowNameEventsGetResponse200 = { data: string; status: 200; @@ -6610,7 +6732,7 @@ export const getGetWorkflowPodConditionsApiWorkflowNameEventsGetUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6621,6 +6743,10 @@ export const getGetWorkflowPodConditionsApiWorkflowNameEventsGetUrl = ( : `/api/workflow/${name}/events`; }; +/** + * Returns the workflow pod conditions. + * @summary Get Workflow Pod Conditions + */ export const getWorkflowPodConditionsApiWorkflowNameEventsGet = async ( name: string, params?: GetWorkflowPodConditionsApiWorkflowNameEventsGetParams, @@ -6631,16 +6757,17 @@ export const getWorkflowPodConditionsApiWorkflowNameEventsGet = async ( method: "GET", }); + const contentType = (res.headers.get("content-type") ?? "").toLowerCase(); const body = [204, 205, 304].includes(res.status) ? null : await res.text(); - const data: getWorkflowPodConditionsApiWorkflowNameEventsGetResponse["data"] = body ? JSON.parse(body) : {}; + const data: getWorkflowPodConditionsApiWorkflowNameEventsGetResponse["data"] = body + ? contentType.includes("json") + ? JSON.parse(body) + : body + : {}; return { data, status: res.status, headers: res.headers } as getWorkflowPodConditionsApiWorkflowNameEventsGetResponse; }; -/** - * Returns the workflow error logs. - * @summary Get Workflow Error Logs - */ export type getWorkflowErrorLogsApiWorkflowNameErrorLogsGetResponse200 = { data: string; status: 200; @@ -6672,7 +6799,7 @@ export const getGetWorkflowErrorLogsApiWorkflowNameErrorLogsGetUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6683,6 +6810,10 @@ export const getGetWorkflowErrorLogsApiWorkflowNameErrorLogsGetUrl = ( : `/api/workflow/${name}/error_logs`; }; +/** + * Returns the workflow error logs. + * @summary Get Workflow Error Logs + */ export const getWorkflowErrorLogsApiWorkflowNameErrorLogsGet = async ( name: string, params?: GetWorkflowErrorLogsApiWorkflowNameErrorLogsGetParams, @@ -6693,16 +6824,17 @@ export const getWorkflowErrorLogsApiWorkflowNameErrorLogsGet = async ( method: "GET", }); + const contentType = (res.headers.get("content-type") ?? "").toLowerCase(); const body = [204, 205, 304].includes(res.status) ? null : await res.text(); - const data: getWorkflowErrorLogsApiWorkflowNameErrorLogsGetResponse["data"] = body ? JSON.parse(body) : {}; + const data: getWorkflowErrorLogsApiWorkflowNameErrorLogsGetResponse["data"] = body + ? contentType.includes("json") + ? JSON.parse(body) + : body + : {}; return { data, status: res.status, headers: res.headers } as getWorkflowErrorLogsApiWorkflowNameErrorLogsGetResponse; }; -/** - * Returns the workflow spec. - * @summary Get Workflow Spec - */ export type getWorkflowSpecApiWorkflowNameSpecGetResponse200 = { data: string; status: 200; @@ -6732,7 +6864,7 @@ export const getGetWorkflowSpecApiWorkflowNameSpecGetUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6743,6 +6875,10 @@ export const getGetWorkflowSpecApiWorkflowNameSpecGetUrl = ( : `/api/workflow/${name}/spec`; }; +/** + * Returns the workflow spec. + * @summary Get Workflow Spec + */ export const getWorkflowSpecApiWorkflowNameSpecGet = async ( name: string, params?: GetWorkflowSpecApiWorkflowNameSpecGetParams, @@ -6753,16 +6889,17 @@ export const getWorkflowSpecApiWorkflowNameSpecGet = async ( method: "GET", }); + const contentType = (res.headers.get("content-type") ?? "").toLowerCase(); const body = [204, 205, 304].includes(res.status) ? null : await res.text(); - const data: getWorkflowSpecApiWorkflowNameSpecGetResponse["data"] = body ? JSON.parse(body) : {}; + const data: getWorkflowSpecApiWorkflowNameSpecGetResponse["data"] = body + ? contentType.includes("json") + ? JSON.parse(body) + : body + : {}; return { data, status: res.status, headers: res.headers } as getWorkflowSpecApiWorkflowNameSpecGetResponse; }; -/** - * Returns the workflow spec. - * @summary Tag Workflow - */ export type tagWorkflowApiWorkflowNameTagPostResponse200 = { data: unknown; status: 200; @@ -6795,13 +6932,13 @@ export const getTagWorkflowApiWorkflowNameTagPostUrl = ( if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6810,6 +6947,10 @@ export const getTagWorkflowApiWorkflowNameTagPostUrl = ( return stringifiedParams.length > 0 ? `/api/workflow/${name}/tag?${stringifiedParams}` : `/api/workflow/${name}/tag`; }; +/** + * Returns the workflow spec. + * @summary Tag Workflow + */ export const tagWorkflowApiWorkflowNameTagPost = async ( name: string, params?: TagWorkflowApiWorkflowNameTagPostParams, @@ -6826,10 +6967,6 @@ export const tagWorkflowApiWorkflowNameTagPost = async ( return { data, status: res.status, headers: res.headers } as tagWorkflowApiWorkflowNameTagPostResponse; }; -/** - * Send command to all tasks in a group. - * @summary Exec Into Group - */ export type execIntoGroupApiWorkflowNameExecGroupGroupNamePostResponse200 = { data: ExecIntoGroupApiWorkflowNameExecGroupGroupNamePost200; status: 200; @@ -6862,7 +6999,7 @@ export const getExecIntoGroupApiWorkflowNameExecGroupGroupNamePostUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6873,6 +7010,10 @@ export const getExecIntoGroupApiWorkflowNameExecGroupGroupNamePostUrl = ( : `/api/workflow/${name}/exec/group/${groupName}`; }; +/** + * Send command to all tasks in a group. + * @summary Exec Into Group + */ export const execIntoGroupApiWorkflowNameExecGroupGroupNamePost = async ( name: string, groupName: string, @@ -6894,10 +7035,6 @@ export const execIntoGroupApiWorkflowNameExecGroupGroupNamePost = async ( } as execIntoGroupApiWorkflowNameExecGroupGroupNamePostResponse; }; -/** - * Exec into a task container. - * @summary Exec Into Task - */ export type execIntoTaskApiWorkflowNameExecTaskTaskNamePostResponse200 = { data: RouterResponse; status: 200; @@ -6930,7 +7067,7 @@ export const getExecIntoTaskApiWorkflowNameExecTaskTaskNamePostUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -6941,6 +7078,10 @@ export const getExecIntoTaskApiWorkflowNameExecTaskTaskNamePostUrl = ( : `/api/workflow/${name}/exec/task/${taskName}`; }; +/** + * Exec into a task container. + * @summary Exec Into Task + */ export const execIntoTaskApiWorkflowNameExecTaskTaskNamePost = async ( name: string, taskName: string, @@ -6958,10 +7099,6 @@ export const execIntoTaskApiWorkflowNameExecTaskTaskNamePost = async ( return { data, status: res.status, headers: res.headers } as execIntoTaskApiWorkflowNameExecTaskTaskNamePostResponse; }; -/** - * Portforward into a task container. - * @summary Port Forward Task - */ export type portForwardTaskApiWorkflowNamePortforwardTaskNamePostResponse200 = { data: RouterResponse[] | RouterResponse; status: 200; @@ -6997,13 +7134,13 @@ export const getPortForwardTaskApiWorkflowNamePortforwardTaskNamePostUrl = ( if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -7014,6 +7151,10 @@ export const getPortForwardTaskApiWorkflowNamePortforwardTaskNamePostUrl = ( : `/api/workflow/${name}/portforward/${taskName}`; }; +/** + * Portforward into a task container. + * @summary Port Forward Task + */ export const portForwardTaskApiWorkflowNamePortforwardTaskNamePost = async ( name: string, taskName: string, @@ -7035,10 +7176,6 @@ export const portForwardTaskApiWorkflowNamePortforwardTaskNamePost = async ( } as portForwardTaskApiWorkflowNamePortforwardTaskNamePostResponse; }; -/** - * Hold a webserver connection to a task container. - * @summary Port Forward Webserver - */ export type portForwardWebserverApiWorkflowNameWebserverTaskNamePostResponse200 = { data: RouterResponse; status: 200; @@ -7071,7 +7208,7 @@ export const getPortForwardWebserverApiWorkflowNameWebserverTaskNamePostUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -7082,6 +7219,10 @@ export const getPortForwardWebserverApiWorkflowNameWebserverTaskNamePostUrl = ( : `/api/workflow/${name}/webserver/${taskName}`; }; +/** + * Hold a webserver connection to a task container. + * @summary Port Forward Webserver + */ export const portForwardWebserverApiWorkflowNameWebserverTaskNamePost = async ( name: string, taskName: string, @@ -7103,10 +7244,6 @@ export const portForwardWebserverApiWorkflowNameWebserverTaskNamePost = async ( } as portForwardWebserverApiWorkflowNameWebserverTaskNamePostResponse; }; -/** - * Rsync into a task container. - * @summary Rsync Task - */ export type rsyncTaskApiWorkflowNameRsyncTaskTaskNamePostResponse200 = { data: RouterResponse; status: 200; @@ -7134,6 +7271,10 @@ export const getRsyncTaskApiWorkflowNameRsyncTaskTaskNamePostUrl = (name: string return `/api/workflow/${name}/rsync/task/${taskName}`; }; +/** + * Rsync into a task container. + * @summary Rsync Task + */ export const rsyncTaskApiWorkflowNameRsyncTaskTaskNamePost = async ( name: string, taskName: string, @@ -7150,10 +7291,6 @@ export const rsyncTaskApiWorkflowNameRsyncTaskTaskNamePost = async ( return { data, status: res.status, headers: res.headers } as rsyncTaskApiWorkflowNameRsyncTaskTaskNamePostResponse; }; -/** - * Get default/all user credentials - * @summary Get User Credential - */ export type getUserCredentialApiCredentialsGetResponse200 = { data: CredentialGetResponse; status: 200; @@ -7179,6 +7316,10 @@ export const getGetUserCredentialApiCredentialsGetUrl = () => { return `/api/credentials`; }; +/** + * Get default/all user credentials + * @summary Get User Credential + */ export const getUserCredentialApiCredentialsGet = async ( options?: RequestInit, ): Promise => { @@ -7193,10 +7334,6 @@ export const getUserCredentialApiCredentialsGet = async ( return { data, status: res.status, headers: res.headers } as getUserCredentialApiCredentialsGetResponse; }; -/** - * Post/Update user credentials - * @summary Set User Credential - */ export type setUserCredentialApiCredentialsCredNamePostResponse200 = { data: unknown; status: 200; @@ -7224,6 +7361,10 @@ export const getSetUserCredentialApiCredentialsCredNamePostUrl = (credName: stri return `/api/credentials/${credName}`; }; +/** + * Post/Update user credentials + * @summary Set User Credential + */ export const setUserCredentialApiCredentialsCredNamePost = async ( credName: string, credentialOptions: CredentialOptions, @@ -7242,10 +7383,6 @@ export const setUserCredentialApiCredentialsCredNamePost = async ( return { data, status: res.status, headers: res.headers } as setUserCredentialApiCredentialsCredNamePostResponse; }; -/** - * Delete user credentials given the secret_id - * @summary Delete Users Credential - */ export type deleteUsersCredentialApiCredentialsCredNameDeleteResponse200 = { data: CredentialGetResponse; status: 200; @@ -7273,6 +7410,10 @@ export const getDeleteUsersCredentialApiCredentialsCredNameDeleteUrl = (credName return `/api/credentials/${credName}`; }; +/** + * Delete user credentials given the secret_id + * @summary Delete Users Credential + */ export const deleteUsersCredentialApiCredentialsCredNameDelete = async ( credName: string, options?: RequestInit, @@ -7292,10 +7433,6 @@ export const deleteUsersCredentialApiCredentialsCredNameDelete = async ( } as deleteUsersCredentialApiCredentialsCredNameDeleteResponse; }; -/** - * Returns the information of resources available in different pools. - * @summary Get Resources - */ export type getResourcesApiResourcesGetResponse200 = { data: ResourcesResponse | PoolResourcesResponse; status: 200; @@ -7325,13 +7462,13 @@ export const getGetResourcesApiResourcesGetUrl = (params?: GetResourcesApiResour if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -7340,6 +7477,10 @@ export const getGetResourcesApiResourcesGetUrl = (params?: GetResourcesApiResour return stringifiedParams.length > 0 ? `/api/resources?${stringifiedParams}` : `/api/resources`; }; +/** + * Returns the information of resources available in different pools. + * @summary Get Resources + */ export const getResourcesApiResourcesGet = async ( params?: GetResourcesApiResourcesGetParams, options?: RequestInit, @@ -7355,10 +7496,6 @@ export const getResourcesApiResourcesGet = async ( return { data, status: res.status, headers: res.headers } as getResourcesApiResourcesGetResponse; }; -/** - * Returns the request resource's information. - * @summary Get One Resource - */ export type getOneResourceApiResourcesNameGetResponse200 = { data: ResourcesResponse; status: 200; @@ -7384,6 +7521,10 @@ export const getGetOneResourceApiResourcesNameGetUrl = (name: string) => { return `/api/resources/${name}`; }; +/** + * Returns the request resource's information. + * @summary Get One Resource + */ export const getOneResourceApiResourcesNameGet = async ( name: string, options?: RequestInit, @@ -7399,14 +7540,6 @@ export const getOneResourceApiResourcesNameGet = async ( return { data, status: res.status, headers: res.headers } as getOneResourceApiResourcesNameGetResponse; }; -/** - * Returns information regarding pools to users. - -If all_pools is set to true, all pools' information will be returned in API response. -Otherwise, only information from pools that the user has access to will be returned -in the response. - * @summary Get Pools - */ export type getPoolsApiPoolGetResponse200 = { data: MinimalPoolConfig; status: 200; @@ -7434,13 +7567,13 @@ export const getGetPoolsApiPoolGetUrl = (params?: GetPoolsApiPoolGetParams) => { if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -7449,6 +7582,14 @@ export const getGetPoolsApiPoolGetUrl = (params?: GetPoolsApiPoolGetParams) => { return stringifiedParams.length > 0 ? `/api/pool?${stringifiedParams}` : `/api/pool`; }; +/** + * Returns information regarding pools to users. + * + * If all_pools is set to true, all pools' information will be returned in API response. + * Otherwise, only information from pools that the user has access to will be returned + * in the response. + * @summary Get Pools + */ export const getPoolsApiPoolGet = async ( params?: GetPoolsApiPoolGetParams, options?: RequestInit, @@ -7464,9 +7605,6 @@ export const getPoolsApiPoolGet = async ( return { data, status: res.status, headers: res.headers } as getPoolsApiPoolGetResponse; }; -/** - * @summary Get Pool Quotas - */ export type getPoolQuotasApiPoolQuotaGetResponse200 = { data: PoolResponse; status: 200; @@ -7496,13 +7634,13 @@ export const getGetPoolQuotasApiPoolQuotaGetUrl = (params?: GetPoolQuotasApiPool if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -7511,6 +7649,9 @@ export const getGetPoolQuotasApiPoolQuotaGetUrl = (params?: GetPoolQuotasApiPool return stringifiedParams.length > 0 ? `/api/pool_quota?${stringifiedParams}` : `/api/pool_quota`; }; +/** + * @summary Get Pool Quotas + */ export const getPoolQuotasApiPoolQuotaGet = async ( params?: GetPoolQuotasApiPoolQuotaGetParams, options?: RequestInit, @@ -7526,10 +7667,6 @@ export const getPoolQuotasApiPoolQuotaGet = async ( return { data, status: res.status, headers: res.headers } as getPoolQuotasApiPoolQuotaGetResponse; }; -/** - * This api validates that a workflow is well formed and valid and then submits it. - * @summary Submit Workflow - */ export type submitWorkflowApiPoolPoolNameWorkflowPostResponse200 = { data: SubmitResponse; status: 200; @@ -7560,17 +7697,17 @@ export const getSubmitWorkflowApiPoolPoolNameWorkflowPostUrl = ( const normalizedParams = new URLSearchParams(); Object.entries(params || {}).forEach(([key, value]) => { - const explodeParameters = ["env_vars"]; + const explodeParameters = ["env_vars", "label"]; if (Array.isArray(value) && explodeParameters.includes(key)) { value.forEach((v) => { - normalizedParams.append(key, v === null ? "null" : v.toString()); + normalizedParams.append(key, v === null ? "null" : String(v)); }); return; } if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -7581,9 +7718,13 @@ export const getSubmitWorkflowApiPoolPoolNameWorkflowPostUrl = ( : `/api/pool/${poolName}/workflow`; }; +/** + * This api validates that a workflow is well formed and valid and then submits it. + * @summary Submit Workflow + */ export const submitWorkflowApiPoolPoolNameWorkflowPost = async ( poolName: string, - templateSpecNull: TemplateSpec | null, + templateSpecNull?: TemplateSpec | null, params?: SubmitWorkflowApiPoolPoolNameWorkflowPostParams, options?: RequestInit, ): Promise => { @@ -7600,10 +7741,6 @@ export const submitWorkflowApiPoolPoolNameWorkflowPost = async ( return { data, status: res.status, headers: res.headers } as submitWorkflowApiPoolPoolNameWorkflowPostResponse; }; -/** - * This api restarts a failed workflow and then submits it. - * @summary Restart Workflow - */ export type restartWorkflowApiPoolPoolNameWorkflowWorkflowIdRestartPostResponse200 = { data: SubmitResponse; status: 200; @@ -7634,6 +7771,10 @@ export const getRestartWorkflowApiPoolPoolNameWorkflowWorkflowIdRestartPostUrl = return `/api/pool/${poolName}/workflow/${workflowId}/restart`; }; +/** + * This api restarts a failed workflow and then submits it. + * @summary Restart Workflow + */ export const restartWorkflowApiPoolPoolNameWorkflowWorkflowIdRestartPost = async ( poolName: string, workflowId: string, @@ -7656,9 +7797,6 @@ export const restartWorkflowApiPoolPoolNameWorkflowWorkflowIdRestartPost = async } as restartWorkflowApiPoolPoolNameWorkflowWorkflowIdRestartPostResponse; }; -/** - * @summary Get Notification Settings - */ export type getNotificationSettingsApiProfileSettingsGetResponse200 = { data: ProfileResponse; status: 200; @@ -7686,6 +7824,9 @@ export const getGetNotificationSettingsApiProfileSettingsGetUrl = () => { return `/api/profile/settings`; }; +/** + * @summary Get Notification Settings + */ export const getNotificationSettingsApiProfileSettingsGet = async ( options?: RequestInit, ): Promise => { @@ -7700,9 +7841,6 @@ export const getNotificationSettingsApiProfileSettingsGet = async ( return { data, status: res.status, headers: res.headers } as getNotificationSettingsApiProfileSettingsGetResponse; }; -/** - * @summary Set Notification Settings - */ export type setNotificationSettingsApiProfileSettingsPostResponse200 = { data: unknown; status: 200; @@ -7733,7 +7871,7 @@ export const getSetNotificationSettingsApiProfileSettingsPostUrl = ( Object.entries(params || {}).forEach(([key, value]) => { if (value !== undefined) { - normalizedParams.append(key, value === null ? "null" : value.toString()); + normalizedParams.append(key, value === null ? "null" : String(value)); } }); @@ -7742,6 +7880,9 @@ export const getSetNotificationSettingsApiProfileSettingsPostUrl = ( return stringifiedParams.length > 0 ? `/api/profile/settings?${stringifiedParams}` : `/api/profile/settings`; }; +/** + * @summary Set Notification Settings + */ export const setNotificationSettingsApiProfileSettingsPost = async ( userProfile: UserProfile, params?: SetNotificationSettingsApiProfileSettingsPostParams, @@ -7760,9 +7901,6 @@ export const setNotificationSettingsApiProfileSettingsPost = async ( return { data, status: res.status, headers: res.headers } as setNotificationSettingsApiProfileSettingsPostResponse; }; -/** - * @summary Get Osmo Client Version - */ export type getOsmoClientVersionClientVersionGetResponse200 = { data: unknown; status: 200; @@ -7777,6 +7915,9 @@ export const getGetOsmoClientVersionClientVersionGetUrl = () => { return `/client/version`; }; +/** + * @summary Get Osmo Client Version + */ export const getOsmoClientVersionClientVersionGet = async ( options?: RequestInit, ): Promise => { @@ -7791,11 +7932,6 @@ export const getOsmoClientVersionClientVersionGet = async ( return { data, status: res.status, headers: res.headers } as getOsmoClientVersionClientVersionGetResponse; }; -/** - * To be used for the readiness probe, but not liveness probe. That way, if this method is -slow, no new traffic gets routed, instead of killing the service. - * @summary Health - */ export type healthHealthGetResponse200 = { data: HealthHealthGet200; status: 200; @@ -7810,6 +7946,11 @@ export const getHealthHealthGetUrl = () => { return `/health`; }; +/** + * To be used for the readiness probe, but not liveness probe. That way, if this method is + * slow, no new traffic gets routed, instead of killing the service. + * @summary Health + */ export const healthHealthGet = async (options?: RequestInit): Promise => { const res = await fetch(getHealthHealthGetUrl(), { ...options, @@ -7822,9 +7963,6 @@ export const healthHealthGet = async (options?: RequestInit): Promise { return `/api/version`; }; +/** + * @summary Get Version + */ export const getVersionApiVersionGet = async (options?: RequestInit): Promise => { const res = await fetch(getGetVersionApiVersionGetUrl(), { ...options, @@ -7851,10 +7992,6 @@ export const getVersionApiVersionGet = async (options?: RequestInit): Promise { return `/api/users`; }; +/** + * Returns the values of all users who have submitted a workflow. + * @summary Get Users + */ export const getUsersApiUsersGet = async (options?: RequestInit): Promise => { const res = await fetch(getGetUsersApiUsersGetUrl(), { ...options, @@ -7881,10 +8022,6 @@ export const getUsersApiUsersGet = async (options?: RequestInit): Promise { return `/api/tag`; }; +/** + * Returns all workflow tags. + * @summary Get Available Workflow Tags + */ export const getAvailableWorkflowTagsApiTagGet = async ( options?: RequestInit, ): Promise => { @@ -7913,10 +8054,6 @@ export const getAvailableWorkflowTagsApiTagGet = async ( return { data, status: res.status, headers: res.headers } as getAvailableWorkflowTagsApiTagGetResponse; }; -/** - * Get all the workflow plugins configurations - * @summary Get Workflow Plugins Configs - */ export type getWorkflowPluginsConfigsApiPluginsConfigsGetResponse200 = { data: PluginsConfigOutput; status: 200; @@ -7933,6 +8070,10 @@ export const getGetWorkflowPluginsConfigsApiPluginsConfigsGetUrl = () => { return `/api/plugins/configs`; }; +/** + * Get all the workflow plugins configurations + * @summary Get Workflow Plugins Configs + */ export const getWorkflowPluginsConfigsApiPluginsConfigsGet = async ( options?: RequestInit, ): Promise => { @@ -8212,6 +8353,16 @@ export const getReadWorkflowConfigsApiConfigsWorkflowGetResponseMock = ( client_upload_rate_limit: faker.number.int({ min: 0 }), }, }, + labels_config: { + policy: Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ + key: faker.string.alpha({ length: { min: 10, max: 20 } }), + allow_list: Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.string.alpha({ length: { min: 10, max: 20 } }), + ), + enforcement: faker.helpers.arrayElement(Object.values(LabelEnforcement)), + assert_message: faker.string.alpha({ length: { min: 10, max: 20 } }), + })), + }, max_num_tasks: faker.number.int(), max_num_ports_per_task: faker.number.int(), max_retry_per_task: faker.number.int(), @@ -8966,7 +9117,7 @@ export const getGetConfigsHistoryApiConfigsHistoryGetResponseMock = ( overrideResponse: Partial> = {}, ): GetConfigsHistoryResponse => ({ configs: Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ - config_type: faker.helpers.arrayElement(Object.values(SrcLibUtilsConfigHistoryConfigHistoryType)), + config_type: faker.helpers.arrayElement(Object.values(ConfigHistoryType)), name: faker.string.alpha({ length: { min: 10, max: 20 } }), revision: faker.number.int(), username: faker.string.alpha({ length: { min: 10, max: 20 } }), @@ -9300,6 +9451,12 @@ export const getListWorkflowApiWorkflowGetResponseMock = ( ]), app_version: faker.helpers.arrayElement([faker.helpers.arrayElement([faker.number.int(), null]), undefined]), priority: faker.string.alpha({ length: { min: 10, max: 20 } }), + labels: faker.helpers.arrayElement([ + { + [faker.string.alphanumeric(5)]: faker.string.alpha({ length: { min: 10, max: 20 } }), + }, + undefined, + ]), })), more_entries: faker.datatype.boolean(), ...overrideResponse, @@ -9598,6 +9755,18 @@ export const getGetWorkflowApiWorkflowNameGetResponseMock = ( app_version: faker.helpers.arrayElement([faker.helpers.arrayElement([faker.number.int(), null]), undefined]), plugins: { rsync: faker.datatype.boolean() }, priority: faker.string.alpha({ length: { min: 10, max: 20 } }), + labels: faker.helpers.arrayElement([ + { + [faker.string.alphanumeric(5)]: faker.string.alpha({ length: { min: 10, max: 20 } }), + }, + undefined, + ]), + warnings: faker.helpers.arrayElement([ + Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.string.alpha({ length: { min: 10, max: 20 } }), + ), + undefined, + ]), ...overrideResponse, }); @@ -9924,6 +10093,12 @@ export const getSubmitWorkflowApiPoolPoolNameWorkflowPostResponseMock = ( faker.helpers.arrayElement([faker.string.alpha({ length: { min: 10, max: 20 } }), null]), undefined, ]), + warnings: faker.helpers.arrayElement([ + Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.string.alpha({ length: { min: 10, max: 20 } }), + ), + undefined, + ]), ...overrideResponse, }); @@ -9947,6 +10122,12 @@ export const getRestartWorkflowApiPoolPoolNameWorkflowWorkflowIdRestartPostRespo faker.helpers.arrayElement([faker.string.alpha({ length: { min: 10, max: 20 } }), null]), undefined, ]), + warnings: faker.helpers.arrayElement([ + Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => + faker.string.alpha({ length: { min: 10, max: 20 } }), + ), + undefined, + ]), ...overrideResponse, });