Skip to content

Commit

Permalink
Feat/pci ai notebooks manage notebook pop up (Start, Stop, Delete) (#…
Browse files Browse the repository at this point in the history
…13860)

* feat(ai.notebooks): add notebook management popup
* feat(ai.notebooks): fixing rebase issues
* feat(ai.notebooks): fix pr comments

Signed-off-by: Arthur Bullet <[email protected]>
  • Loading branch information
abullet33 authored Nov 15, 2024
1 parent 7e98bfa commit 802e674
Show file tree
Hide file tree
Showing 34 changed files with 1,150 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
{
"titleNot": "Dashboard page",
"tesazdazt": "test"
"deleteNotebookTitle": "Supprimer le notebook",
"deleteNotebookDescription": "Etes-vous sur de vouloir supprimer le notebook {{name}} ?",
"notebookButtonCancel": "Annuler",
"deleteNotebookButtonConfirm": "Supprimer",
"notebookToastErrorTitle": "Echec",
"notebookToastSuccessTitle": "Succès",
"deleteNotebookToastSuccessDescription": "Le notebook {{name}} a été supprimé",
"startNotebookTitle": "Démarrer le notebook",
"startNotebookDescription": "Etes-vous sur de vouloir démarrer le notebook {{name}} ?",
"startNotebookButtonConfirm": "Démarrer",
"startNotebookToastSuccessDescription": "Le notebook {{name}} a été démarré",
"stopNotebookTitle": "Arrêter le notebook",
"stopNotebookDescription1": "Lorsque vous stoppez un AI Notebook :",
"stopNotebookDescriptionList1": "Les ressources de calculs sont libérées (vous ne payez plus pour les CPUs et GPUs)",
"stopNotebookDescriptionList2": "Nous sauvegardons votre Espace de travail (dossier /worskpace) de manière sécurisée (10 Gio gratuits, ensuite au coût de l'Object Storage).",
"stopNotebookDescriptionList3": "Tout ce qui est sur le stockage local éphémère sera perdu.",
"stopNotebookDescription2": "Une fois stoppé, vous pourrez soit",
"stopNotebookDescription2Bis": "supprimer",
"stopNotebookDescription2Ter": "votre AI Notebook soit le",
"stopNotebookDescription2Quater": "relancer",
"stopNotebookDescription3": "Nous restaurerons votre Espace de Travail et réallouerons des ressources de calculs.",
"stopNotebookConfirmation": "Etes-vous sur de vouloir arrêter le notebook {{name}} ?",
"stopNotebookButtonConfirm": "Arrêter",
"stopNotebookToastSuccessDescription": "Le notebook {{name}} a été arrêté"
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"status-DELETING": "En cours de suppression",
"status-RESTARTING": "Redémarrage en cours",
"status-STARTING'": "Démarrage",
"status-STARTING": "Démarrage",
"status-STOPPING": "En cours d'extinction",
"status-FAILED": "Erreur",
"status-ERROR": "Erreur",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Containers } from '@/types/orderFunnel';

export const mockedContainer: Containers = {
status: 'ok',
message: 'message',
containers: ['container1', 'container2'],
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DataStoresWithContainers } from '@/hooks/api/ai/datastore/useGetDatastoresWithContainers.hook';
import { DataStoresWithRegion } from '@/hooks/api/ai/datastore/useGetDatastoresWithRegions.hook';
import * as ai from '@/types/cloud/project/ai';

Expand Down Expand Up @@ -47,3 +48,12 @@ export const mockedGitWithRegion: DataStoresWithRegion = {
type: ai.DataStoreTypeEnum.git,
region: 'GRA',
};

