-
Notifications
You must be signed in to change notification settings - Fork 593
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add create channel form via add flow
- Loading branch information
karthik
committed
Jul 17, 2020
1 parent
dccfe8f
commit bde2b5b
Showing
20 changed files
with
760 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
frontend/packages/knative-plugin/src/components/add/EventingChannelPage.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import * as React from 'react'; | ||
import { Helmet } from 'react-helmet'; | ||
import { RouteComponentProps } from 'react-router'; | ||
import { PageBody, getBadgeFromType } from '@console/shared'; | ||
import { PageHeading } from '@console/internal/components/utils'; | ||
import NamespacedPage, { | ||
NamespacedPageVariants, | ||
} from '@console/dev-console/src/components/NamespacedPage'; | ||
import { QUERY_PROPERTIES } from '@console/dev-console/src/const'; | ||
import { KnativeEventingModel } from '../../models'; | ||
import { useChannelList } from '../../utils/create-channel-utils'; | ||
import AddChannel from './channels/AddChannel'; | ||
|
||
type EventingChannelPageProps = RouteComponentProps<{ ns?: string }>; | ||
|
||
const EventingChannelPage: React.FC<EventingChannelPageProps> = ({ match, location }) => { | ||
const namespace = match.params.ns; | ||
const channels = useChannelList(namespace); | ||
const searchParams = new URLSearchParams(location.search); | ||
return ( | ||
<NamespacedPage disabled variant={NamespacedPageVariants.light}> | ||
<Helmet> | ||
<title>Channel</title> | ||
</Helmet> | ||
<PageHeading badge={getBadgeFromType(KnativeEventingModel.badge)} title="Channel"> | ||
Create a Knative Channel to create an event forwarding and persistence layer with in-memory | ||
and reliable implementations | ||
</PageHeading> | ||
<PageBody flexLayout> | ||
<AddChannel | ||
namespace={namespace} | ||
channels={channels} | ||
selectedApplication={searchParams.get(QUERY_PROPERTIES.APPLICATION)} | ||
contextSource={searchParams.get(QUERY_PROPERTIES.CONTEXT_SOURCE)} | ||
/> | ||
</PageBody> | ||
</NamespacedPage> | ||
); | ||
}; | ||
|
||
export default EventingChannelPage; |
88 changes: 88 additions & 0 deletions
88
frontend/packages/knative-plugin/src/components/add/channels/AddChannel.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import * as React from 'react'; | ||
import { Formik } from 'formik'; | ||
import { connect } from 'react-redux'; | ||
import { history } from '@console/internal/components/utils'; | ||
import { getActiveApplication } from '@console/internal/reducers/ui'; | ||
import { RootState } from '@console/internal/redux'; | ||
import { ALL_APPLICATIONS_KEY } from '@console/shared'; | ||
import { K8sResourceKind, k8sCreate, modelFor, referenceFor } from '@console/internal/module/k8s'; | ||
import { sanitizeApplicationValue } from '@console/dev-console/src/utils/application-utils'; | ||
import { AddChannelFormData, ChannelListProps } from '../import-types'; | ||
import { addChannelValidationSchema } from '../eventSource-validation-utils'; | ||
import ChannelForm from './ChannelForm'; | ||
import { getCreateChannelResource } from '../../../utils/create-channel-utils'; | ||
|
||
interface ChannelProps { | ||
namespace: string; | ||
channels: ChannelListProps; | ||
contextSource?: string; | ||
selectedApplication?: string; | ||
} | ||
|
||
interface StateProps { | ||
activeApplication: string; | ||
} | ||
|
||
type Props = ChannelProps & StateProps; | ||
|
||
const AddChannel: React.FC<Props> = ({ namespace, channels, activeApplication }) => { | ||
const initialValues: AddChannelFormData = { | ||
application: { | ||
initial: sanitizeApplicationValue(activeApplication), | ||
name: sanitizeApplicationValue(activeApplication), | ||
selectedKey: activeApplication, | ||
}, | ||
name: '', | ||
namespace, | ||
apiVersion: '', | ||
type: '', | ||
data: {}, | ||
yamlData: '', | ||
}; | ||
|
||
const createResources = (rawFormData: any): Promise<K8sResourceKind> => { | ||
const channelResource = getCreateChannelResource(rawFormData); | ||
if (channelResource?.kind && modelFor(referenceFor(channelResource))) { | ||
return k8sCreate(modelFor(referenceFor(channelResource)), channelResource); | ||
} | ||
const errMessage = | ||
channelResource?.kind && channelResource?.apiVersion | ||
? `No model registered for ${referenceFor(channelResource)}` | ||
: 'Invalid yaml'; | ||
return Promise.reject(new Error(errMessage)); | ||
}; | ||
|
||
const handleSubmit = (values, actions) => { | ||
createResources(values) | ||
.then(() => { | ||
actions.setSubmitting(false); | ||
history.push(`/topology/ns/${values.namespace}`); | ||
}) | ||
.catch((err) => { | ||
actions.setSubmitting(false); | ||
actions.setStatus({ submitError: err.message }); | ||
}); | ||
}; | ||
|
||
return ( | ||
<Formik | ||
initialValues={initialValues} | ||
onSubmit={handleSubmit} | ||
onReset={history.goBack} | ||
validateOnBlur={false} | ||
validateOnChange={false} | ||
validationSchema={addChannelValidationSchema} | ||
> | ||
{(formikProps) => <ChannelForm {...formikProps} namespace={namespace} channels={channels} />} | ||
</Formik> | ||
); | ||
}; | ||
|
||
const mapStateToProps = (state: RootState, ownProps: ChannelProps): StateProps => { | ||
const activeApplication = ownProps.selectedApplication || getActiveApplication(state); | ||
return { | ||
activeApplication: activeApplication !== ALL_APPLICATIONS_KEY ? activeApplication : '', | ||
}; | ||
}; | ||
|
||
export default connect(mapStateToProps)(AddChannel); |
107 changes: 107 additions & 0 deletions
107
frontend/packages/knative-plugin/src/components/add/channels/ChannelForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import * as React from 'react'; | ||
import * as _ from 'lodash'; | ||
import { Alert } from '@patternfly/react-core'; | ||
import { FormikProps, FormikValues, useFormikContext } from 'formik'; | ||
import { FormFooter, FlexForm, useFormikValidationFix } from '@console/shared'; | ||
import { LoadingInline } from '@console/internal/components/utils'; | ||
import { | ||
isDefaultChannel, | ||
getChannelKind, | ||
getChannelData, | ||
useDefaultChannelConfiguration, | ||
} from '../../../utils/create-channel-utils'; | ||
import { ChannelListProps } from '../import-types'; | ||
import FormViewSection from './sections/FormViewSection'; | ||
import ChannelSelector from './form-fields/ChannelSelector'; | ||
import ChannelYamlEditor from './form-fields/ChannelYamlEditor'; | ||
|
||
interface OwnProps { | ||
namespace: string; | ||
channels: ChannelListProps; | ||
} | ||
|
||
const ChannelForm: React.FC<FormikProps<FormikValues> & OwnProps> = ({ | ||
errors, | ||
handleSubmit, | ||
handleReset, | ||
status, | ||
isSubmitting, | ||
dirty, | ||
namespace, | ||
channels, | ||
}) => { | ||
const { | ||
values, | ||
setFieldValue, | ||
setFieldTouched, | ||
validateForm, | ||
setErrors, | ||
setStatus, | ||
} = useFormikContext<FormikValues>(); | ||
useFormikValidationFix(values); | ||
const [defaultConfiguredChannel, defaultConfiguredChannelLoaded] = useDefaultChannelConfiguration( | ||
namespace, | ||
); | ||
const channelHasFormView = values.type && isDefaultChannel(getChannelKind(values.type)); | ||
const channelKind = getChannelKind(values.type); | ||
const onTypeChange = React.useCallback( | ||
(item: string) => { | ||
setErrors({}); | ||
setStatus({}); | ||
const kind = getChannelKind(item); | ||
if (isDefaultChannel(kind)) { | ||
const nameData = `data.${kind.toLowerCase()}`; | ||
const sourceData = getChannelData(kind.toLowerCase()); | ||
setFieldValue(nameData, sourceData); | ||
setFieldTouched(nameData, true); | ||
} | ||
setFieldValue('yamlData', ''); | ||
setFieldTouched('yamlData', true); | ||
|
||
setFieldValue('type', item); | ||
setFieldTouched('type', true); | ||
|
||
setFieldValue('name', _.kebabCase(`${kind}`)); | ||
setFieldTouched('name', true); | ||
validateForm(); | ||
}, | ||
[setErrors, setStatus, setFieldValue, setFieldTouched, validateForm], | ||
); | ||
|
||
return ( | ||
<FlexForm onSubmit={handleSubmit}> | ||
{((channels && !channels.loaded) || !defaultConfiguredChannelLoaded) && <LoadingInline />} | ||
{channels && | ||
channels.loaded && | ||
defaultConfiguredChannelLoaded && | ||
!_.isEmpty(channels.channelList) && ( | ||
<> | ||
<ChannelSelector | ||
channels={channels.channelList} | ||
onChange={onTypeChange} | ||
defaultConfiguredChannel={defaultConfiguredChannel} | ||
defaultConfiguredChannelLoaded={defaultConfiguredChannelLoaded} | ||
/> | ||
{channelHasFormView && <FormViewSection namespace={namespace} kind={channelKind} />} | ||
{!channelHasFormView && <ChannelYamlEditor />} | ||
</> | ||
)} | ||
{channels && channels.loaded && _.isEmpty(channels.channelList) && ( | ||
<Alert variant="default" title="Channel cannot be created" isInline> | ||
You do not have write access in this project. | ||
</Alert> | ||
)} | ||
<FormFooter | ||
handleReset={handleReset} | ||
errorMessage={status && status.submitError} | ||
isSubmitting={isSubmitting} | ||
submitLabel="Create" | ||
disableSubmit={!dirty || !_.isEmpty(errors)} | ||
resetLabel="Cancel" | ||
sticky | ||
/> | ||
</FlexForm> | ||
); | ||
}; | ||
|
||
export default ChannelForm; |
71 changes: 71 additions & 0 deletions
71
frontend/packages/knative-plugin/src/components/add/channels/form-fields/ChannelSelector.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import * as React from 'react'; | ||
import * as _ from 'lodash'; | ||
import { useField } from 'formik'; | ||
import FormSection from '@console/dev-console/src/components/import/section/FormSection'; | ||
import { DropdownField, DropdownFieldProps } from '@console/shared'; | ||
import { getChannelKind } from '../../../../utils/create-channel-utils'; | ||
import { EventingChannelModel } from '../../../../models'; | ||
|
||
type ChannelSelectorProps = { | ||
channels: string[]; | ||
defaultConfiguredChannel: string; | ||
defaultConfiguredChannelLoaded: boolean; | ||
} & Omit<DropdownFieldProps, 'name'>; | ||
|
||
const ChannelSelector: React.FC<ChannelSelectorProps> = ({ | ||
channels, | ||
onChange, | ||
defaultConfiguredChannel, | ||
defaultConfiguredChannelLoaded, | ||
}) => { | ||
const [selected] = useField('type'); | ||
|
||
const filteredChannels = _.chain(channels) | ||
.filter((ch) => EventingChannelModel.kind !== getChannelKind(ch)) | ||
.partition((ref) => getChannelKind(ref) === defaultConfiguredChannel) | ||
.flatten() | ||
.value(); | ||
|
||
const channelData = filteredChannels.reduce((acc, channel) => { | ||
const channelName = getChannelKind(channel); | ||
acc[channel] = | ||
channelName === defaultConfiguredChannel ? `${channelName} (Default)` : channelName; | ||
return acc; | ||
}, {}); | ||
|
||
const getDefaultChannel = React.useCallback((): string => { | ||
return ( | ||
filteredChannels.find((ch) => getChannelKind(ch) === defaultConfiguredChannel) || | ||
filteredChannels[0] | ||
); | ||
}, [defaultConfiguredChannel, filteredChannels]); | ||
|
||
React.useEffect(() => { | ||
if (!selected.value && defaultConfiguredChannelLoaded && filteredChannels.length > 0) { | ||
const channel = getDefaultChannel(); | ||
onChange && onChange(channel); | ||
} | ||
}, [ | ||
selected.value, | ||
defaultConfiguredChannelLoaded, | ||
getDefaultChannel, | ||
onChange, | ||
filteredChannels.length, | ||
]); | ||
|
||
return ( | ||
<FormSection extraMargin> | ||
<DropdownField | ||
name="type" | ||
label="Type" | ||
items={channelData} | ||
title="Type" | ||
onChange={onChange} | ||
fullWidth | ||
required | ||
/> | ||
</FormSection> | ||
); | ||
}; | ||
|
||
export default ChannelSelector; |
36 changes: 36 additions & 0 deletions
36
...end/packages/knative-plugin/src/components/add/channels/form-fields/ChannelYamlEditor.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import * as React from 'react'; | ||
import { safeDump } from 'js-yaml'; | ||
import { YAMLEditorField } from '@console/shared'; | ||
import FormSection from '@console/dev-console/src/components/import/section/FormSection'; | ||
import { useFormikContext, FormikValues } from 'formik'; | ||
import { | ||
isDefaultChannel, | ||
getChannelKind, | ||
getCreateChannelData, | ||
} from '../../../../utils/create-channel-utils'; | ||
import { AddChannelFormData } from '../../import-types'; | ||
|
||
const ChannelYamlEditor: React.FC = () => { | ||
const { values, setFieldValue, setFieldTouched } = useFormikContext<FormikValues>(); | ||
const formData = React.useRef(values); | ||
if (formData.current.type !== values.type) { | ||
formData.current = values; | ||
} | ||
React.useEffect(() => { | ||
if (values.type && !isDefaultChannel(getChannelKind(values.type))) { | ||
setFieldValue( | ||
'yamlData', | ||
safeDump(getCreateChannelData(formData.current as AddChannelFormData)), | ||
); | ||
setFieldTouched('yamlData', true); | ||
} | ||
}, [values.type, setFieldTouched, setFieldValue]); | ||
|
||
return ( | ||
<FormSection flexLayout fullWidth> | ||
<YAMLEditorField name="yamlData" /> | ||
</FormSection> | ||
); | ||
}; | ||
|
||
export default ChannelYamlEditor; |
25 changes: 25 additions & 0 deletions
25
...nd/packages/knative-plugin/src/components/add/channels/sections/DefaultChannelSection.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import * as React from 'react'; | ||
import { TextInputTypes } from '@patternfly/react-core'; | ||
import ApplicationSelector from '@console/dev-console/src/components/import/app/ApplicationSelector'; | ||
import FormSection from '@console/dev-console/src/components/import/section/FormSection'; | ||
import { InputField } from '@console/shared'; | ||
|
||
type DefaultChannelSectionProps = { | ||
namespace: string; | ||
}; | ||
|
||
const DefaultChannelSection: React.FC<DefaultChannelSectionProps> = ({ namespace }) => ( | ||
<FormSection extraMargin> | ||
<ApplicationSelector namespace={namespace} /> | ||
<InputField | ||
type={TextInputTypes.text} | ||
data-test-id="channel-name" | ||
name="name" | ||
label="Name" | ||
helpText="A unique name for the component/channel" | ||
required | ||
/> | ||
</FormSection> | ||
); | ||
|
||
export default DefaultChannelSection; |
Oops, something went wrong.