Skip to content

Commit

Permalink
Add sort order for sinkable resources
Browse files Browse the repository at this point in the history
  • Loading branch information
karthikjeeyar committed Dec 17, 2021
1 parent 275f3d4 commit a0a75af
Show file tree
Hide file tree
Showing 13 changed files with 94 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface ResourceDropdownFieldProps extends DropdownFieldProps {
appendItems?: ResourceDropdownItems;
customResourceKey?: (key: string, resource: K8sResourceKind) => string;
dataTest?: string;
menuClassName?: string;
}

const ResourceDropdownField: React.FC<ResourceDropdownFieldProps> = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
getEventSourceResource,
} from '../../utils/create-eventsources-utils';
import { getEventSourceModels } from '../../utils/fetch-dynamic-eventsources-utils';
import { craftResourceKey } from '../pub-sub/pub-sub-utils';
import { EVENT_SOURCES_APP } from './const';
import { eventSourceValidationSchema } from './eventSource-validation-utils';
import EventSourceForm from './EventSourceForm';
Expand Down Expand Up @@ -89,7 +90,6 @@ export const EventSource: React.FC<Props> = ({
const [sinkGroupVersionKind = '', sinkName = ''] = contextSource?.split('/') ?? [];
const [sinkGroup = '', sinkVersion = '', sinkKind = ''] =
getGroupVersionKind(sinkGroupVersionKind) ?? [];
const sinkKey = sinkName && sinkKind ? `${sinkKind}-${sinkName}` : '';
const sinkApiVersion = sinkGroup ? `${sinkGroup}/${sinkVersion}` : '';

const eventSourceMetaDescription = (
Expand All @@ -114,7 +114,7 @@ export const EventSource: React.FC<Props> = ({
apiVersion: sinkApiVersion,
kind: sinkKind,
name: sinkName,
key: sinkKey,
key: craftResourceKey(sinkName, { kind: sinkKind, apiVersion: sinkGroupVersionKind }),
uri: '',
},
type: sourceKind,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.max-height-menu {
max-height: 30vh;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useFormikContext, FormikValues, useField } from 'formik';
import * as fuzzy from 'fuzzysearch';
import { isEmpty } from 'lodash';
import { useTranslation } from 'react-i18next';
import { K8sResourceKind, referenceFor } from '@console/internal/module/k8s';
import { K8sResourceKind } from '@console/internal/module/k8s';
import { getFieldId, ResourceDropdownField } from '@console/shared';
import { EventingBrokerModel, EventingChannelModel } from '../../../../models';
import { getDynamicChannelResourceList } from '../../../../utils/fetch-dynamic-eventsources-utils';
Expand All @@ -13,8 +13,11 @@ import {
knativeEventingResourcesBroker,
k8sServices,
} from '../../../../utils/get-knative-resources';
import { craftResourceKey } from '../../../pub-sub/pub-sub-utils';
import { SinkType } from '../../import-types';

import './SinkResources.scss';

export interface SinkResourcesProps {
namespace: string;
isMoveSink?: boolean;
Expand All @@ -27,14 +30,15 @@ const SinkResources: React.FC<SinkResourcesProps> = ({ namespace, isMoveSink })
FormikValues
>();
const [, { touched: sinkTypeTouched }] = useField('formData.sinkType');
const autocompleteFilter = (strText, item): boolean => fuzzy(strText, item?.props?.name);
const autocompleteFilter = (strText: string, item: React.ReactElement): boolean =>
fuzzy(strText, item?.props?.name);
const fieldId = getFieldId('sink-name', 'dropdown');
const onChange = React.useCallback(
(selectedValue, valueObj) => {
const modelData = valueObj?.props?.model;
const name = valueObj?.props?.name;
if (name && modelData) {
const { apiGroup, apiVersion, kind } = modelData;
const { apiGroup = 'core', apiVersion, kind } = modelData;
setFieldValue('formData.sink.name', name);
setFieldTouched('formData.sink.name', true);
setFieldValue('formData.sink.apiVersion', `${apiGroup}/${apiVersion}`);
Expand Down Expand Up @@ -98,6 +102,7 @@ const SinkResources: React.FC<SinkResourcesProps> = ({ namespace, isMoveSink })
</>
)}
<ResourceDropdownField
menuClassName={'max-height-menu'}
data-test="sinkable-resources"
name="formData.sink.key"
resources={resourcesData}
Expand All @@ -109,9 +114,7 @@ const SinkResources: React.FC<SinkResourcesProps> = ({ namespace, isMoveSink })
onChange={onChange}
autocompleteFilter={autocompleteFilter}
autoSelect
customResourceKey={(key: string, resource: K8sResourceKind) =>
key ? `${referenceFor(resource)}-${key}` : undefined
}
customResourceKey={craftResourceKey}
resourceFilter={resourceFilter}
onLoad={handleOnLoad}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,63 @@
import {
EventBrokerObj,
EventIMCObj,
knativeServiceObj,
sampleDeploymentsCamelConnector,
sampleServices,
} from '../../../topology/__tests__/topology-knative-test-data';
import { RESOURCE_KEY_SEPERATOR, craftResourceKey, getResourceNameFromKey } from '../pub-sub-utils';
import {
RESOURCE_KEY_SEPERATOR,
craftResourceKey,
getResourceNameFromKey,
getSinkableResourceOrder,
} from '../pub-sub-utils';

describe('pub-sub-utils', () => {
const service = sampleServices.data[0];
const service = { ...sampleServices.data[0], apiVersion: 'core/v1' };

describe('craftResourceKey', () => {
it('should return the name if the resource is not having kind and apiversion', () => {
expect(craftResourceKey('test', { metadata: { name: '' } })).toBe(
`${RESOURCE_KEY_SEPERATOR}test`,
);
it('should return undefined if the resource is not having kind or apiversion', () => {
expect(craftResourceKey('test', { metadata: { name: 'test' } })).toBeUndefined();
});

it('should return undefined if the name is not passed', () => {
expect(craftResourceKey('', { metadata: { name: '' } })).toBeUndefined();
expect(craftResourceKey('', { ...service, metadata: { name: '' } })).toBeUndefined();
});

it('should return a valid resource key', () => {
expect(craftResourceKey('nodejs-ex', service)).toBe('core~v1~Service#nodejs-ex');
expect(craftResourceKey('nodejs-ex', service)).toBe('4#core~v1~Service#nodejs-ex');
});

it('should differentiate k8s service and knative service', () => {
expect(craftResourceKey('nodejs-ex', service)).toBe('core~v1~Service#nodejs-ex');
expect(craftResourceKey('nodejs-ex', service)).toBe('4#core~v1~Service#nodejs-ex');
expect(craftResourceKey('nodejs-ex', knativeServiceObj)).toBe(
'serving.knative.dev~v1~Service#nodejs-ex',
'1#serving.knative.dev~v1~Service#nodejs-ex',
);
});
});

describe('getSinkableResourceOrder', () => {
it('sort order should 1 if knative service is passed', () => {
expect(getSinkableResourceOrder(knativeServiceObj.apiVersion)).toBe(1);
});

it('sort order should 2 if channel is passed', async () => {
expect(getSinkableResourceOrder(EventIMCObj.apiVersion)).toBe(2);
});

it('sort order should 3 if broker is passed', () => {
expect(getSinkableResourceOrder(EventBrokerObj.apiVersion)).toBe(3);
});

it('sort order should 4 if k8s service is passed', () => {
expect(getSinkableResourceOrder(service.apiVersion)).toBe(4);
});

it('sort order should 4 for any unknown or non-knative resources', () => {
expect(getSinkableResourceOrder(sampleDeploymentsCamelConnector.data[0].apiVersion)).toBe(4);
});
});

describe('getResourceNameFromKey', () => {
it('should return the name of the resource from the resource key', () => {
expect(getResourceNameFromKey(`${RESOURCE_KEY_SEPERATOR}test`)).toBe('test');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,25 @@ import { K8sResourceKind, referenceFor } from '@console/internal/module/k8s';

export const RESOURCE_KEY_SEPERATOR = '#';

export const craftResourceKey = (key: string, resource: K8sResourceKind): string | undefined =>
key ? `${referenceFor(resource)}${RESOURCE_KEY_SEPERATOR}${key}` : undefined;
export const getResourceApiGroup = (apiVersion: string): string => apiVersion.split('/')[0];

export const getSinkableResourceOrder = (apiVersion: string): number => {
const sortOrder = {
'serving.knative.dev': 1,
'messaging.knative.dev': 2,
'eventing.knative.dev': 3,
};
return sortOrder[getResourceApiGroup(apiVersion)] ?? 4;
};
export const craftResourceKey = (key: string, resource: K8sResourceKind): string | undefined => {
if (!resource?.apiVersion) return undefined;
const { apiVersion } = resource;
return key
? [getSinkableResourceOrder(apiVersion), referenceFor(resource), key].join(
RESOURCE_KEY_SEPERATOR,
)
: undefined;
};

export const getResourceNameFromKey = (key: string): string =>
key?.split(RESOURCE_KEY_SEPERATOR).pop() ?? '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Formik, FormikValues, FormikHelpers } from 'formik';
import { useTranslation } from 'react-i18next';
import { K8sResourceKind, k8sUpdate, referenceFor, modelFor } from '@console/internal/module/k8s';
import { getSinkableResources } from '../../utils/get-knative-resources';
import { craftResourceKey, sanitizeResourceName } from '../pub-sub/pub-sub-utils';
import SinkPubsubModal from './SinkPubsubModal';

export interface SinkPubsubProps {
Expand All @@ -26,17 +27,17 @@ const SinkPubsub: React.FC<SinkPubsubProps> = ({ source, resourceType, cancel, c
ref: {
apiVersion,
kind,
name: sinkName,
name: craftResourceKey(sinkName, spec?.subscriber?.ref),
},
};

const handleSubmit = (values: FormikValues, action: FormikHelpers<FormikValues>) => {
const updatePayload = {
const updatePayload = sanitizeResourceName({
...source,
...(sinkName !== values?.ref?.name && {
spec: { ...source.spec, subscriber: { ...values } },
}),
};
});
return k8sUpdate(modelFor(referenceFor(source)), updatePayload)
.then(() => {
action.setStatus({ error: '' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '@console/internal/components/factory/modal';
import { FirehoseResource } from '@console/internal/components/utils';
import { ResourceDropdownField } from '@console/shared';
import { craftResourceKey } from '../pub-sub/pub-sub-utils';

export interface SinkPubsubModalProps {
resourceName: string;
Expand Down Expand Up @@ -43,7 +44,7 @@ const SinkPubsubModal: React.FC<Props> = ({
setFieldTouched('ref.name', true);
setFieldValue('ref.name', selectedValue);
if (modelResource) {
const { apiGroup, apiVersion, kind } = modelResource;
const { apiGroup = 'core', apiVersion, kind } = modelResource;
const sinkApiversion = `${apiGroup}/${apiVersion}`;
setFieldValue('ref.apiVersion', sinkApiversion);
setFieldTouched('ref.apiVersion', true);
Expand Down Expand Up @@ -76,6 +77,7 @@ const SinkPubsubModal: React.FC<Props> = ({
placeholder={t('knative-plugin~Select a sink')}
showBadge
autocompleteFilter={autocompleteFilter}
customResourceKey={craftResourceKey}
onChange={onSinkChange}
autoSelect
selectedKey={values?.ref?.name}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ describe('SinkPubsub', () => {
it('should render Formik with proper initial values', () => {
const formikForm = pubsubForm.find(Formik);
expect(formikForm).toHaveLength(1);
expect(formikForm.get(0).props.initialValues.ref.name).toBe('overlayimage');
expect(formikForm.get(0).props.initialValues.ref.name).toBe(
'1#serving.knative.dev~v1~Service#overlayimage',
);
});

it('should render Formik child with proper props', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as yup from 'yup';
import { K8sResourceKind, k8sUpdate, referenceFor, modelFor } from '@console/internal/module/k8s';
import { sinkTypeUriValidation } from '../add/eventSource-validation-utils';
import { SinkType } from '../add/import-types';
import { craftResourceKey } from '../pub-sub/pub-sub-utils';
import SinkSourceModal from './SinkSourceModal';

export interface SinkSourceProps {
Expand All @@ -23,15 +24,14 @@ const SinkSource: React.FC<SinkSourceProps> = ({ source, cancel, close }) => {
const { name: sinkName = '', apiVersion = '', kind = '', uri = '' } = isSinkRef
? spec?.sink?.ref
: spec?.sink || {};
const sinkKey = sinkName && kind ? `${kind}-${sinkName}` : '';
const initialValues = {
formData: {
sinkType: uri ? SinkType.Uri : SinkType.Resource,
sink: {
apiVersion,
kind,
name: sinkName,
key: sinkKey,
key: craftResourceKey(sinkName, { kind, apiVersion }),
uri,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const SinkSourceModal: React.FC<Props> = ({
}) => {
const { t } = useTranslation();
const dirty =
values?.formData?.sink?.name !== initialValues.formData.sink.name ||
values?.formData?.sink?.key !== initialValues.formData.sink.key ||
values?.formData?.sink?.uri !== initialValues.formData.sink.uri;

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('SinkSourceModal Form', () => {
apiVersion: `${ServiceModel.apiGroup}/${ServiceModel.apiVersion}`,
kind: ServiceModel.kind,
name: 'event-greeter',
key: 'serving.knative.dev~event-greeter',
},
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
k8sUpdate,
kindForReference,
apiVersionForReference,
apiGroupForReference,
groupVersionFor,
} from '@console/internal/module/k8s';
import { RootState } from '@console/internal/redux';
import { getOwnedResources, OverviewItem } from '@console/shared';
Expand Down Expand Up @@ -743,6 +745,7 @@ const getEventSourcesData = (sinkUri: string, resources) => {
);
};

const getApiGroup = (apiVersion: string) => groupVersionFor(apiVersion)?.group;
/**
* Form Edge data for event sources
*/
Expand All @@ -760,7 +763,7 @@ export const getEventTopologyEdgeItems = (resource: K8sResourceKind, { data }):
if (
resName === sinkTarget.name &&
kind === sinkTarget.kind &&
apiVersion === sinkTarget.apiVersion
getApiGroup(apiVersion) === getApiGroup(sinkTarget.apiVersion)
) {
edges.push({
id: `${uid}_${resUid}`,
Expand Down Expand Up @@ -794,7 +797,8 @@ export const getTriggerTopologyEdgeItems = (broker: K8sResourceKind, resources):
});
if (
knativeService &&
connectedService.apiVersion === apiVersionForReference(referenceFor(knativeService))
getApiGroup(connectedService.apiVersion) ===
apiGroupForReference(referenceFor(knativeService))
) {
const {
metadata: { uid: serviceUid },
Expand Down Expand Up @@ -841,7 +845,7 @@ export const getSubscriptionTopologyEdgeItems = (
} = res;
if (
resName === svcData.name &&
svcData.apiVersion === apiVersionForReference(referenceFor(resource))
groupVersionFor(svcData.apiVersion).group === apiGroupForReference(referenceFor(res))
) {
edges.push({
id: `${uid}_${resUid}`,
Expand Down

0 comments on commit a0a75af

Please sign in to comment.