export const mockedDatastoreWithContainer: DataStoresWithContainers = {
alias: 'alias',
endpoint: 'endpoint',
owner: ai.DataStoreOwnerEnum.customer,
type: ai.DataStoreTypeEnum.s3,
id: 'id',
container: 'container2',
};
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ export const FrameworkTile = ({
versions={framework.versions}
selectedVersion={selectedVersion}
isFrameworkSelected={selected}
onChange={(versionName) =>
setSelectedVersion(framework.versions.find((v) => v === versionName))
}
onChange={(versionName) => {
setSelectedVersion(framework.versions.find((v) => v === versionName));
}}
/>
<RadioTile.Separator />
<p className="text-xs text-justify leading-relaxed">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const POLLING = {
NOTEBOOKS: 30_000,
NOTEBOOK: 30_000,
};

export const USER_INACTIVITY_TIMEOUT = 5 * 60_000; // inactivity after 5 minutes
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,26 @@ export const addNotebook = async ({ projectId, notebookInfo }: AddNotebook) =>
apiClient.v6
.post(`/cloud/project/${projectId}/ai/notebook`, notebookInfo)
.then((res) => res.data as ai.notebook.Notebook);

export const startNotebook = async ({
projectId,
notebookId,
}: NotebookData) => {
return apiClient.v6
.put(`/cloud/project/${projectId}/ai/notebook/${notebookId}/start`)
.then((res) => res.data as ai.notebook.Notebook);
};

export const stopNotebook = async ({ projectId, notebookId }: NotebookData) => {
return apiClient.v6
.put(`/cloud/project/${projectId}/ai/notebook/${notebookId}/stop`)
.then((res) => res.data as ai.notebook.Notebook);
};

export const deleteNotebook = async ({ projectId, notebookId }: NotebookData) =>
apiClient.v6.delete(`/cloud/project/${projectId}/ai/notebook/${notebookId}`);

export const getCommand = async ({ projectId, notebookInfo }: AddNotebook) =>
apiClient.v6
.post(`/cloud/project/${projectId}/ai/notebook/command`, notebookInfo)
.then((res) => res.data as ai.Command);
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
import { apiClient } from '@ovh-ux/manager-core-api';
import { describe, expect, vi } from 'vitest';
import { getNotebooks } from '@/data/api/ai/notebook/notebook.api';
import {
addNotebook,
deleteNotebook,
getCommand,
getNotebook,
getNotebooks,
startNotebook,
stopNotebook,
} from '@/data/api/ai/notebook/notebook.api';
import { mockedNotebookSpec } from '@/__tests__/helpers/mocks/notebook';

vi.mock('@ovh-ux/manager-core-api', () => {
const get = vi.fn(() => {
return Promise.resolve({ data: null });
});
const post = vi.fn(() => {
return Promise.resolve({ data: null });
});
const put = vi.fn(() => {
return Promise.resolve({ data: null });
});
const del = vi.fn(() => {
return Promise.resolve({ data: null });
});
return {
apiClient: {
v6: {
get,
post,
put,
delete: del,
},
},
};
Expand All @@ -36,4 +57,118 @@ describe('notebook functions', () => {
},
);
});

it('should call getNotebook', async () => {
expect(apiClient.v6.get).not.toHaveBeenCalled();
await getNotebook({
projectId: 'projectId',
notebookId: 'notebookId',
});
expect(apiClient.v6.get).toHaveBeenCalledWith(
'/cloud/project/projectId/ai/notebook/notebookId',
);
});

it('should call addNotebook', async () => {
expect(apiClient.v6.post).not.toHaveBeenCalled();
await addNotebook({
projectId: 'projectId',
notebookInfo: mockedNotebookSpec,
});
expect(apiClient.v6.post).toHaveBeenCalledWith(
'/cloud/project/projectId/ai/notebook',
{
env: {
editorId: 'editor',
frameworkId: 'frameworkId',
frameworkVersion: 'frameworkVersion',
},
envVars: [
{
name: 'envVarsName',
value: 'envVarsValue',
},
],
name: 'name',
region: 'region',
resources: {
cpu: 1,
ephemeralStorage: 1,
flavor: 'flavor',
gpu: 1,
memory: 1,
privateNetwork: 1,
publicNetwork: 1,
},
},
);
});

it('should call startNotebook', async () => {
expect(apiClient.v6.put).not.toHaveBeenCalled();
await startNotebook({
projectId: 'projectId',
notebookId: 'notebookId',
});
expect(apiClient.v6.put).toHaveBeenCalledWith(
'/cloud/project/projectId/ai/notebook/notebookId/start',
);
});

it('should call stopNotebook', async () => {
expect(apiClient.v6.put).not.toHaveBeenCalled();
await stopNotebook({
projectId: 'projectId',
notebookId: 'notebookId',
});
expect(apiClient.v6.put).toHaveBeenCalledWith(
'/cloud/project/projectId/ai/notebook/notebookId/stop',
);
});

it('should call deleteNotebook', async () => {
expect(apiClient.v6.put).not.toHaveBeenCalled();
await deleteNotebook({
projectId: 'projectId',
notebookId: 'notebookId',
});
expect(apiClient.v6.delete).toHaveBeenCalledWith(
'/cloud/project/projectId/ai/notebook/notebookId',
);
});

it('should call getCommand', async () => {
expect(apiClient.v6.post).not.toHaveBeenCalled();
await getCommand({
projectId: 'projectId',
notebookInfo: mockedNotebookSpec,
});
expect(apiClient.v6.post).toHaveBeenCalledWith(
'/cloud/project/projectId/ai/notebook/command',
{
env: {
editorId: 'editor',
frameworkId: 'frameworkId',
frameworkVersion: 'frameworkVersion',
},
envVars: [
{
name: 'envVarsName',
value: 'envVarsValue',
},
],
name: 'name',
region: 'region',
resources: {
cpu: 1,
ephemeralStorage: 1,
flavor: 'flavor',
gpu: 1,
memory: 1,
privateNetwork: 1,
publicNetwork: 1,
},
},
);
});
});
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { apiClient } from '@ovh-ux/manager-core-api';
import { describe, expect, vi } from 'vitest';
import { getSshkey } from '@/data/api/sshkey/sshkey.api';
import { addSSHKey, getSshkey } from '@/data/api/sshkey/sshkey.api';
import { mockedSshKey } from '@/__tests__/helpers/mocks/sshkey';

