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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Upcoming Features
---

Add validation to Create Stream POST request ([#12557](https://github.com/linode/manager/pull/12557))
4 changes: 3 additions & 1 deletion packages/api-v4/src/datastream/streams.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { createStreamSchema } from '@linode/validation';

import { BETA_API_ROOT } from '../constants';
import Request, {
setData,
Expand Down Expand Up @@ -41,7 +43,7 @@ export const getStreams = (params?: Params, filter?: Filter) =>
*/
export const createStream = (data: CreateStreamPayload) =>
Request<Stream>(
setData(data), // @TODO (DPS-34044) add validation schema
setData(data, createStreamSchema),
setURL(`${BETA_API_ROOT}/monitor/streams`),
setMethod('POST'),
);
2 changes: 1 addition & 1 deletion packages/api-v4/src/datastream/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ interface ClientCertificateDetails {
type AuthenticationType = 'basic' | 'none';

interface Authentication {
details: AuthenticationDetails;
details?: AuthenticationDetails;
type: AuthenticationType;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Datastream form validation on create stream and destination ([#12557](https://github.com/linode/manager/pull/12557))
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('DestinationCreate', () => {
component: <DestinationCreate />,
useFormOptions: {
defaultValues: {
destination_type: destinationType.LinodeObjectStorage,
destination: { type: destinationType.LinodeObjectStorage },
},
},
});
Expand All @@ -30,7 +30,7 @@ describe('DestinationCreate', () => {
component: <DestinationCreate />,
useFormOptions: {
defaultValues: {
destination_label: '',
destination: { label: '' },
},
},
});
Expand All @@ -47,7 +47,7 @@ describe('DestinationCreate', () => {
component: <DestinationCreate />,
useFormOptions: {
defaultValues: {
destination_type: destinationType.LinodeObjectStorage,
destination: { type: destinationType.LinodeObjectStorage },
},
},
});
Expand All @@ -64,7 +64,7 @@ describe('DestinationCreate', () => {
component: <DestinationCreate />,
useFormOptions: {
defaultValues: {
destination_type: destinationType.LinodeObjectStorage,
destination: { type: destinationType.LinodeObjectStorage },
},
},
});
Expand All @@ -81,7 +81,7 @@ describe('DestinationCreate', () => {
component: <DestinationCreate />,
useFormOptions: {
defaultValues: {
destination_type: destinationType.LinodeObjectStorage,
destination: { type: destinationType.LinodeObjectStorage },
},
},
});
Expand All @@ -104,7 +104,7 @@ describe('DestinationCreate', () => {
component: <DestinationCreate />,
useFormOptions: {
defaultValues: {
destination_type: destinationType.LinodeObjectStorage,
destination: { type: destinationType.LinodeObjectStorage },
},
},
});
Expand All @@ -121,7 +121,7 @@ describe('DestinationCreate', () => {
component: <DestinationCreate />,
useFormOptions: {
defaultValues: {
destination_type: destinationType.LinodeObjectStorage,
destination: { type: destinationType.LinodeObjectStorage },
},
},
});
Expand All @@ -138,7 +138,7 @@ describe('DestinationCreate', () => {
component: <DestinationCreate />,
useFormOptions: {
defaultValues: {
destination_type: destinationType.LinodeObjectStorage,
destination: { type: destinationType.LinodeObjectStorage },
},
},
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { yupResolver } from '@hookform/resolvers/yup';
import { destinationType } from '@linode/api-v4';
import { Autocomplete, Box, Button, Paper, TextField } from '@linode/ui';
import { createDestinationSchema } from '@linode/validation';
import { useTheme } from '@mui/material/styles';
import * as React from 'react';
import { Controller, FormProvider, useForm, useWatch } from 'react-hook-form';
Expand All @@ -10,7 +12,7 @@ import { getDestinationTypeOption } from 'src/features/DataStream/dataStreamUtil
import { DestinationLinodeObjectStorageDetailsForm } from 'src/features/DataStream/Shared/DestinationLinodeObjectStorageDetailsForm';
import { destinationTypeOptions } from 'src/features/DataStream/Shared/types';

import type { CreateStreamForm } from 'src/features/DataStream/Streams/StreamCreate/types';
import type { CreateDestinationForm } from 'src/features/DataStream/Shared/types';

export const DestinationCreate = () => {
const theme = useTheme();
Expand All @@ -30,17 +32,21 @@ export const DestinationCreate = () => {
title: 'Create Destination',
};

const form = useForm<CreateStreamForm>({
const form = useForm<CreateDestinationForm>({
defaultValues: {
destination_type: destinationType.LinodeObjectStorage,
region: '',
type: destinationType.LinodeObjectStorage,
details: {
region: '',
},
},
mode: 'onBlur',
resolver: yupResolver(createDestinationSchema),
});
const { control, handleSubmit } = form;

const selectedDestinationType = useWatch({
control,
name: 'destination_type',
name: 'type',
});

const onSubmit = handleSubmit(async () => {});
Expand All @@ -51,15 +57,16 @@ export const DestinationCreate = () => {
<LandingHeader {...landingHeaderProps} />
<Paper>
<FormProvider {...form}>
<form onSubmit={onSubmit}>
<form id="createDestinationForm" onSubmit={onSubmit}>
<Controller
control={control}
name="destination_type"
name="type"
render={({ field }) => (
<Autocomplete
disableClearable
disabled={true}
label="Destination Type"
onBlur={field.onBlur}
onChange={(_, { value }) => {
field.onChange(value);
}}
Expand All @@ -71,11 +78,13 @@ export const DestinationCreate = () => {
/>
<Controller
control={control}
name="destination_label"
render={({ field }) => (
name="label"
render={({ field, fieldState }) => (
<TextField
aria-required
errorText={fieldState.error?.message}
label="Destination Name"
onBlur={field.onBlur}
onChange={(value) => {
field.onChange(value);
}}
Expand All @@ -100,6 +109,7 @@ export const DestinationCreate = () => {
>
<Button
buttonType="primary"
form="createDestinationForm"
sx={{ mt: theme.spacingFunction(16) }}
type="submit"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,29 @@ import { RegionSelect } from 'src/components/RegionSelect/RegionSelect';
import { PathSample } from 'src/features/DataStream/Shared/PathSample';
import { useFlags } from 'src/hooks/useFlags';

export const DestinationLinodeObjectStorageDetailsForm = () => {
interface DestinationLinodeObjectStorageDetailsFormProps {
controlPaths?: {
accessKeyId: string;
accessKeySecret: string;
bucketName: string;
host: string;
path: string;
region: string;
};
}

const defaultPaths = {
accessKeyId: 'details.access_key_id',
accessKeySecret: 'details.access_key_secret',
bucketName: 'details.bucket_name',
host: 'details.host',
path: 'details.path',
region: 'details.region',
};

export const DestinationLinodeObjectStorageDetailsForm = ({
controlPaths = defaultPaths,
}: DestinationLinodeObjectStorageDetailsFormProps) => {
const { gecko2 } = useFlags();
const { isGeckoLAEnabled } = useIsGeckoEnabled(gecko2?.enabled, gecko2?.la);
const { data: regions } = useRegionsQuery();
Expand All @@ -19,11 +41,13 @@ export const DestinationLinodeObjectStorageDetailsForm = () => {
<>
<Controller
control={control}
name="host"
render={({ field }) => (
name={controlPaths.host}
render={({ field, fieldState }) => (
<TextField
aria-required
errorText={fieldState.error?.message}
label="Host"
onBlur={field.onBlur}
onChange={(value) => {
field.onChange(value);
}}
Expand All @@ -35,11 +59,13 @@ export const DestinationLinodeObjectStorageDetailsForm = () => {
/>
<Controller
control={control}
name="bucket_name"
render={({ field }) => (
name={controlPaths.bucketName}
render={({ field, fieldState }) => (
<TextField
aria-required
errorText={fieldState.error?.message}
label="Bucket"
onBlur={field.onBlur}
onChange={(value) => {
field.onChange(value);
}}
Expand All @@ -51,13 +77,15 @@ export const DestinationLinodeObjectStorageDetailsForm = () => {
/>
<Controller
control={control}
name="region"
render={({ field }) => (
name={controlPaths.region}
render={({ field, fieldState }) => (
<RegionSelect
currentCapability="Object Storage"
disableClearable
errorText={fieldState.error?.message}
isGeckoLAEnabled={isGeckoLAEnabled}
label="Region"
onBlur={field.onBlur}
onChange={(_, region) => field.onChange(region.id)}
regionFilter="core"
regions={regions ?? []}
Expand All @@ -67,11 +95,13 @@ export const DestinationLinodeObjectStorageDetailsForm = () => {
/>
<Controller
control={control}
name="access_key_id"
render={({ field }) => (
name={controlPaths.accessKeyId}
render={({ field, fieldState }) => (
<HideShowText
aria-required
errorText={fieldState.error?.message}
label="Access Key ID"
onBlur={field.onBlur}
onChange={(value) => field.onChange(value)}
placeholder="Access Key ID..."
value={field.value}
Expand All @@ -80,11 +110,13 @@ export const DestinationLinodeObjectStorageDetailsForm = () => {
/>
<Controller
control={control}
name="access_key_secret"
render={({ field }) => (
name={controlPaths.accessKeySecret}
render={({ field, fieldState }) => (
<HideShowText
aria-required
errorText={fieldState.error?.message}
label="Secret Access Key"
onBlur={field.onBlur}
onChange={(value) => field.onChange(value)}
placeholder="Secret Access Key..."
value={field.value}
Expand All @@ -96,11 +128,13 @@ export const DestinationLinodeObjectStorageDetailsForm = () => {
<Box alignItems="end" display="flex" flexWrap="wrap" gap="16px">
<Controller
control={control}
name="path"
render={({ field }) => (
name={controlPaths.path}
render={({ field, fieldState }) => (
<TextField
aria-required
errorText={fieldState.error?.message}
label="Log Path Prefix"
onBlur={field.onBlur}
onChange={(value) => field.onChange(value)}
placeholder="Log Path Prefix..."
sx={{ width: 416 }}
Expand Down
9 changes: 6 additions & 3 deletions packages/manager/src/features/DataStream/Shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
type LinodeObjectStorageDetails,
} from '@linode/api-v4';

import type { CustomHTTPsDetails } from '@linode/api-v4';

export interface DestinationTypeOption {
label: string;
value: string;
Expand All @@ -20,7 +22,8 @@ export const destinationTypeOptions: DestinationTypeOption[] = [
},
];

export interface CreateDestinationForm extends LinodeObjectStorageDetails {
destination_label: string;
destination_type: DestinationType;
export interface CreateDestinationForm {
details: CustomHTTPsDetails | LinodeObjectStorageDetails;
label: string;
type: DestinationType;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ describe('StreamCreateCheckoutBar', () => {
component: <StreamCreateCheckoutBar createStream={createStream} />,
useFormOptions: {
defaultValues: {
destination_type: destinationType.LinodeObjectStorage,
destination: {
type: destinationType.LinodeObjectStorage,
},
},
},
});
Expand All @@ -46,9 +48,13 @@ describe('StreamCreateCheckoutBar', () => {
const TestFormComponent = () => {
const methods = useForm({
defaultValues: {
type: streamType.AuditLogs,
destination_type: destinationType.LinodeObjectStorage,
label: '',
stream: {
label: '',
type: streamType.AuditLogs,
},
destination: {
type: destinationType.LinodeObjectStorage,
},
},
});

Expand Down
Loading