Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions frontend/src/components/agency/agency/Agency.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -327,25 +327,26 @@ function Agency() {
return;
}

const apiDeploymentData = apiDeployments?.data || [];
const pipelineData = pipelines?.data || [];
const apiDeploymentData =
apiDeployments?.data?.results || apiDeployments?.data || [];
const pipelineData = pipelines?.data?.results || pipelines?.data || [];

// Find active deployments
const activeApiDeployment = apiDeploymentData.find(
(deployment) => deployment.is_active,
);
// Show banner for any API deployment linked to this workflow, prefer active one
const apiDeployment =
apiDeploymentData.find((deployment) => deployment.is_active) ||
apiDeploymentData[0];

// For pipelines, any pipeline associated with this workflow is considered a deployment
// regardless of active status, since workflows can only have one deployment
const workflowPipelines = pipelineData;

// Set deployment info
let deploymentInfo = null;
if (activeApiDeployment) {
if (apiDeployment) {
deploymentInfo = {
type: "API",
name: activeApiDeployment.display_name,
id: activeApiDeployment.id,
name: apiDeployment.display_name,
id: apiDeployment.id,
};
} else if (workflowPipelines.length > 0) {
// If multiple pipelines, prioritize by type: ETL > TASK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import { NotificationModal } from "../../pipelines-or-deployments/notification-m
import { SharePermission } from "../../widgets/share-permission/SharePermission";
import { workflowService } from "../../workflows/workflow/workflow-service.js";
import { CreateApiDeploymentModal } from "../create-api-deployment-modal/CreateApiDeploymentModal";
import { DeleteModal } from "../delete-modal/DeleteModal";
import { DisplayCode } from "../display-code/DisplayCode";
import { Layout } from "../layout/Layout";
import { ManageKeys } from "../manage-keys/ManageKeys";
Expand All @@ -38,7 +37,6 @@ function ApiDeployment() {
const workflowApiService = workflowService();
const [isTableLoading, setIsTableLoading] = useState(true);
const [openAddApiModal, setOpenAddApiModal] = useState(false);
const [openDeleteModal, setOpenDeleteModal] = useState(false);
const [openCodeModal, setOpenCodeModal] = useState(false);
const [openManageKeysModal, setOpenManageKeysModal] = useState(false);
const [selectedRow, setSelectedRow] = useState({});
Expand Down Expand Up @@ -164,11 +162,11 @@ function ApiDeployment() {

fetchListRef.current = getApiDeploymentList;

const deleteApiDeployment = () => {
const deleteApiDeployment = (item) => {
const id = item?.id || selectedRow.id;
apiDeploymentsApiService
.deleteApiDeployment(selectedRow.id)
.then((res) => {
setOpenDeleteModal(false);
.deleteApiDeployment(id)
.then(() => {
getApiDeploymentList(
pagination.current,
pagination.pageSize,
Expand Down Expand Up @@ -215,8 +213,8 @@ function ApiDeployment() {
openAddModal(true);
};

const handleDeleteDeployment = () => {
setOpenDeleteModal(true);
const handleDeleteDeployment = (item) => {
deleteApiDeployment(item);
};

const handleViewLogsDeployment = (deployment) => {
Expand Down Expand Up @@ -306,6 +304,7 @@ function ApiDeployment() {
pageSize: pagination.pageSize,
total: pagination.total,
onChange: handlePaginationChange,
itemLabel: "APIs",
}}
/>
{openAddApiModal && (
Expand All @@ -320,11 +319,6 @@ function ApiDeployment() {
workflowEndpointList={workflowEndpointList}
/>
)}
<DeleteModal
open={openDeleteModal}
setOpen={setOpenDeleteModal}
deleteRecord={deleteApiDeployment}
/>
<ManageKeys
isDialogOpen={openManageKeysModal}
setDialogOpen={setOpenManageKeysModal}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const CreateApiDeploymentModal = ({
openCodeModal,
setSelectedRow,
workflowId,
workflowEndpointList,
workflowEndpointList = [],
setDeploymentName,
onDeploymentCreated,
}) => {
Expand Down Expand Up @@ -83,7 +83,7 @@ const CreateApiDeploymentModal = ({
};

const createApiDeployment = () => {
const wf = workflowEndpointList.find(
const wf = workflowEndpointList?.find(
(item) => item?.workflow === formDetails?.workflow,
);
setPostHogCustomEvent("intent_success_api_deployment", {
Expand Down Expand Up @@ -262,7 +262,7 @@ const CreateApiDeploymentModal = ({
help={getBackendErrorDetail("workflow", backendErrors)}
>
<Select>
{workflowEndpointList.map((endpoint) => {
{workflowEndpointList?.map((endpoint) => {
return (
<Option
value={endpoint.workflow}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ function ExecutionLogs() {
];
const onChange = (key) => {
navigate(`/${sessionDetails?.orgName}/logs`);
setPagination((prev) => ({ ...prev, current: 1 }));
setActiveTab(key);
};
const onOk = (value) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { useAxiosPrivate } from "../../../hooks/useAxiosPrivate.js";
import { useAlertStore } from "../../../store/alert-store.js";
import { useSessionStore } from "../../../store/session-store.js";
import { Layout } from "../../deployments/layout/Layout.jsx";
import { DeleteModal } from "../delete-modal/DeleteModal.jsx";
import { EtlTaskDeploy } from "../etl-task-deploy/EtlTaskDeploy.jsx";
import FileHistoryModal from "../file-history-modal/FileHistoryModal.jsx";
import { LogsModal } from "../log-modal/LogsModal.jsx";
Expand Down Expand Up @@ -38,7 +37,6 @@ import { createPipelineCardConfig } from "./PipelineCardConfig.jsx";
function Pipelines({ type }) {
const [tableData, setTableData] = useState([]);
const [openEtlOrTaskModal, setOpenEtlOrTaskModal] = useState(false);
const [openDeleteModal, setOpenDeleteModal] = useState(false);
const [selectedPorD, setSelectedPorD] = useState({});
const [tableLoading, setTableLoading] = useState(true);
const { sessionDetails } = useSessionStore();
Expand Down Expand Up @@ -169,24 +167,16 @@ function Pipelines({ type }) {
const handleSync = (params) => {
const body = { ...params, pipeline_type: type.toUpperCase() };
const pipelineId = params?.pipeline_id;
const fieldsToUpdate = {
last_run_status: "processing",
};
handleLoaderInTableData(fieldsToUpdate, pipelineId);

handleSyncApiReq(body)
.then((res) => {
const data = res?.data?.pipeline;
fieldsToUpdate.last_run_status = data?.last_run_status;
fieldsToUpdate.last_run_time = data?.last_run_time;
const pipelineData = res?.data?.pipeline;
if (pipelineData) {
handleLoaderInTableData(pipelineData, pipelineId);
}
})
.catch((err) => {
setAlertDetails(handleException(err, "Failed to sync."));
fieldsToUpdate.last_run_status = "FAILURE";
fieldsToUpdate.last_run_time = new Date().toISOString();
})
.finally(() => {
handleLoaderInTableData(fieldsToUpdate, pipelineId);
});
};

Expand Down Expand Up @@ -240,18 +230,17 @@ function Pipelines({ type }) {
});
};

const deletePipeline = () => {
const deletePipeline = (item) => {
const id = item?.id || selectedPorD.id;
const requestOptions = {
method: "DELETE",
url: `/api/v1/unstract/${sessionDetails?.orgId}/pipeline/${selectedPorD.id}/`,
url: `/api/v1/unstract/${sessionDetails?.orgId}/pipeline/${id}/`,
headers: {
"X-CSRFToken": sessionDetails?.csrfToken,
},
};
axiosPrivate(requestOptions)
.then(() => {
setOpenDeleteModal(false);
// Refresh with current pagination
getPipelineList(pagination.current, pagination.pageSize, searchTerm);
setAlertDetails({
type: "success",
Expand All @@ -265,19 +254,16 @@ function Pipelines({ type }) {

const clearFileMarkers = async (workflowId) => {
const id = workflowId || selectedPorD?.workflow_id;
const success = await clearFileHistory(id);
if (success && openDeleteModal) {
setOpenDeleteModal(false);
}
await clearFileHistory(id);
};

// Handlers for icon actions (top-right)
const handleEditPipeline = () => {
openAddModal(true);
};

const handleDeletePipeline = () => {
setOpenDeleteModal(true);
const handleDeletePipeline = (item) => {
deletePipeline(item);
};

// Handlers for expanded view actions
Expand Down Expand Up @@ -394,6 +380,7 @@ function Pipelines({ type }) {
pageSize: pagination.pageSize,
total: pagination.total,
onChange: handlePaginationChange,
itemLabel: "pipelines",
}}
/>
{openEtlOrTaskModal && (
Expand All @@ -416,11 +403,6 @@ function Pipelines({ type }) {
fetchExecutionLogs={handleFetchLogs}
loading={isFetchingLogs}
/>
<DeleteModal
open={openDeleteModal}
setOpen={setOpenDeleteModal}
deleteRecord={deletePipeline}
/>
<ManageKeys
isDialogOpen={openManageKeysModal}
setDialogOpen={setOpenManageKeysModal}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,16 +293,6 @@ function ApiEndpointSection({ apiEndpoint }) {
return null;
}

// Validate URL scheme to prevent javascript: or other malicious protocols
const isValidUrl = (() => {
try {
const parsed = new URL(apiEndpoint, globalThis.location.origin);
return ["http:", "https:"].includes(parsed.protocol);
} catch {
return false;
}
})();

return (
<div className="card-list-endpoint-wrapper">
<Card size="small" className="card-list-endpoint-row">
Expand All @@ -312,19 +302,9 @@ function ApiEndpointSection({ apiEndpoint }) {
</Typography.Text>
<div className="card-list-endpoint-value">
<Tooltip title={apiEndpoint} overlayStyle={{ maxWidth: 500 }}>
{isValidUrl ? (
<Typography.Link
href={apiEndpoint}
target="_blank"
onClick={(e) => e.stopPropagation()}
>
{shortenApiEndpoint(apiEndpoint)}
</Typography.Link>
) : (
<Typography.Text>
{shortenApiEndpoint(apiEndpoint)}
</Typography.Text>
)}
<Typography.Text>
{shortenApiEndpoint(apiEndpoint)}
</Typography.Text>
</Tooltip>
<Tooltip title="Copy endpoint">
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ function CardGridView({
showSizeChanger
pageSizeOptions={["10", "20", "50"]}
showTotal={(total, range) =>
`${range[0]}-${range[1]} of ${total} items`
`${range[0]}-${range[1]} of ${total} ${pagination.itemLabel || "items"}`
}
/>
</Flex>
Expand Down Expand Up @@ -196,6 +196,7 @@ CardGridView.propTypes = {
pageSize: PropTypes.number,
total: PropTypes.number,
onChange: PropTypes.func,
itemLabel: PropTypes.string,
}),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,23 @@ def parse_azure_error(e: Exception) -> ConnectorError:
client_error = e.message if hasattr(e, "message") else str(e)
error_message += (
f"Authentication failed. Please check your connection credentials. \n"
f"Error: \n```\n{client_error}\n```"
f"```\n{client_error}\n```"
)
# ClientAuthenticationError typically indicates 401 Unauthorized
status_code = getattr(e, "status_code", None) or 401
return ConnectorError(error_message, status_code=status_code)
elif isinstance(e, AzureException.ServiceRequestError):
client_error = e.message if hasattr(e, "message") else str(e)
error_message += (
f"Failed to connect to Azure service. \n" f"Error: \n```\n{client_error}\n```"
)
error_message += f"Failed to connect to Azure service. \n```\n{client_error}\n```"
return ConnectorError(error_message)
elif isinstance(e, AzureException.HttpResponseError):
client_error = e.message if hasattr(e, "message") else str(e)
error_message += (
f"Azure service returned an error response. \n"
f"Error: \n```\n{client_error}\n```"
f"Azure service returned an error response. \n```\n{client_error}\n```"
)
# Preserve the HTTP status code from Azure's response
status_code = getattr(e, "status_code", None)
return AzureHttpError(error_message, status_code=status_code)
else:
error_message += (
f"Error from Azure Cloud Storage. \n" f"Error: \n```\n{str(e)}\n```"
)
error_message += f"Error from Azure Cloud Storage. \n```\n{str(e)}\n```"
return ConnectorError(error_message)
Loading