Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds kebab option for edit URI along with associated action modal #6074

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions frontend/packages/knative-plugin/src/actions/edit-sink-uri.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { KebabOption } from '@console/internal/components/utils';
import { K8sKind, K8sResourceKind } from '@console/internal/module/k8s';
import { editSinkUriModal } from '../components/modals';

export const editSinkUri = (
model: K8sKind,
source: K8sResourceKind,
resources: K8sResourceKind[],
): KebabOption => {
return {
label: 'Edit URI',
callback: () =>
editSinkUriModal({
source,
eventSourceList: resources,
}),
accessReview: {
group: model.apiGroup,
resource: model.plural,
name: resources[0].metadata.name,
namespace: resources[0].metadata.namespace,
verb: 'update',
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ export const deleteRevisionModal = (props) =>
import(
'../revisions/DeleteRevisionModalController' /* webpackChunkName: "delete-revision" */
).then((m) => m.deleteRevisionModalLauncher(props));

export const editSinkUriModal = (props) =>
import('../sink-uri/SinkUriController' /* webpackChunkName: "sink-uri" */).then((m) =>
m.sinkModalLauncher(props),
);
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from '../../const';
import { RevisionModel, EventingSubscriptionModel } from '../../models';
import { getRevisionActions } from '../../actions/getRevisionActions';
import { editSinkUri } from '../../actions/edit-sink-uri';
import {
isDynamicEventResourceKind,
isEventingChannelResourceKind,
Expand Down Expand Up @@ -48,7 +49,7 @@ export const KnativeResourceOverviewPage: React.ComponentType<KnativeResourceOve
kindsInFlight,
}: KnativeResourceOverviewPageProps) => {
if (NodeType.SinkUri === item?.obj?.type?.nodeType) {
return <SinkUriResourcesTab itemData={item} />;
return <SinkUriResourcesTab itemData={item} menuAction={editSinkUri} />;
}
if (kindsInFlight) {
return !knativeModels ? null : <LoadingBox />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,36 @@ import * as React from 'react';
import * as classNames from 'classnames';
import * as _ from 'lodash';
import { OverviewItem } from '@console/shared';
import { referenceFor } from '@console/internal/module/k8s';
import { referenceFor, modelFor } from '@console/internal/module/k8s';
import {
ActionsMenu,
ResourceLink,
SidebarSectionHeading,
ExternalLink,
KebabAction,
} from '@console/internal/components/utils';

export type SinkUriResourcesTabProps = {
itemData: OverviewItem;
menuAction: KebabAction;
};

const SinkUriResourcesTab: React.FC<SinkUriResourcesTabProps> = ({ itemData }) => {
const SinkUriResourcesTab: React.FC<SinkUriResourcesTabProps> = ({ itemData, menuAction }) => {
const { obj, eventSources } = itemData;
const sinkUri = obj?.spec?.sinkUri;
const actions = [];
if (eventSources.length > 0) {
const sourceModel = modelFor(referenceFor(eventSources[0]));
actions.push(menuAction(sourceModel, obj, eventSources));
}

return (
<div className="overview__sidebar-pane resource-overview">
<div className="overview__sidebar-pane-head resource-overview__heading">
<h1 className="co-m-pane__heading">
<div className="co-m-pane__name co-resource-item">URI</div>
<div className="co-actions">
<ActionsMenu actions={[]} />
<ActionsMenu actions={actions} />
</div>
</h1>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as React from 'react';
import * as _ from 'lodash';
import { Formik, FormikValues, FormikHelpers } from 'formik';
import { K8sResourceKind, k8sUpdate, referenceFor, modelFor } from '@console/internal/module/k8s';
import SinkUriModal from './SinkUriModal';

export interface SinkUriProps {
source: K8sResourceKind;
eventSourceList: K8sResourceKind[];
cancel?: () => void;
close?: () => void;
}

const SinkUri: React.FC<SinkUriProps> = ({ source, eventSourceList, cancel, close }) => {
const initialValues = {
uri: source.spec?.sinkUri ?? '',
};
const handleSubmit = (values: FormikValues, action: FormikHelpers<FormikValues>) => {
const requests: Promise<K8sResourceKind>[] = [];
_.forEach(eventSourceList, (evSrc) => {
const updatePayload = {
...evSrc,
spec: { ...evSrc.spec, sink: { ...values } },
};
requests.push(k8sUpdate(modelFor(referenceFor(evSrc)), updatePayload));
});
Promise.race(requests)
.then(() => {
action.setSubmitting(false);
action.setStatus({ error: '' });
close();
})
.catch((err) => {
action.setStatus({ error: err.message || 'An error occurred. Please try again' });
});
};

return (
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
onReset={cancel}
initialStatus={{ error: '' }}
>
{(formikProps) => <SinkUriModal {...formikProps} cancel={cancel} />}
</Formik>
);
};

export default SinkUri;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react';
import { K8sResourceKind } from '@console/internal/module/k8s';
import { createModalLauncher, ModalComponentProps } from '@console/internal/components/factory';
import SinkUri from './SinkUri';

type SinkUriControllerProps = {
source: K8sResourceKind;
eventSourceList: K8sResourceKind[];
};

const SinkUriController: React.FC<SinkUriControllerProps> = ({
source,
eventSourceList,
...props
}) => <SinkUri {...props} source={source} eventSourceList={eventSourceList} />;

type Props = SinkUriControllerProps & ModalComponentProps;

export const sinkModalLauncher = createModalLauncher<Props>(SinkUriController);

export default SinkUriController;
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as React from 'react';
import { FormikProps, FormikValues } from 'formik';
import { Form, FormGroup, TextInputTypes } from '@patternfly/react-core';
import { InputField, getFieldId } from '@console/shared';
import {
ModalTitle,
ModalBody,
ModalSubmitFooter,
} from '@console/internal/components/factory/modal';
import FormSection from '@console/dev-console/src/components/import/section/FormSection';

export interface SinkUriModalProps {
cancel?: () => void;
}

type Props = FormikProps<FormikValues> & SinkUriModalProps;

const SinkUriModal: React.FC<Props> = ({
handleSubmit,
cancel,
isSubmitting,
status,
values,
initialValues,
}) => {
const fieldId = getFieldId('sink-name', 'uri');
const dirty = values?.uri !== initialValues.uri;
return (
<Form onSubmit={handleSubmit}>
<div className="modal-content modal-content--no-inner-scroll">
<ModalTitle>Edit URI</ModalTitle>
<ModalBody>
<FormSection fullWidth>
<FormGroup
fieldId={fieldId}
helperText="Editing this URI will affect all associated Event Sources."
isRequired
>
<InputField
type={TextInputTypes.text}
name="uri"
placeholder="Enter URI"
data-test-id="edit-sink-uri"
required
/>
</FormGroup>
</FormSection>
</ModalBody>
<ModalSubmitFooter
inProgress={isSubmitting}
submitText="Save"
submitDisabled={!dirty}
cancel={cancel}
errorMessage={status.error}
/>
</div>
</Form>
);
};

export default SinkUriModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { Formik } from 'formik';
import SinkUri from '../SinkUri';
import { sampleEventSourceSinkbinding } from '../../../topology/__tests__/topology-knative-test-data';

type SinkUriProps = React.ComponentProps<typeof SinkUri>;

describe('SinkUri', () => {
const sinkUriObj = {
metadata: { uid: 'http%3A%2F%2Fsvc.cluster.com', namespace: 'jai-test' },
spec: { sinkUri: 'http://svc.cluster.com' },
type: { nodeType: 'sink-uri' },
};
const formProps: SinkUriProps = {
source: sinkUriObj,
eventSourceList: sampleEventSourceSinkbinding.data,
};
const edituriForm: ShallowWrapper<SinkUriProps> = shallow(<SinkUri {...formProps} />);

it('should render Formik with proper initial values', () => {
const formikForm = edituriForm.find(Formik);
expect(formikForm).toHaveLength(1);
expect(formikForm.get(0).props.initialValues.uri).toBe('http://svc.cluster.com');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import {
ModalTitle,
ModalBody,
ModalSubmitFooter,
} from '@console/internal/components/factory/modal';
import { InputField } from '@console/shared';
import { formikFormProps } from '@console/shared/src/test-utils/formik-props-utils';
import SinkUriModal from '../SinkUriModal';

type SinkUriModalProps = React.ComponentProps<typeof SinkUriModal>;

describe('SinkUriModal Form', () => {
let formProps: SinkUriModalProps;
let SinkUriModalWrapper: ShallowWrapper<SinkUriModalProps>;
const formValues = {
uri: 'http://svc.cluster.com',
};
beforeEach(() => {
formProps = {
...formikFormProps,
values: formValues,
initialValues: formValues,
};
SinkUriModalWrapper = shallow(<SinkUriModal {...formProps} />);
});

it('should render ModalTitle, body and footer', () => {
expect(SinkUriModalWrapper.find(ModalTitle)).toHaveLength(1);
expect(SinkUriModalWrapper.find(ModalBody)).toHaveLength(1);
expect(SinkUriModalWrapper.find(ModalSubmitFooter)).toHaveLength(1);
});

it('should render InputField for uri', () => {
const inputField = SinkUriModalWrapper.find(InputField);
expect(inputField).toHaveLength(1);
expect(inputField.get(0).props.name).toBe('uri');
});

it('should call handleSubmit on form submit', () => {
SinkUriModalWrapper.simulate('submit');
expect(formProps.handleSubmit).toHaveBeenCalled();
});

it('Save should be disabled if value is not changed', () => {
const modalSubmitFooter = SinkUriModalWrapper.find(ModalSubmitFooter);
expect(modalSubmitFooter.get(0).props.submitDisabled).toBe(true);
});

it('Save should be enabled if value is changed', () => {
const sinkValues = {
uri: 'http://svc.cluster12.com',
};
formProps = {
...formProps,
values: {
...formProps.values,
...sinkValues,
},
};
SinkUriModalWrapper = shallow(<SinkUriModal {...formProps} />);
const modalSubmitFooter = SinkUriModalWrapper.find(ModalSubmitFooter);
expect(modalSubmitFooter.get(0).props.submitDisabled).toBe(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { Kebab, kebabOptionsToMenu } from '@console/internal/components/utils';
import { modelFor, referenceFor } from '@console/internal/module/k8s';
import { RevisionModel } from '../../models';
import { getRevisionActions } from '../../actions/getRevisionActions';
import { editSinkUri } from '../../actions/edit-sink-uri';
import {
TYPE_EVENT_SOURCE,
TYPE_EVENT_SOURCE_LINK,
Expand Down Expand Up @@ -76,6 +77,17 @@ export const knativeContextMenu = (element: Node) => {
return createMenuItems(kebabOptionsToMenu(kebabOptions));
};

export const editUriContextMenu = (element: Node) => {
const item = element.getData();
const actions = [];
const { obj, eventSources } = item.resources;
if (eventSources.length > 0) {
const sourceModel = modelFor(referenceFor(eventSources[0]));
actions.push(editSinkUri(sourceModel, obj, eventSources));
}
return createMenuItems(kebabOptionsToMenu(actions));
};

const dragOperation: EditableDragOperationType = {
type: CREATE_PUB_SUB_CONNECTOR_OPERATION,
edit: true,
Expand Down Expand Up @@ -131,7 +143,9 @@ export const getKnativeComponentFactory = (): ComponentFactory => {
case TYPE_SINK_URI:
return withDragNode(nodeDragSourceSpec(type))(
withSelection({ controlled: true })(
withDndDrop<any, any, {}, NodeComponentProps>(sinkUriDropTargetSpec)(SinkUriNode),
withContextMenu(editUriContextMenu)(
withDndDrop<any, any, {}, NodeComponentProps>(sinkUriDropTargetSpec)(SinkUriNode),
),
),
);
case TYPE_KNATIVE_REVISION:
Expand Down