Skip to content

Commit

Permalink
add support for creating camel source
Browse files Browse the repository at this point in the history
  • Loading branch information
nemesis09 committed Apr 9, 2020
1 parent 002f911 commit 8250960
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 100 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as React from 'react';
import * as _ from 'lodash';
import { Formik } from 'formik';
import { connect } from 'react-redux';
import { safeDump } from 'js-yaml';
import { history } from '@console/internal/components/utils';
import { getActiveApplication } from '@console/internal/reducers/ui';
import { RootState } from '@console/internal/redux';
Expand All @@ -10,10 +12,13 @@ import { FirehoseList } from '@console/dev-console/src/components/import/import-
import { sanitizeApplicationValue } from '@console/dev-console/src/utils/application-utils';
import { eventSourceValidationSchema } from './eventSource-validation-utils';
import EventSourceForm from './EventSourceForm';
import { EventSources, EventSourceFormData } from './import-types';
import { KnownEventSources, EventSourceFormData, EditorType } from './import-types';
import {
getEventSourcesDepResource,
getEventSourceData,
getEventSourceYaml,
getEventSourcesResourceFromYAML,
isKnownEventSourceSelected,
} from '../../utils/create-eventsources-utils';

interface EventSourceProps {
Expand All @@ -35,8 +40,9 @@ const EventSource: React.FC<Props> = ({
activeApplication,
contextSource,
}) => {
const typeEventSource = EventSources.CronJobSource;
const typeEventSource = KnownEventSources.CronJobSource;
const serviceName = contextSource?.split('/').pop() || '';
const eventYaml = getEventSourceYaml(namespace, typeEventSource.toLocaleLowerCase());
const initialValues: EventSourceFormData = {
project: {
name: namespace || '',
Expand All @@ -56,10 +62,17 @@ const EventSource: React.FC<Props> = ({
data: {
[typeEventSource.toLowerCase()]: getEventSourceData(typeEventSource.toLowerCase()),
},
yamlData: !_.isEmpty(eventYaml) ? safeDump(eventYaml) : '',
editorType: EditorType.Form,
};

const createResources = (rawFormData: any): Promise<K8sResourceKind> => {
const knEventSourceResource = getEventSourcesDepResource(rawFormData);
let knEventSourceResource: K8sResourceKind;
if (isKnownEventSourceSelected(rawFormData.type)) {
knEventSourceResource = getEventSourcesDepResource(rawFormData);
} else {
knEventSourceResource = getEventSourcesResourceFromYAML(rawFormData);
}
return k8sCreate(modelFor(referenceFor(knEventSourceResource)), knEventSourceResource);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { FormFooter } from '@console/shared';
import { Form } from '@patternfly/react-core';
import AppSection from '@console/dev-console/src/components/import/app/AppSection';
import { FirehoseList } from '@console/dev-console/src/components/import/import-types';
import CronJobSection from './event-sources/CronJobSection';
import SinkBindingSection from './event-sources/SinkBindingSection';
import ApiServerSection from './event-sources/ApiServerSection';
import SinkSection from './event-sources/SinkSection';
import { EventSources } from './import-types';
import EventSourcesSelector from './event-sources/EventSourcesSelector';
import { useEventSourceList } from '../../utils/create-eventsources-utils';
import SinkSection from './event-sources/SinkSection';
import {
useEventSourceList,
renderEventSourceSection,
isKnownEventSourceSelected,
} from '../../utils/create-eventsources-utils';

interface OwnProps {
namespace: string;
Expand All @@ -28,27 +28,29 @@ const EventSourceForm: React.FC<FormikProps<FormikValues> & OwnProps> = ({
dirty,
namespace,
projects,
}) => (
<Form className="co-deploy-image" onSubmit={handleSubmit}>
<EventSourcesSelector eventSourceList={useEventSourceList(namespace)} />
{values.type === EventSources.CronJobSource && <CronJobSection />}
{values.type === EventSources.SinkBinding && <SinkBindingSection />}
{values.type === EventSources.ApiServerSource && <ApiServerSection namespace={namespace} />}
<SinkSection namespace={namespace} />
<AppSection
project={values.project}
noProjectsAvailable={projects?.loaded && _.isEmpty(projects.data)}
/>
<FormFooter
handleReset={handleReset}
errorMessage={status && status.submitError}
isSubmitting={isSubmitting}
submitLabel="Create"
sticky
disableSubmit={!dirty || !_.isEmpty(errors)}
resetLabel="Cancel"
/>
</Form>
);
}) => {
const EventSourceSection: React.FC = renderEventSourceSection(values.type);
return (
<Form className="co-deploy-image" onSubmit={handleSubmit}>
<EventSourcesSelector eventSourceList={useEventSourceList(namespace)} />
<EventSourceSection />
{isKnownEventSourceSelected(values.type) && <SinkSection namespace={namespace} />}
{isKnownEventSourceSelected(values.type) && (
<AppSection
project={values.project}
noProjectsAvailable={projects?.loaded && _.isEmpty(projects.data)}
/>
)}
<FormFooter
handleReset={handleReset}
errorMessage={status && status.submitError}
isSubmitting={isSubmitting}
submitLabel="Create"
disableSubmit={!dirty || !_.isEmpty(errors)}
resetLabel="Cancel"
/>
</Form>
);
};

export default EventSourceForm;
Original file line number Diff line number Diff line change
@@ -1,68 +1,45 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import { cloneDeep } from 'lodash';
import { formikFormProps } from '@console/shared/src/test-utils/formik-props-utils';
import AppSection from '@console/dev-console/src/components/import/app/AppSection';
import EventSourceForm from '../EventSourceForm';
import EventSourcesSelector from '../event-sources/EventSourcesSelector';
import CronJobSection from '../event-sources/CronJobSection';
import SinkSection from '../event-sources/SinkSection';
import { getDefaultEventingData } from '../../../utils/__tests__/knative-serving-data';
import { EventSources } from '../import-types';
import { KnownEventSources } from '../import-types';

type EventSourceFormProps = React.ComponentProps<typeof EventSourceForm>;
let formProps: EventSourceFormProps;

describe('EventSource Form', () => {
const defaultEventingData = getDefaultEventingData(EventSources.CronJobSource);
const defaultEventingData = getDefaultEventingData(KnownEventSources.CronJobSource);
beforeEach(() => {
formProps = {
...formikFormProps,
values: {
type: 'CronJobSource',
},
namespace: 'myapp',
errors: {},
touched: {},
isSubmitting: true,
isValidating: true,
status: {},
submitCount: 0,
dirty: false,
handleReset: jest.fn(),
handleSubmit: jest.fn(),
getFieldProps: jest.fn(),
handleBlur: jest.fn(),
handleChange: jest.fn(),
initialErrors: {},
initialStatus: {},
initialTouched: {},
isValid: true,
projects: { loaded: true, loadError: '', data: [] },
initialValues: defaultEventingData,
registerField: jest.fn(),
resetForm: jest.fn(),
setErrors: jest.fn(),
setFieldError: jest.fn(),
setFieldTouched: jest.fn(),
setFieldValue: jest.fn(),
setFormikState: jest.fn(),
setStatus: jest.fn(),
setSubmitting: jest.fn(),
setTouched: jest.fn(),
setValues: jest.fn(),
submitForm: jest.fn(),
unregisterField: jest.fn(),
validateField: jest.fn(),
validateForm: jest.fn(),
getFieldMeta: jest.fn(),
validateOnBlur: true,
validateOnChange: true,
};
});
it('should render SinkSection , EventSourcesSelector for all sources', () => {
it('should render SinkSection , EventSourcesSelector for CronjobSource', () => {
const eventSourceForm = shallow(<EventSourceForm {...formProps} />);
expect(eventSourceForm.find(SinkSection)).toHaveLength(1);
expect(eventSourceForm.find(EventSourcesSelector)).toHaveLength(1);
});

it('should not render SinkSection , AppSection for CamelSource', () => {
const eventSourceForm = shallow(
<EventSourceForm {...formProps} values={{ type: 'CamelSource' }} />,
);
expect(eventSourceForm.find(SinkSection)).toHaveLength(0);
expect(eventSourceForm.find(AppSection)).toHaveLength(0);
});

it('should render CronJobSection for cronJob source', () => {
const eventSourceForm = shallow(<EventSourceForm {...formProps} />);
expect(eventSourceForm.find(CronJobSection)).toHaveLength(1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { cloneDeep } from 'lodash';
import { eventSourceValidationSchema } from '../eventSource-validation-utils';
import { getDefaultEventingData } from '../../../utils/__tests__/knative-serving-data';
import { EventSources } from '../import-types';
import { KnownEventSources } from '../import-types';

describe('Event Source ValidationUtils', () => {
it('should validate the form data', async () => {
const defaultEventingData = getDefaultEventingData(EventSources.CronJobSource);
const defaultEventingData = getDefaultEventingData(KnownEventSources.CronJobSource);
const mockData = cloneDeep(defaultEventingData);
await eventSourceValidationSchema
.isValid(mockData)
.then((valid) => expect(valid).toEqual(true));
});

it('should throw an error for required fields if empty', async () => {
const defaultEventingData = getDefaultEventingData(EventSources.CronJobSource);
const defaultEventingData = getDefaultEventingData(KnownEventSources.CronJobSource);
const mockData = cloneDeep(defaultEventingData);
mockData.sink.knativeService = '';
await eventSourceValidationSchema
Expand All @@ -27,15 +27,15 @@ describe('Event Source ValidationUtils', () => {

describe('ApiServerSource : Event Source Validation', () => {
it('should validate the form data', async () => {
const defaultEventingData = getDefaultEventingData(EventSources.ApiServerSource);
const defaultEventingData = getDefaultEventingData(KnownEventSources.ApiServerSource);
const mockData = cloneDeep(defaultEventingData);
await eventSourceValidationSchema
.isValid(mockData)
.then((valid) => expect(valid).toEqual(true));
});

it('should throw an error for required fields if empty', async () => {
const defaultEventingData = getDefaultEventingData(EventSources.ApiServerSource);
const defaultEventingData = getDefaultEventingData(KnownEventSources.ApiServerSource);
const mockData = cloneDeep(defaultEventingData);
mockData.sink.knativeService = '';
await eventSourceValidationSchema
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,42 @@
import * as React from 'react';
import { useFormikContext, FormikValues } from 'formik';
import { useFormikContext, FormikValues, useField } from 'formik';
import { safeDump } from 'js-yaml';
import { ItemSelectorField } from '@console/shared';
import FormSection from '@console/dev-console/src/components/import/section/FormSection';
import { NormalizedEventSources } from '../import-types';
import { getEventSourceData } from '../../../utils/create-eventsources-utils';
import { NormalizedEventSources, EditorType } from '../import-types';
import {
getEventSourceData,
getEventSourceYaml,
isKnownEventSourceSelected,
} from '../../../utils/create-eventsources-utils';

interface EventSourcesSelectorProps {
eventSourceList: NormalizedEventSources;
}

const EventSourcesSelector: React.FC<EventSourcesSelectorProps> = ({ eventSourceList }) => {
const { setFieldValue, setFieldTouched, validateForm } = useFormikContext<FormikValues>();
const [namespaceField] = useField('project.name');
const handleItemChange = React.useCallback(
(item: string) => {
const name = `data.${item.toLowerCase()}`;
setFieldValue(name, getEventSourceData(item.toLowerCase()));
setFieldTouched(name, true);
if (isKnownEventSourceSelected(item)) {
const name = `data.${item.toLowerCase()}`;
setFieldValue(name, getEventSourceData(item.toLowerCase()));
setFieldTouched(name, true);
setFieldValue('editorType', EditorType.Form);
setFieldTouched('editorType', true);
} else {
setFieldValue(
'yamlData',
safeDump(getEventSourceYaml(namespaceField.value, item.toLowerCase())),
);
setFieldTouched('yamlData', true);
setFieldValue('editorType', EditorType.YAML);
setFieldTouched('editorType', true);
}
validateForm();
},
[setFieldValue, setFieldTouched, validateForm],
[namespaceField, setFieldValue, setFieldTouched, validateForm],
);
return (
<FormSection title="Type" fullWidth>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as React from 'react';
import YAMLEditorField from '@console/shared/src/components/formik-fields/YAMLEditorField';
import FormSection from '@console/dev-console/src/components/import/section/FormSection';
import { useField } from 'formik';

const YAMLEditorSection: React.FC = () => {
const [field] = useField('type');
return (
<FormSection fullWidth title={field.value}>
<YAMLEditorField name="yamlData" />
</FormSection>
);
};

export default YAMLEditorSection;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import FormSection from '@console/dev-console/src/components/import/section/FormSection';
import YAMLEditorSection from '../YAMLEditorSection';

jest.mock('formik', () => ({
useField: jest.fn(() => [{ value: 'CamelSource' }, [{}, {}]]),
}));

describe('YAMLEditorSection', () => {
const wrapper = shallow(<YAMLEditorSection />);
it('should render FormSection with proper props', () => {
expect(wrapper.find(FormSection)).toHaveLength(1);
expect(wrapper.find(FormSection).props().title).toEqual('CamelSource');
expect(wrapper.find(FormSection).props().fullWidth).toBe(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
projectNameValidationSchema,
applicationNameValidationSchema,
} from '@console/dev-console/src/components/import/validation-schema';
import { EventSources } from './import-types';
import { KnownEventSources, EditorType } from './import-types';

const sinkServiceSchema = yup.object().shape({
knativeService: yup.string().required('Required'),
Expand All @@ -13,7 +13,7 @@ const sinkServiceSchema = yup.object().shape({
export const sourceDataSpecSchema = yup
.object()
.when('type', {
is: EventSources.CronJobSource,
is: KnownEventSources.CronJobSource,
then: yup.object().shape({
cronjobsource: yup.object().shape({
data: yup
Expand All @@ -28,7 +28,7 @@ export const sourceDataSpecSchema = yup
}),
})
.when('type', {
is: EventSources.SinkBinding,
is: KnownEventSources.SinkBinding,
then: yup.object().shape({
sinkbinding: yup.object().shape({
subject: yup.object().shape({
Expand All @@ -48,7 +48,7 @@ export const sourceDataSpecSchema = yup
}),
})
.when('type', {
is: EventSources.ApiServerSource,
is: KnownEventSources.ApiServerSource,
then: yup.object().shape({
apiserversource: yup.object().shape({
resources: yup
Expand All @@ -65,9 +65,24 @@ export const sourceDataSpecSchema = yup
});

export const eventSourceValidationSchema = yup.object().shape({
project: projectNameValidationSchema,
application: applicationNameValidationSchema,
name: nameValidationSchema,
sink: sinkServiceSchema,
data: sourceDataSpecSchema,
project: yup.object().when('editorType', {
is: EditorType.Form,
then: projectNameValidationSchema,
}),
application: yup.object().when('editorType', {
is: EditorType.Form,
then: applicationNameValidationSchema,
}),
name: yup.string().when('editorType', {
is: EditorType.Form,
then: nameValidationSchema,
}),
sink: yup.object().when('editorType', {
is: EditorType.Form,
then: sinkServiceSchema,
}),
data: yup.object().when('editorType', {
is: EditorType.Form,
then: sourceDataSpecSchema,
}),
});

0 comments on commit 8250960

Please sign in to comment.