Skip to content

Commit

Permalink
add Kamelets- schema generated forms & yaml/form switching
Browse files Browse the repository at this point in the history
  • Loading branch information
vikram-raj committed Dec 3, 2020
1 parent e9bbb1f commit a0ffbca
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const DynamicForm: React.FC<DynamicFormProps> = ({
widgets = {},
customUISchema,
noActions,
showAlert = true,
...restProps
}) => {
const { t } = useTranslation();
Expand All @@ -56,14 +57,16 @@ export const DynamicForm: React.FC<DynamicFormProps> = ({
}
return (
<>
<Alert
isInline
className="co-alert co-break-word"
variant="info"
title={t(
'console-shared~Note: Some fields may not be represented in this form view. Please select "YAML view" for full control.',
)}
/>
{showAlert && (
<Alert
isInline
className="co-alert co-break-word"
variant="info"
title={t(
'console-shared~Note: Some fields may not be represented in this form view. Please select "YAML view" for full control.',
)}
/>
)}
<Accordion asDefinitionList={false} className="co-dynamic-form__accordion">
<Form
{...restProps}
Expand Down Expand Up @@ -109,6 +112,7 @@ type DynamicFormProps = FormProps<any> & {
ErrorTemplate?: React.FC<{ errors: string[] }>;
noActions?: boolean;
customUISchema?: boolean;
showAlert?: boolean;
onCancel?: () => void;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import * as React from 'react';
import { FormProps } from 'react-jsonschema-form';
import cx from 'classnames';
import { useField, useFormikContext, FormikValues } from 'formik';
import { AsyncComponent } from '@console/internal/components/utils';

type DynamicFormFieldProps = FormProps<any> & {
name: string;
errors?: string[];
formDescription?: React.ReactNode;
showAlert?: boolean;
};

const DynamicFormField: React.FC<DynamicFormFieldProps> = ({
Expand All @@ -16,20 +18,30 @@ const DynamicFormField: React.FC<DynamicFormFieldProps> = ({
errors,
formDescription,
formContext,
showAlert,
}) => {
const [field] = useField(name);
const { setFieldValue } = useFormikContext<FormikValues>();

return (
<div className="row">
<div className="col-sm-12 col-md-4 col-md-push-8 col-lg-5 col-lg-push-7">
<div className={cx({ row: formDescription })}>
<div
className={cx({
'col-sm-12 col-md-4 col-md-push-8 col-lg-5 col-lg-push-7': formDescription,
})}
>
{formDescription}
</div>
<div className="col-sm-12 col-md-8 col-md-pull-4 col-lg-7 col-lg-pull-5">
<div
className={cx({
'col-sm-12 col-md-8 col-md-pull-4 col-lg-7 col-lg-pull-5': formDescription,
})}
>
<AsyncComponent
loader={() => import('../dynamic-form').then((c) => c.DynamicForm)}
errors={errors}
formContext={formContext}
showAlert={showAlert}
uiSchema={uiSchema}
formData={field.value}
onChange={(data) => setFieldValue(name, data)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const CatalogDetailsModal: React.FC<CatalogDetailsModalProps> = ({ item, onClose
Object.values(CatalogQueryParams).map((q) => queryParams.delete(q)); // don't pass along catalog specific query params

const to = params
? `${url}?${params}&${queryParams.toString()}`
? `${url}?${params}${Object.keys(queryParams).length > 0 ? `&${queryParams.toString()}` : ''}`
: `${url}?${queryParams.toString()}`;

const vendor = item.provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
"Event sources": "Event sources",
"Event sources are objects that link to an event producer and an event sink or consumer. Cluster administrators can customize the content made available in the catalog.": "Event sources are objects that link to an event producer and an event sink or consumer. Cluster administrators can customize the content made available in the catalog.",
"**Event sources** are objects that link to an event producer and an event sink or consumer.": "**Event sources** are objects that link to an event producer and an event sink or consumer.",
"Provider": "Provider",
"No Revisions": "No Revisions",
"Move sink to {{resourceObjKind}}": "Move sink to {{resourceObjKind}}",
"Move sink to URI": "Move sink to URI",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
isKnownEventSource,
getEventSourceData,
handleRedirect,
getKameletSourceData,
} from '../../utils/create-eventsources-utils';
import { getEventSourceModels } from '../../utils/fetch-dynamic-eventsources-utils';
import { KNATIVE_EVENT_SOURCE_APIGROUP } from '../../const';
Expand All @@ -33,6 +34,7 @@ import {
SinkType,
EVENT_SOURCES_APP,
} from './import-types';
import { CamelKameletBindingModel } from '../../models';
import EventSourceMetaDescription from './EventSourceMetadataDescription';

interface EventSourceProps {
Expand All @@ -41,6 +43,7 @@ interface EventSourceProps {
contextSource?: string;
selectedApplication?: string;
sourceKind?: string;
kameletSource?: K8sResourceKind;
}

interface StateProps {
Expand All @@ -57,6 +60,7 @@ export const EventSource: React.FC<Props> = ({
contextSource,
perspective,
sourceKind = '',
kameletSource,
}) => {
const perpectiveExtension = useExtensions<Perspective>(isPerspective);
const { t } = useTranslation();
Expand All @@ -67,11 +71,17 @@ export const EventSource: React.FC<Props> = ({
const selDataModel = _.find(getEventSourceModels(), { kind: sourceKind });
selApiVersion = selDataModel
? `${selDataModel?.apiGroup}/${selDataModel?.apiVersion}`
: kameletSource
? `${CamelKameletBindingModel.apiGroup}/${CamelKameletBindingModel.apiVersion}`
: `${KNATIVE_EVENT_SOURCE_APIGROUP}/v1alpha2`;
sourceData = isKnownEventSource(sourceKind)
? { [sourceKind]: getEventSourceData(sourceKind) }
? kameletSource
? { [sourceKind]: getKameletSourceData(kameletSource) }
: { [sourceKind]: getEventSourceData(sourceKind) }
: {};
selSourceName = _.kebabCase(sourceKind);
selSourceName = kameletSource
? `kamelet-${kameletSource.metadata.name}`
: _.kebabCase(sourceKind);
}
const [sinkGroupVersionKind = '', sinkName = ''] = contextSource?.split('/') ?? [];
const [sinkGroup = '', sinkVersion = '', sinkKind = ''] =
Expand All @@ -82,7 +92,6 @@ export const EventSource: React.FC<Props> = ({
const eventSourceMetaDescription = (
<EventSourceMetaDescription eventSourceStatus={eventSourceStatus} sourceKind={sourceKind} />
);

const catalogInitialValues: EventSourceSyncFormData = {
editorType: EditorType.Form,
showCanUseYAMLMessage: true,
Expand Down Expand Up @@ -160,6 +169,7 @@ export const EventSource: React.FC<Props> = ({
namespace={namespace}
eventSourceStatus={eventSourceStatus}
eventSourceMetaDescription={eventSourceMetaDescription}
kameletSource={kameletSource}
/>
)}
</Formik>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface OwnProps {
namespace: string;
eventSourceStatus: EventSourceListData | null;
eventSourceMetaDescription: React.ReactNode;
kameletSource: K8sResourceKind;
}

const EventSourceForm: React.FC<FormikProps<FormikValues> & OwnProps> = ({
Expand All @@ -39,6 +40,7 @@ const EventSourceForm: React.FC<FormikProps<FormikValues> & OwnProps> = ({
namespace,
eventSourceStatus,
eventSourceMetaDescription,
kameletSource,
}) => {
const { t } = useTranslation();
const yamlEditor = <YAMLEditorField name="yamlData" onSave={handleSubmit} />;
Expand Down Expand Up @@ -73,6 +75,18 @@ const EventSourceForm: React.FC<FormikProps<FormikValues> & OwnProps> = ({
[values.formData.type]: {
..._.omit(specData, 'sink'),
},
...(kameletSource && {
[values.formData.type]: {
source: {
ref: {
apiVersion: kameletSource.apiVersion,
kind: kameletSource.kind,
name: kameletSource.metadata.name,
},
properties: specData?.source?.properties,
},
},
}),
},
};
return values.formData.type === EventSources.KafkaSource
Expand Down Expand Up @@ -108,15 +122,15 @@ const EventSourceForm: React.FC<FormikProps<FormikValues> & OwnProps> = ({
variant="info"
/>
)}
<EventSourceSection namespace={namespace} catalogFlow fullWidth />{' '}
<EventSourceSection namespace={namespace} kameletSource={kameletSource} fullWidth />{' '}
</div>
</div>
)}
</>
);
return (
<FlexForm onSubmit={handleSubmit}>
{isDynamicEventSourceKind(values.formData.type) && (
{(isDynamicEventSourceKind(values.formData.type) || kameletSource) && (
<SyncedEditorField
name="editorType"
formContext={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,41 @@ import { RouteComponentProps } from 'react-router';
import { useTranslation } from 'react-i18next';
import { PageBody } from '@console/shared';
import { LoadingInline, PageHeading } from '@console/internal/components/utils';
import { K8sResourceKind } from '@console/internal/module/k8s';
import { useK8sGet } from '@console/internal/components/utils/k8s-get-hook';
import NamespacedPage, {
NamespacedPageVariants,
} from '@console/dev-console/src/components/NamespacedPage';
import { QUERY_PROPERTIES } from '@console/dev-console/src/const';
import ConnectedEventSource from './EventSource';
import EventSourceAlert from './EventSourceAlert';
import { useEventSourceList } from '../../utils/create-eventsources-utils';
import {
addCamelKameletSourceList,
useEventSourceList,
} from '../../utils/create-eventsources-utils';
import { isDynamicEventSourceKind } from '../../utils/fetch-dynamic-eventsources-utils';
import { CamelKameletBindingModel, CamelKameletModel } from '../../models';

type EventSourcePageProps = RouteComponentProps<{ ns?: string }>;

const EventSourcePage: React.FC<EventSourcePageProps> = ({ match, location }) => {
const { t } = useTranslation();
const namespace = match.params.ns;
const eventSourceStatus = useEventSourceList(namespace);
const eventSourceList = useEventSourceList(namespace);
const searchParams = new URLSearchParams(location.search);
const sourceKindProp = searchParams.get('sourceKind');
const isSourceKindPresent = sourceKindProp && isDynamicEventSourceKind(sourceKindProp);

const kameletName = sourceKindProp && searchParams.get('name');
const [kamelet, kameletLoaded, kameletLoadError] = useK8sGet<K8sResourceKind>(
CamelKameletModel,
kameletName,
namespace,
);
const isKameletSource = kameletName && sourceKindProp === CamelKameletBindingModel.kind;
const isSourceKindPresent =
(sourceKindProp && isDynamicEventSourceKind(sourceKindProp)) || isKameletSource;
const eventSourceStatus = isKameletSource
? addCamelKameletSourceList(eventSourceList, kamelet, kameletLoaded, kameletLoadError)
: eventSourceList;
return (
<NamespacedPage disabled variant={NamespacedPageVariants.light}>
<Helmet>
Expand All @@ -36,7 +52,7 @@ const EventSourcePage: React.FC<EventSourcePageProps> = ({ match, location }) =>
<PageBody flexLayout>
<EventSourceAlert
eventSourceStatus={eventSourceStatus}
showSourceKindAlert={!isSourceKindPresent}
showSourceKindAlert={!isSourceKindPresent || kameletLoadError}
/>
{eventSourceStatus?.loaded ? (
<ConnectedEventSource
Expand All @@ -45,6 +61,7 @@ const EventSourcePage: React.FC<EventSourcePageProps> = ({ match, location }) =>
selectedApplication={searchParams.get(QUERY_PROPERTIES.APPLICATION)}
contextSource={searchParams.get(QUERY_PROPERTIES.CONTEXT_SOURCE)}
sourceKind={searchParams.get('sourceKind')}
kameletSource={isKameletSource && kamelet}
/>
) : (
<LoadingInline />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import * as React from 'react';
import { useFormikContext, FormikValues } from 'formik';
import * as _ from 'lodash';
import { useFormikValidationFix } from '@console/shared';
import { JSONSchema6 } from 'json-schema';
import { TextVariants, Text } from '@patternfly/react-core';
import { DynamicFormField, useFormikValidationFix } from '@console/shared';
import { capabilityWidgetMap } from '@console/operator-lifecycle-manager/src/components/descriptors/spec/spec-descriptor-input';
import { useK8sWatchResource } from '@console/internal/components/utils/k8s-watch-hook';
import { K8sResourceKind } from '@console/internal/module/k8s';
import AppSection from '@console/dev-console/src/components/import/app/AppSection';
Expand All @@ -20,18 +23,42 @@ import { isKnownEventSource } from '../../../utils/create-eventsources-utils';
interface EventSourceSectionProps {
namespace: string;
fullWidth?: boolean;
catalogFlow?: boolean;
kameletSource?: K8sResourceKind;
}

const getUISchema = (formSchema) => {
const uiSchema = {};
for (const k in formSchema.properties) {
if (formSchema.properties.hasOwnProperty(k)) {
uiSchema[k] = {
'ui:title': formSchema.properties[k].title,
'ui:description': formSchema.properties[k].description,
...(formSchema.properties[k].hasOwnProperty('x-descriptors')
? { 'ui:widget': capabilityWidgetMap.get(formSchema.properties[k]['x-descriptors'][0]) }
: {}),
};
}
}
return uiSchema;
};

const EventSourceSection: React.FC<EventSourceSectionProps> = ({
namespace,
fullWidth = false,
catalogFlow = false,
kameletSource,
}) => {
const { values } = useFormikContext<FormikValues>();
const projectResource = { kind: ProjectModel.kind, prop: ProjectModel.id, isList: true };
const [data, loaded] = useK8sWatchResource<K8sResourceKind[]>(projectResource);
useFormikValidationFix(values);
const formSchema: JSONSchema6 = React.useMemo(
() => ({
type: 'object',
required: kameletSource?.spec?.definition?.required,
properties: kameletSource?.spec?.definition?.properties,
}),
[kameletSource],
);

if (!values.formData.type) {
return null;
Expand Down Expand Up @@ -69,13 +96,26 @@ const EventSourceSection: React.FC<EventSourceSectionProps> = ({
case EventSources.PingSource:
EventSource = <PingSourceSection title={sectionTitle} fullWidth={fullWidth} />;
break;
case EventSources.KameletBinding:
EventSource = kameletSource && (
<>
<Text component={TextVariants.h2}>{kameletSource.spec?.definition?.title}</Text>
<DynamicFormField
name="formData.data.KameletBinding.source.properties"
schema={formSchema}
uiSchema={getUISchema(formSchema)}
showAlert={false}
/>
</>
);
break;
default:
EventSource = !catalogFlow ? <YAMLEditorSection title={sectionTitle} /> : null;
EventSource = <YAMLEditorSection title={sectionTitle} />;
}
return (
<>
{EventSource}
{(isKnownEventSource(values.formData.type) || catalogFlow) && defaultFormSection}
{isKnownEventSource(values.formData.type) && defaultFormSection}
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
EventSourceSinkBindingModel,
EventingIMCModel,
EventingKafkaChannelModel,
CamelKameletBindingModel,
} from '../../models';

export const EventSources = {
Expand All @@ -16,6 +17,7 @@ export const EventSources = {
KafkaSource: EventSourceKafkaModel.kind,
PingSource: EventSourcePingModel.kind,
SinkBinding: EventSourceSinkBindingModel.kind,
KameletBinding: CamelKameletBindingModel.kind,
};
export const defaultChannels = {
InMemoryChannel: EventingIMCModel,
Expand Down
Loading

0 comments on commit a0ffbca

Please sign in to comment.