vi.mock('@ovh-ux/manager-core-api', () => {
const get = vi.fn(() => {
return Promise.resolve({ data: null });
});
const post = vi.fn(() => {
return Promise.resolve({ data: null });
});
return {
apiClient: {
v6: {
get,
post,
},
},
};
Expand All @@ -35,4 +40,21 @@ describe('sshkey functions', () => {
},
);
});

it('should call addSshKey', async () => {
expect(apiClient.v6.post).not.toHaveBeenCalled();
await addSSHKey({
projectId: 'projectId',
sshKey: mockedSshKey,
});
expect(apiClient.v6.post).toHaveBeenCalledWith(
'/cloud/project/projectId/sshkey',
{
id: 'idSSHKEY',
name: 'nameSSHKEY',
publicKey: 'publicKey',
regions: ['GRA'],
},
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { renderHook, waitFor } from '@testing-library/react';
import { vi } from 'vitest';
import { QueryClientWrapper } from '@/__tests__/helpers/wrappers/QueryClientWrapper';
import * as datastoreApi from '@/data/api/ai/datastore.api';
import { getDatastoreContainer } from '@/data/api/ai/datastore.api';
import { mockedContainer } from '@/__tests__/helpers/mocks/container';
import { useGetDatastoreContainer } from './useGetDatastoreContainer.hook';

vi.mock('@/data/api/ai/datastore.api', () => ({
getDatastoreContainer: vi.fn(),
}));

describe('useGetDatastoreContainer', () => {
it('should return Container in Datastore', async () => {
const projectId = 'projectId';
const region = 'region';
const alias = 'alias';

vi.mocked(datastoreApi.getDatastoreContainer).mockResolvedValue(
mockedContainer,
);

const { result } = renderHook(
() => useGetDatastoreContainer(projectId, region, alias),
{
wrapper: QueryClientWrapper,
},
);

await waitFor(() => {
expect(result.current.isSuccess).toBe(true);
expect(result.current.data).toEqual(mockedContainer);
expect(datastoreApi.getDatastoreContainer).toHaveBeenCalledWith({
projectId,
region,
alias,
});
});
});
});
Loading

0 comments on commit 802e674

Please sign in to comment.