Compare commits

..

2 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
752fe5259d Initial plan 2026-01-19 13:08:04 +00:00
Sakshi Gupta
b8bf30551d updated the text 2026-01-19 18:35:48 +05:30
11 changed files with 100 additions and 65 deletions

View File

@@ -46,10 +46,6 @@ export type DataExploreMessageV3 =
params: {
updateType: "created" | "deleted" | "settings";
};
}
| {
type: FabricMessageTypes.RestoreContainer;
params: [];
};
export interface GetCosmosTokenMessageOptions {
verb: "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace";

View File

@@ -516,7 +516,7 @@ describe("CopyJobActionMenu", () => {
expect(screen.getByText("Cancel")).toBeInTheDocument();
});
it("should disable complete action when job is being updated", () => {
it("should handle complete action disabled state for online jobs", () => {
const job = createMockJob({
Status: CopyJobStatusType.InProgress,
Mode: CopyJobMigrationType.Online,
@@ -530,34 +530,8 @@ describe("CopyJobActionMenu", () => {
const completeButton = screen.getByText("Complete");
fireEvent.click(completeButton);
// Simulate dialog confirmation to trigger state update
const [, , , onOkCallback] = mockShowOkCancelModalDialog.mock.calls[0];
onOkCallback();
fireEvent.click(actionButton);
const completeButtonAfterClick = screen.getByText("Complete").closest("button");
expect(completeButtonAfterClick).toBeInTheDocument();
expect(completeButtonAfterClick).toHaveAttribute("aria-disabled", "true");
});
it("should disable complete action when any other action is being performed", () => {
const job = createMockJob({
Status: CopyJobStatusType.InProgress,
Mode: CopyJobMigrationType.Online,
});
render(<TestComponentWrapper job={job} />);
const actionButton = screen.getByRole("button", { name: "Actions" });
fireEvent.click(actionButton);
const pauseButton = screen.getByText("Pause");
fireEvent.click(pauseButton);
fireEvent.click(actionButton);
const completeButtonAfterClick = screen.getByText("Complete").closest("button");
expect(completeButtonAfterClick).toBeInTheDocument();
expect(completeButtonAfterClick).toHaveAttribute("aria-disabled", "true");
expect(screen.getByText("Complete")).toBeInTheDocument();
});
});

View File

@@ -61,6 +61,7 @@ const CopyJobActionMenu: React.FC<CopyJobActionMenuProps> = ({ job, handleClick
const getMenuItems = (): IContextualMenuProps["items"] => {
const isThisJobUpdating = updatingJobAction?.jobName === job.Name;
const updatingAction = updatingJobAction?.action;
const baseItems = [
{
@@ -104,7 +105,7 @@ const CopyJobActionMenu: React.FC<CopyJobActionMenuProps> = ({ job, handleClick
text: ContainerCopyMessages.MonitorJobs.Actions.complete,
iconProps: { iconName: "CheckMark" },
onClick: () => showActionConfirmationDialog(job, CopyJobActions.complete),
disabled: isThisJobUpdating,
disabled: isThisJobUpdating && updatingAction === CopyJobActions.complete,
});
}
return filteredItems;

View File

@@ -7,7 +7,7 @@ import {
AddGlobalSecondaryIndexPanelProps,
} from "Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel";
import { useDatabases } from "Explorer/useDatabases";
import { isFabric, isFabricNative, openRestoreContainerDialog } from "Platform/Fabric/FabricUtil";
import { isFabric, isFabricNative } from "Platform/Fabric/FabricUtil";
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
import { ReactTabKind, useTabs } from "hooks/useTabs";
@@ -35,7 +35,6 @@ import StoredProcedure from "./Tree/StoredProcedure";
import Trigger from "./Tree/Trigger";
import UserDefinedFunction from "./Tree/UserDefinedFunction";
import { useSelectedNode } from "./useSelectedNode";
import { extractFeatures } from "../Platform/Hosted/extractFeatures";
export interface CollectionContextMenuButtonParams {
databaseId: string;
@@ -61,17 +60,6 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin
},
];
if (isFabricNative() && !userContext.fabricContext?.isReadOnly) {
const features = extractFeatures();
if (features?.enableRestoreContainer) {
items.push({
iconSrc: AddCollectionIcon,
onClick: () => openRestoreContainerDialog(),
label: `Restore ${getCollectionName()}`,
});
}
}
if (!isFabricNative() && (userContext.apiType !== "Tables" || userContext.features.enableSDKoperations)) {
items.push({
iconSrc: DeleteDatabaseIcon,

View File

@@ -54,6 +54,6 @@
.mainButtonsContainer {
display: flex;
gap: 0 16px;
margin: 40px auto
margin-bottom: 10px
}

View File

@@ -105,12 +105,9 @@ const App = (): JSX.Element => {
// Scenario-based health tracking: start ApplicationLoad and complete phases.
const { startScenario, completePhase } = useMetricScenario();
React.useEffect(() => {
// Only start scenario after config is initialized to avoid race conditions
// with message handlers that depend on configContext.platform
if (config) {
startScenario(MetricScenario.ApplicationLoad);
}
}, [config, startScenario]);
startScenario(MetricScenario.ApplicationLoad);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
React.useEffect(() => {
if (explorer) {

View File

@@ -0,0 +1,88 @@
import { initializeIcons } from "@fluentui/react";
import "bootstrap/dist/css/bootstrap.css";
import React from "react";
import * as ReactDOM from "react-dom";
import { configContext, initializeConfiguration } from "../ConfigContext";
import { GalleryHeaderComponent } from "../Explorer/Controls/Header/GalleryHeaderComponent";
import { GalleryTab } from "../Explorer/Controls/NotebookGallery/GalleryViewerComponent";
import {
NotebookViewerComponent,
NotebookViewerComponentProps,
} from "../Explorer/Controls/NotebookViewer/NotebookViewerComponent";
import * as FileSystemUtil from "../Explorer/Notebook/FileSystemUtil";
import { IGalleryItem, JunoClient } from "../Juno/JunoClient";
import * as GalleryUtils from "../Utils/GalleryUtils";
const onInit = async () => {
initializeIcons();
await initializeConfiguration();
const galleryViewerProps = GalleryUtils.getGalleryViewerProps(window.location.search);
const notebookViewerProps = GalleryUtils.getNotebookViewerProps(window.location.search);
let backNavigationText: string;
let onBackClick: () => void;
if (galleryViewerProps.selectedTab !== undefined) {
backNavigationText = GalleryUtils.getTabTitle(galleryViewerProps.selectedTab);
onBackClick = () =>
(window.location.href = `${configContext.hostedExplorerURL}gallery.html?tab=${
GalleryTab[galleryViewerProps.selectedTab]
}`);
}
const hideInputs = notebookViewerProps.hideInputs;
const notebookUrl = decodeURIComponent(notebookViewerProps.notebookUrl);
const galleryItemId = notebookViewerProps.galleryItemId;
let galleryItem: IGalleryItem;
if (galleryItemId) {
const junoClient = new JunoClient();
const galleryItemJunoResponse = await junoClient.getNotebookInfo(galleryItemId);
galleryItem = galleryItemJunoResponse.data;
}
// The main purpose of hiding the prompt is to hide everything when hiding inputs.
// It is generally not very useful to just hide the prompt.
const hidePrompts = hideInputs;
render(notebookUrl, backNavigationText, hideInputs, hidePrompts, galleryItem, onBackClick);
};
const render = (
notebookUrl: string,
backNavigationText: string,
hideInputs?: boolean,
hidePrompts?: boolean,
galleryItem?: IGalleryItem,
onBackClick?: () => void,
) => {
const props: NotebookViewerComponentProps = {
junoClient: galleryItem ? new JunoClient() : undefined,
notebookUrl,
galleryItem,
backNavigationText,
hideInputs,
hidePrompts,
onBackClick: onBackClick,
onTagClick: undefined,
};
if (galleryItem) {
document.title = FileSystemUtil.stripExtension(galleryItem.name, "ipynb");
}
const element = (
<>
<header>
<GalleryHeaderComponent />
</header>
<div style={{ marginLeft: 120, marginRight: 120 }}>
<NotebookViewerComponent {...props} />
</div>
</>
);
ReactDOM.render(element, document.getElementById("notebookContent"));
};
// Entry point
window.addEventListener("load", onInit);

View File

@@ -105,12 +105,6 @@ const requestAndStoreAccessToken = async (): Promise<void> => {
});
};
export const openRestoreContainerDialog = (): void => {
if (isFabricNative()) {
sendCachedDataMessage(FabricMessageTypes.RestoreContainer, []);
}
};
/**
* Check token validity and schedule a refresh if necessary
* @param tokenTimestamp

View File

@@ -40,7 +40,6 @@ export type Features = {
readonly disableConnectionStringLogin: boolean;
readonly enableContainerCopy: boolean;
readonly enableCloudShell: boolean;
readonly enableRestoreContainer: boolean; // only for Fabric
// can be set via both flight and feature flag
autoscaleDefault: boolean;
@@ -94,7 +93,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
notebookBasePath: get("notebookbasepath"),
notebookServerToken: get("notebookservertoken"),
notebookServerUrl: get("notebookserverurl"),
sandboxNotebookOutputs: true,
sandboxNotebookOutputs: "true" === get("sandboxnotebookoutputs", "true"),
selfServeType: get("selfservetype"),
showMinRUSurvey: "true" === get("showminrusurvey"),
ttl90Days: "true" === get("ttl90days"),
@@ -112,7 +111,6 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
enablePriorityBasedExecution: "true" === get("enableprioritybasedexecution"),
disableConnectionStringLogin: "true" === get("disableconnectionstringlogin"),
enableContainerCopy: "true" === get("enablecontainercopy"),
enableRestoreContainer: "true" === get("enablerestorecontainer"),
enableCloudShell: true,
};
}

View File

@@ -27,7 +27,7 @@ describe("AuthorizationUtils", () => {
enableKoResourceTree: false,
enableThroughputBuckets: false,
hostedDataExplorer: false,
sandboxNotebookOutputs: true,
sandboxNotebookOutputs: false,
showMinRUSurvey: false,
ttl90Days: false,
enableThroughputCap: false,
@@ -43,7 +43,6 @@ describe("AuthorizationUtils", () => {
partitionKeyDefault: false,
partitionKeyDefault2: false,
notebooksDownBanner: false,
enableRestoreContainer: false,
},
});
};

View File

@@ -10,7 +10,7 @@ let CONTAINER_ID: string;
// Set up test database and container with data before all tests
test.beforeAll(async () => {
testContainer = await createTestSQLContainer({ includeTestData: true });
testContainer = await createTestSQLContainer(true);
DATABASE_ID = testContainer.database.id;
CONTAINER_ID = testContainer.container.id;
});