Skip to content

Commit

Permalink
ECOPROJECT-2357: Adjusting UI to last changes of API (#51)
Browse files Browse the repository at this point in the history
* ECOPROJECT-2357: Adjusting UI to last changes of API

Signed-off-by: Montse Ortega <[email protected]>
  • Loading branch information
ammont82 authored Dec 12, 2024
1 parent b9c87e1 commit 58564bd
Show file tree
Hide file tree
Showing 25 changed files with 727 additions and 263 deletions.
24 changes: 24 additions & 0 deletions apps/demo/src/apis/MockAgentApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { AgentApiInterface, DeleteAgentRequest } from "@migration-planner-ui/api-client/apis";
import { Agent } from "@migration-planner-ui/api-client/models";
import { InitOverrideFunction, ApiResponse, ConfigurationParameters } from "@migration-planner-ui/api-client/runtime";

export class MockAgentApi implements AgentApiInterface {
constructor(_configuration: ConfigurationParameters) {
console.warn("#### CAUTION: Using MockAgentApi ####");
}

deleteAgentRaw(_requestParameters: DeleteAgentRequest, _initOverrides?: RequestInit | InitOverrideFunction): Promise<ApiResponse<Agent>> {
throw new Error("Method not implemented.");
}
deleteAgent(_requestParameters: DeleteAgentRequest, _initOverrides?: RequestInit | InitOverrideFunction): Promise<Agent> {
throw new Error("Method not implemented.");
}
listAgentsRaw(_initOverrides?: RequestInit | InitOverrideFunction): Promise<ApiResponse<Array<Agent>>> {
throw new Error("Method not implemented.");
}
async listAgents(_initOverrides?: RequestInit | InitOverrideFunction): Promise<Array<Agent>> {
const { default: json } = await import("./responses/agents.json");
return json as unknown as Array<Agent>;
}

}
19 changes: 3 additions & 16 deletions apps/demo/src/apis/MockSourceApi.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// import { sleep, Time } from "#/common/Time";
import {
CreateSourceRequest,
DeleteSourceRequest,
GetSourceImageRequest,
GetImageRequest,
ReadSourceRequest,
SourceApiInterface,
} from "@migration-planner-ui/api-client/apis";
Expand All @@ -18,18 +17,6 @@ export class MockSourceApi implements SourceApiInterface {
console.warn("#### CAUTION: Using MockSourceApi ####");
}

async createSourceRaw(
_requestParameters: CreateSourceRequest,
_initOverrides?: RequestInit | InitOverrideFunction
): Promise<ApiResponse<Source>> {
throw new Error("Method not implemented.");
}
async createSource(
_requestParameters: CreateSourceRequest,
_initOverrides?: RequestInit | InitOverrideFunction
): Promise<Source> {
throw new Error("Method not implemented.");
}
async deleteSourceRaw(
_requestParameters: DeleteSourceRequest,
_initOverrides?: RequestInit | InitOverrideFunction
Expand All @@ -53,13 +40,13 @@ export class MockSourceApi implements SourceApiInterface {
throw new Error("Method not implemented.");
}
async getSourceImageRaw(
_requestParameters: GetSourceImageRequest,
_requestParameters: GetImageRequest,
_initOverrides?: RequestInit | InitOverrideFunction
): Promise<ApiResponse<Blob>> {
throw new Error("Method not implemented.");
}
async getSourceImage(
_requestParameters: GetSourceImageRequest,
_requestParameters: GetImageRequest,
_initOverrides?: RequestInit | InitOverrideFunction
): Promise<Blob> {
throw new Error("Method not implemented.");
Expand Down
24 changes: 24 additions & 0 deletions apps/demo/src/apis/responses/agents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[
{
"associated": true,
"createdAt": "2024-12-11T07:22:40.264166+01:00",
"credentialUrl": "http://127.0.0.1:3333",
"sourceId": "29031FCA-3274-4067-A655-8BC4C322EA3F",
"id": "f8d28f7d-d862-47c5-b823-9763b319ff91",
"status": "up-to-date",
"statusInfo": "Inventory successfully collected",
"updatedAt": "2024-12-11T07:23:25.261977+01:00",
"version": "b1dc3f5"
},
{
"associated": true,
"createdAt": "2024-12-11T07:22:40.264166+01:00",
"credentialUrl": "http://127.0.0.1:3333",
"sourceId": "29031FCA-3274-4067-A655-8BC4C322EA3C",
"id": "f8d28f7d-d862-47c5-b823-9763b319ff92",
"status": "gathering-initial-inventory",
"statusInfo": "Gathering inventory",
"updatedAt": "2024-12-11T07:23:25.261977+01:00",
"version": "b1dc3f5"
}
]
6 changes: 5 additions & 1 deletion apps/demo/src/main/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";
import { Configuration } from "@migration-planner-ui/api-client/runtime";
import { SourceApi } from "@migration-planner-ui/api-client/apis";
import { AgentApi, SourceApi } from "@migration-planner-ui/api-client/apis";
import { Spinner } from "@patternfly/react-core";
import {
Container,
Expand All @@ -19,7 +19,11 @@ function getConfiguredContainer(): Container {
});
const container = new Container();
container.register(Symbols.SourceApi, new SourceApi(plannerApiConfig));
container.register(Symbols.AgentApi, new AgentApi(plannerApiConfig));

//For UI testing we can use the mock Apis
//container.register(Symbols.SourceApi, new MockSourceApi(plannerApiConfig));
//container.register(Symbols.AgentApi, new MockAgentApi(plannerApiConfig));
return container;
}

Expand Down
8 changes: 4 additions & 4 deletions apps/demo/src/migration-wizard/MigrationWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const openAssistedInstaller = (): void => {
export const MigrationWizard: React.FC = () => {
const computedHeight = useComputedHeightFromPageHeader();
const discoverSourcesContext = useDiscoverySources();
const isDiscoverySourceUpToDate = discoverSourcesContext.sourceSelected?.status === "up-to-date";
const isDiscoverySourceUpToDate = discoverSourcesContext.agentSelected?.status === "up-to-date";

return (
<Wizard height={computedHeight}>
Expand All @@ -22,7 +22,7 @@ export const MigrationWizard: React.FC = () => {
id="connect-step"
footer={{
isCancelHidden: true,
isNextDisabled: !isDiscoverySourceUpToDate,
isNextDisabled: (!isDiscoverySourceUpToDate || discoverSourcesContext.sourceSelected===null),
}}
>
<ConnectStep />
Expand All @@ -31,7 +31,7 @@ export const MigrationWizard: React.FC = () => {
name="Discover"
id="discover-step"
footer={{ isCancelHidden: true }}
isDisabled={discoverSourcesContext.sourceSelected?.status !== 'up-to-date'}
isDisabled={discoverSourcesContext.agentSelected?.status !== 'up-to-date' || discoverSourcesContext.sourceSelected===null}
>
<DiscoveryStep />
</WizardStep>
Expand All @@ -42,7 +42,7 @@ export const MigrationWizard: React.FC = () => {
nextButtonText: "Let's create a new cluster",
onNext: openAssistedInstaller,
}}
isDisabled={discoverSourcesContext.sourceSelected?.status !== 'up-to-date'}
isDisabled={discoverSourcesContext.agentSelected?.status !== 'up-to-date' || discoverSourcesContext.sourceSelected===null}
>
<PrepareMigrationStep />
</WizardStep>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@ declare namespace DiscoverySources {
errorLoadingSources?: Error;
isDeletingSource: boolean;
errorDeletingSource?: Error;
isCreatingSource: boolean;
errorCreatingSource?: Error;
isDownloadingSource: boolean;
errorDownloadingSource?: Error;
isPolling: boolean;
sourceSelected: Source;
listSources: () => Promise<Source[]>;
deleteSource: (id: string) => Promise<Source>;
createSource: (name: string, sourceSshKey: string) => Promise<Source>;
downloadSource: (sourceName: string, sourceSshKey: string) => Promise<void>;
downloadSource: (sourceSshKey: string) => Promise<void>;
startPolling: (delay: number) => void;
stopPolling: () => void;
selectSource: (source:Source) => void;
agents: Agent[]|undefined;
isLoadingAgents: boolean;
errorLoadingAgents?: Error;
listAgents: () => Promise<Agent[]>;
deleteAgent: (agent: Agent) => Promise<Agent>;
isDeletingAgent: boolean;
errorDeletingAgent?: Error;
selectAgent: (agent:Agent) => void;
agentSelected: Agent;
selectSourceById: (sourceId:string)=>void;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,26 @@ import React, {
type PropsWithChildren,
} from "react";
import { useAsyncFn, useInterval } from "react-use";
import { type SourceApiInterface } from "@migration-planner-ui/api-client/apis";
import { type SourceApiInterface, type AgentApiInterface } from "@migration-planner-ui/api-client/apis";
import { useInjection } from "@migration-planner-ui/ioc";
import { Symbols } from "#/main/Symbols";
import { Context } from "./Context";
import { Source } from "@migration-planner-ui/api-client/models";
import { Agent, Source } from "@migration-planner-ui/api-client/models";

export const Provider: React.FC<PropsWithChildren> = (props) => {
const { children } = props;

const [sourceSelected, setSourceSelected] = useState<Source | null>(null)

const [agentSelected, setAgentSelected] = useState<Agent | null>(null)

const sourceApi = useInjection<SourceApiInterface>(Symbols.SourceApi);
const agentsApi = useInjection<AgentApiInterface>(Symbols.AgentApi);

const [listAgentsState, listAgents] = useAsyncFn(async () => {
const agents = await agentsApi.listAgents();
return agents;
});

const [listSourcesState, listSources] = useAsyncFn(async () => {
const sources = await sourceApi.listSources();
Expand All @@ -27,20 +35,11 @@ export const Provider: React.FC<PropsWithChildren> = (props) => {
return deletedSource;
});

const [createSourceState, createSource] = useAsyncFn(async (name: string, sshKey: string) => {
const createdSource = await sourceApi.createSource({
sourceCreate: { name, sshKey },
});
return createdSource;
});

const [downloadSourceState, downloadSource] = useAsyncFn(
async (sourceName: string, sourceSshKey: string): Promise<void> => {
async (sshKey:string): Promise<void> => {
const anchor = document.createElement("a");
anchor.download = sourceName + ".ova";

const newSource = await createSource(sourceName, sourceSshKey);
const imageUrl = `/planner/api/v1/sources/${newSource.id}/image`;
const imageUrl = `/planner/api/v1/image${sshKey ? '?sshKey=' + sshKey : ''}`;

const response = await fetch(imageUrl, { method: 'HEAD' });

Expand Down Expand Up @@ -84,31 +83,60 @@ export const Provider: React.FC<PropsWithChildren> = (props) => {
}, [isPolling]);
useInterval(() => {
listSources();
listAgents();
}, pollingDelay);

const selectSource = useCallback((source: Source) => {
const selectSource = useCallback((source: Source|null) => {
setSourceSelected(source);
}, []);

const selectSourceById = useCallback((sourceId: string) => {
const source = listSourcesState.value?.find(source => source.id === sourceId);
setSourceSelected(source||null);
}, [listSourcesState.value]);


const selectAgent = useCallback((agent: Agent) => {
setAgentSelected(agent);
if (agent && agent.sourceId!==null) selectSourceById(agent.sourceId ?? '');
}, [selectSourceById]);


const [deleteAgentState, deleteAgent] = useAsyncFn(async (agent: Agent) => {
if (agent && agent.sourceId !== null) {
await deleteSource(agent.sourceId ?? '');
selectSource(null);
}
const deletedAgent = await agentsApi.deleteAgent({id: agent.id});
return deletedAgent;
});

const ctx: DiscoverySources.Context = {
sources: listSourcesState.value ?? [],
isLoadingSources: listSourcesState.loading,
errorLoadingSources: listSourcesState.error,
isDeletingSource: deleteSourceState.loading,
errorDeletingSource: deleteSourceState.error,
isCreatingSource: createSourceState.loading,
errorCreatingSource: createSourceState.error,
isDownloadingSource: downloadSourceState.loading,
errorDownloadingSource: downloadSourceState.error,
errorDownloadingSource: downloadSourceState.error,
isPolling,
listSources,
deleteSource,
createSource,
downloadSource,
startPolling,
stopPolling,
sourceSelected: sourceSelected,
selectSource,
agents: listAgentsState.value ?? [],
isLoadingAgents: listAgentsState.loading,
errorLoadingAgents: listAgentsState.error,
listAgents,
deleteAgent,
isDeletingAgent: deleteAgentState.loading,
errorDeletingAgent: deleteAgentState.error,
selectAgent,
agentSelected: agentSelected,
selectSourceById
};

return <Context.Provider value={ctx}>{children}</Context.Provider>;
Expand Down
26 changes: 14 additions & 12 deletions apps/demo/src/migration-wizard/steps/connect/ConnectStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@ export const ConnectStep: React.FC = () => {
const toggleDiscoverySourceSetupModal = useCallback((): void => {
setShouldShowDiscoverySetupModal((lastState) => !lastState);
}, []);
const hasSources = discoverySourcesContext.sources.length > 0;
const [firstSource, ..._otherSources] = discoverySourcesContext.sources;
const hasAgents = discoverySourcesContext.agents && discoverySourcesContext.agents.length > 0;
const [firstAgent, ..._otherAgents] = discoverySourcesContext.agents || [];

useEffect(() => {
if (!discoverySourcesContext.sourceSelected && firstSource) {
discoverySourcesContext.selectSource(firstSource);
if (!discoverySourcesContext.agentSelected && firstAgent) {
if (firstAgent.status === 'up-to-date') {
discoverySourcesContext.selectAgent(firstAgent);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [firstSource]);
}, [firstAgent]);

return (
<Stack hasGutter>
Expand Down Expand Up @@ -69,7 +71,7 @@ export const ConnectStep: React.FC = () => {
</ListItem>
</List>
</TextContent>
{discoverySourcesContext.sourceSelected?.status ===
{discoverySourcesContext.agentSelected?.status ===
"waiting-for-credentials" && (
<Alert
isInline
Expand All @@ -78,11 +80,11 @@ export const ConnectStep: React.FC = () => {
actionLinks={
<AlertActionLink
component="a"
href={discoverySourcesContext.sourceSelected?.credentialUrl}
href={discoverySourcesContext.agentSelected?.credentialUrl}
target="_blank"
rel="noopener noreferrer"
>
{discoverySourcesContext.sourceSelected?.credentialUrl}
{discoverySourcesContext.agentSelected?.credentialUrl}
</AlertActionLink>
}
>
Expand Down Expand Up @@ -113,7 +115,7 @@ export const ConnectStep: React.FC = () => {
</Panel>
</StackItem>
<StackItem>
{hasSources && (
{hasAgents && (
<Button
variant="secondary"
onClick={toggleDiscoverySourceSetupModal}
Expand All @@ -130,11 +132,11 @@ export const ConnectStep: React.FC = () => {
isDisabled={discoverySourcesContext.isDownloadingSource}
onSubmit={async (event) => {
const form = event.currentTarget;
const name = form["discoverySourceName"].value as string;
const sshKey = form["discoverySourceSshKey"].value as string;
await discoverySourcesContext.downloadSource(name, sshKey);
await discoverySourcesContext.downloadSource(sshKey);
toggleDiscoverySourceSetupModal();
await discoverySourcesContext.listSources();
//await discoverySourcesContext.listSources();
await discoverySourcesContext.listAgents();
}}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const enum Columns {
Name = "Name",
CredentialsUrl = "Credentials URL",
Status = "Status",
Hosts = "Hosts",
VMs = "VMs",
Expand Down
Loading

0 comments on commit 58564bd

Please sign in to comment.