Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { createFileRoute } from '@tanstack/react-router'
import { SettingsCloudCredentials } from '@qovery/domains/organizations/feature'

export const Route = createFileRoute('/_authenticated/organization/$organizationId/settings/cloud-credentials')({
component: RouteComponent,
})

function RouteComponent() {
return <div>Hello "/_authenticated/organization/$organizationId/settings/cloud-credentials"!</div>
return <SettingsCloudCredentials />
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ import { wrapWithReactHookForm } from '__tests__/utils/wrap-with-react-hook-form
import { CloudProviderEnum } from 'qovery-typescript-axios'
import * as useCreateCloudProviderCredentialHook from '@qovery/domains/cloud-providers/feature'
import * as useEditCloudProviderCredentialHook from '@qovery/domains/cloud-providers/feature'
import * as useDeleteCloudProviderCredentialHook from '@qovery/domains/cloud-providers/feature'
import { getByText, renderWithProviders, screen } from '@qovery/shared/util-tests'
import * as useClusterCloudProviderInfoHook from '../hooks/use-cluster-cloud-provider-info/use-cluster-cloud-provider-info'
import ClusterCredentialsModal, { type ClusterCredentialsModalProps, handleSubmit } from './cluster-credentials-modal'

jest.mock('@qovery/domains/cloud-providers/feature', () => ({
useCreateCloudProviderCredential: jest.fn(),
useEditCloudProviderCredential: jest.fn(),
useDeleteCloudProviderCredential: jest.fn(),
}))

jest.mock('../hooks/use-cluster-cloud-provider-info/use-cluster-cloud-provider-info', () => ({
Expand All @@ -21,7 +19,6 @@ let props: ClusterCredentialsModalProps

const mockCreateCredential = jest.fn()
const mockEditCredential = jest.fn()
const mockDeleteCredential = jest.fn()

describe('ClusterCredentialsModal', () => {
beforeEach(() => {
Expand All @@ -42,10 +39,6 @@ describe('ClusterCredentialsModal', () => {
isLoading: false,
})

jest.spyOn(useDeleteCloudProviderCredentialHook, 'useDeleteCloudProviderCredential').mockReturnValue({
mutateAsync: mockDeleteCredential,
})

jest.spyOn(useClusterCloudProviderInfoHook, 'useClusterCloudProviderInfo').mockReturnValue({
data: { cloud_provider: 'AWS' },
isLoading: false,
Expand Down Expand Up @@ -132,27 +125,5 @@ describe('ClusterCredentialsModal', () => {

expect(mockEditCredential).toHaveBeenCalled()
})

it('should handle delete confirmation', async () => {
const { userEvent } = renderWithProviders(wrapWithReactHookForm(<ClusterCredentialsModal {...props} />))

const deleteButton = screen.getByTestId('delete-button')
await userEvent.click(deleteButton)

const confirmationInput = screen.getByTestId('input-value')
await userEvent.type(confirmationInput, 'delete')

const modalTitle = screen.getByText('Delete credential')
expect(modalTitle).toBeInTheDocument()

const confirmationModal = modalTitle.parentElement

if (confirmationModal) {
const confirmButton = getByText(confirmationModal, 'Confirm')
await userEvent.click(confirmButton)
}

expect(mockDeleteCredential).toHaveBeenCalled()
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Controller, type FieldValues, FormProvider, useForm } from 'react-hook-
import { P, match } from 'ts-pattern'
import {
useCreateCloudProviderCredential,
useDeleteCloudProviderCredential,
useEditCloudProviderCredential,
} from '@qovery/domains/cloud-providers/feature'
import { CLUSTER_SETTINGS_IMAGE_REGISTRY_URL, CLUSTER_SETTINGS_URL, CLUSTER_URL } from '@qovery/shared/routes'
Expand Down Expand Up @@ -125,7 +124,7 @@ function CalloutEdit({
<Icon iconName="circle-exclamation" iconStyle="regular" />
</Callout.Icon>
<Callout.Text>
<Callout.TextDescription className="flex flex-col gap-1">
<Callout.TextDescription className="flex flex-col gap-1 text-warning">
The credential change won't be applied to the mirroring registry of this cluster. Make sure to update the
credentials properly in this cluster's mirroring registry section.
{clusterId && (
Expand Down Expand Up @@ -162,7 +161,6 @@ export function ClusterCredentialsModal({

const { mutateAsync: createCloudProviderCredential, isLoading: isLoadingCreate } = useCreateCloudProviderCredential()
const { mutateAsync: editCloudProviderCredential, isLoading: isLoadingEdit } = useEditCloudProviderCredential()
const { mutateAsync: deleteCloudProviderCredential } = useDeleteCloudProviderCredential()

const [fileDetails, setFileDetails] = useState<{ name: string; size: number }>()
const { getRootProps, getInputProps, isDragActive } = useDropzone({
Expand Down Expand Up @@ -293,33 +291,6 @@ export function ClusterCredentialsModal({
}
})

const onDelete = async () => {
openModalConfirmation({
title: 'Delete credential',
description: (
<p>
To confirm the deletion of <strong>{credential?.name}</strong>, please type "delete"
</p>
),
name: credential?.name,
confirmationMethod: 'action',
action: async () => {
if (credential?.id) {
try {
await deleteCloudProviderCredential({
organizationId,
cloudProvider: cloudProviderLocal,
credentialId: credential.id,
})
onClose()
} catch (error) {
console.error(error)
}
}
},
})
}

const watchType = methods.watch('type')
const watchAzureApplicationId = methods.watch('azure_application_id')
const watchAzureSubscriptionId = methods.watch('azure_subscription_id')
Expand Down Expand Up @@ -368,7 +339,6 @@ export function ClusterCredentialsModal({
}
onSubmit={onSubmit}
onClose={onClose}
onDelete={onDelete}
loading={isLoadingCreate || isLoadingEdit}
isEdit={isEdit}
submitLabel={submitLabel}
Expand Down Expand Up @@ -399,7 +369,7 @@ export function ClusterCredentialsModal({
{watchType === 'STATIC' && (
<>
{cloudProviderLocal === 'AWS' && (
<div className="flex flex-col gap-2 rounded border border-neutral p-4">
<div className="flex flex-col gap-2 rounded-md border border-neutral bg-surface-neutral p-4">
<h2 className="text-sm font-medium text-neutral">1. Create a user for Qovery</h2>
<p className="text-sm text-neutral-subtle">Follow the instructions available on this page</p>
<ExternalLink
Expand All @@ -412,7 +382,7 @@ export function ClusterCredentialsModal({
)}
{cloudProviderLocal === 'GCP' && (
<>
<div className="flex flex-col gap-2 rounded border border-neutral p-4">
<div className="flex flex-col gap-2 rounded-md border border-neutral bg-surface-neutral p-4">
<h2 className="text-sm font-medium text-neutral">
1. Connect to your GCP Console and create/open a project
</h2>
Expand All @@ -421,11 +391,11 @@ export function ClusterCredentialsModal({
https://console.cloud.google.com/
</ExternalLink>
</div>
<div className="flex flex-col gap-2 rounded border border-neutral p-4">
<div className="flex flex-col gap-2 rounded-md border border-neutral bg-surface-neutral p-4">
<h2 className="text-sm font-medium text-neutral">
2. Open the embedded Google shell and run the following command
</h2>
<div className="flex gap-6 rounded-sm bg-surface-neutral-subtle p-3 text-neutral">
<div className="flex gap-6 rounded border border-neutral bg-surface-neutral-subtle p-3 text-neutral retina:border-[0.5px]">
<div>
<span className="select-none">$ </span>
curl https://setup.qovery.com/create_credentials_gcp.sh | \ bash -s -- $GOOGLE_CLOUD_PROJECT
Expand All @@ -440,7 +410,7 @@ bash -s -- $GOOGLE_CLOUD_PROJECT qovery_role qovery-service-account"
</>
)}
{cloudProviderLocal === 'SCW' && (
<div className="flex flex-col gap-2 rounded border border-neutral p-4">
<div className="flex flex-col gap-2 rounded-md border border-neutral bg-surface-neutral p-4">
<h2 className="text-sm font-medium text-neutral">1. Generate Access key Id/Secret Access Key</h2>
<p className="text-sm text-neutral-subtle">Follow the instructions available on this page</p>
<ExternalLink
Expand All @@ -455,14 +425,14 @@ bash -s -- $GOOGLE_CLOUD_PROJECT qovery_role qovery-service-account"
)}
{watchType === 'STS' ? (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2 rounded border border-neutral p-4">
<div className="flex flex-col gap-2 rounded-md border border-neutral bg-surface-neutral p-4">
<h2 className="text-sm font-medium text-neutral">1. Connect to your AWS Console</h2>
<p className="text-sm text-neutral-subtle">Make sure you are connected to the right AWS account</p>
<ExternalLink href="https://aws.amazon.com/fr/console/" size="sm">
https://aws.amazon.com/fr/console/
</ExternalLink>
</div>
<div className="flex flex-col gap-2 rounded border border-neutral p-4">
<div className="flex flex-col gap-2 rounded-md border border-neutral bg-surface-neutral p-4">
<h2 className="text-sm font-medium text-neutral">
2. Create a role for Qovery and grant assume role permissions
</h2>
Expand All @@ -476,7 +446,7 @@ bash -s -- $GOOGLE_CLOUD_PROJECT qovery_role qovery-service-account"
Cloudformation stack
</ExternalLink>
</div>
<div className="flex flex-col gap-4 rounded border border-neutral p-4">
<div className="flex flex-col gap-4 rounded-md border border-neutral bg-surface-neutral p-4">
<h2 className="text-sm font-medium text-neutral">3. Insert here the role ARN</h2>
<Controller
name="name"
Expand Down Expand Up @@ -516,7 +486,7 @@ bash -s -- $GOOGLE_CLOUD_PROJECT qovery_role qovery-service-account"
</div>
</div>
) : (
<div className="flex flex-col gap-4 rounded border border-neutral p-4">
<div className="flex flex-col gap-4 rounded-md border border-neutral bg-surface-neutral p-4">
<h2 className="text-sm font-medium text-neutral">
{cloudProviderLocal === 'GCP'
? '3. Download the key.json generated and drag and drop it here'
Expand Down Expand Up @@ -562,7 +532,7 @@ bash -s -- $GOOGLE_CLOUD_PROJECT qovery_role qovery-service-account"
/>
{isEditDirty && (
<>
<hr />
<hr className="border-neutral" />
<span className="text-sm text-neutral-subtle">Confirm your secret key</span>
</>
)}
Expand Down Expand Up @@ -609,7 +579,7 @@ bash -s -- $GOOGLE_CLOUD_PROJECT qovery_role qovery-service-account"
/>
{isEditDirty && (
<>
<hr />
<hr className="border-neutral" />
<span className="text-sm text-neutral-subtle">Confirm your secret key</span>
</>
)}
Expand Down Expand Up @@ -759,7 +729,7 @@ bash -s -- $GOOGLE_CLOUD_PROJECT qovery_role qovery-service-account"

return (
<>
<div className="flex flex-col gap-2 rounded border border-neutral p-4">
<div className="flex flex-col gap-2 rounded-md border border-neutral bg-surface-neutral p-4">
<h2 className="text-sm font-medium text-neutral">
2. Connect to your Azure Console and go to shell console
</h2>
Expand All @@ -770,7 +740,7 @@ bash -s -- $GOOGLE_CLOUD_PROJECT qovery_role qovery-service-account"
https://portal.azure.com/
</ExternalLink>
</div>
<div className="flex flex-col gap-2 rounded border border-neutral p-4">
<div className="flex flex-col gap-2 rounded-md border border-neutral bg-surface-neutral p-4">
<h2 className="text-sm font-medium text-neutral">
3. Open the embedded Azure shell and run the following command
</h2>
Expand All @@ -782,7 +752,7 @@ bash -s -- $GOOGLE_CLOUD_PROJECT qovery_role qovery-service-account"
Please note that this script can take up to 30 seconds to complete.
</p>
</div>
<div className="flex gap-6 rounded-sm bg-surface-neutral-subtle p-3 text-neutral">
<div className="flex gap-6 rounded border border-neutral bg-surface-neutral-subtle p-3 text-neutral retina:border-[0.5px]">
<div>
<span className="select-none">$ </span>
{snippet}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import * as Dialog from '@radix-ui/react-dialog'
import { type ClusterCredentials, type CredentialCluster } from 'qovery-typescript-axios'
import { Link } from 'react-router-dom'
import { CLUSTER_SETTINGS_CREDENTIALS_URL, CLUSTER_SETTINGS_URL, CLUSTER_URL } from '@qovery/shared/routes'
import { Heading, Section } from '@qovery/shared/ui'
import { Heading, Link, Section } from '@qovery/shared/ui'
import { pluralize } from '@qovery/shared/util-js'
import { ClusterAvatar } from '../cluster-avatar/cluster-avatar'

Expand All @@ -21,20 +20,23 @@ export function CredentialsListClustersModal({
return (
<Section className="p-5">
<Dialog.Title asChild>
<Heading level={1} className="mb-2 max-w-sm text-2xl text-neutral-400">
<Heading level={1} className="mb-2 max-w-sm text-2xl text-neutral">
Attached {pluralize(clusters.length, 'cluster', 'clusters')} ({clusters.length})
</Heading>
</Dialog.Title>
<Dialog.Description className="mb-6 text-sm text-neutral-350">Credential: {credential.name}</Dialog.Description>
<div className="flex flex-col gap-y-1 rounded border border-neutral-250 bg-neutral-100 p-2">
<Dialog.Description className="mb-6 text-sm text-neutral-subtle">
Credential: {credential.name}
</Dialog.Description>
<div className="flex flex-col gap-y-1 rounded border border-neutral bg-surface-neutral-subtle p-2">
{clusters.map((cluster) => (
<Link
key={cluster.id}
to={`${CLUSTER_URL(organizationId, cluster.id)}${CLUSTER_SETTINGS_URL}${CLUSTER_SETTINGS_CREDENTIALS_URL}`}
target="_blank"
rel="noreferrer"
color="brand"
>
<div className="flex items-center gap-1.5 text-sm font-medium text-brand-500 hover:text-brand-600">
<div className="text-brand-solid hover:text-brand-solidHover flex items-center gap-1.5 text-sm font-medium">
<ClusterAvatar cloudProvider={cluster.cloud_provider} size="sm" />
{cluster.name}
</div>
Expand Down
1 change: 1 addition & 0 deletions libs/domains/organizations/feature/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export * from './lib/invoice-banner/invoice-banner'
export * from './lib/free-trial-banner/free-trial-banner'
export * from './lib/settings-general/settings-general'
export * from './lib/settings-labels-annotations/settings-labels-annotations'
export * from './lib/settings-cloud-credentials/settings-cloud-credentials'
export * from './lib/settings-git-repository-access/settings-git-repository-access'
export * from './lib/settings-helm-repositories/settings-helm-repositories'
export * from './lib/settings-container-registries/settings-container-registries'
Expand Down
Loading