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 datamodel for channel and update visualisation #5803

Merged
merged 2 commits into from Jun 26, 2020
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
Expand Up @@ -38,6 +38,8 @@ import { RootState } from '@console/internal/redux';
import { getActiveApplication } from '@console/internal/reducers/ui';
import { selectOverviewDetailsTab } from '@console/internal/actions/ui';
import { getEventSourceStatus } from '@console/knative-plugin/src/topology/knative-topology-utils';
import { TYPE_EVENT_PUB_SUB_LINK } from '@console/knative-plugin/src/topology/const';
import KnativeResourceOverviewPage from '@console/knative-plugin/src/components/overview/KnativeResourceOverviewPage';
import {
getQueryArgument,
setQueryArgument,
Expand Down Expand Up @@ -447,6 +449,10 @@ const Topology: React.FC<ComponentProps> = ({
}

if (isEdge(selectedEntity)) {
if (selectedEntity.getType() === TYPE_EVENT_PUB_SUB_LINK) {
const itemResources = selectedEntity.getData();
return <KnativeResourceOverviewPage item={itemResources.resources} />;
}
return <ConnectedTopologyEdgePanel edge={selectedEntity as BaseEdge} model={filteredModel} />;
}
return null;
Expand Down
Expand Up @@ -7,6 +7,7 @@ import {
TYPE_EVENT_SOURCE_LINK,
TYPE_KNATIVE_REVISION,
TYPE_KNATIVE_SERVICE,
TYPE_EVENT_PUB_SUB,
TYPE_REVISION_TRAFFIC,
} from '@console/knative-plugin/src/topology/const';
import { getTopologyResourceObject } from '../topology-utils';
Expand Down Expand Up @@ -68,7 +69,7 @@ export const edgeActions = (edge: Edge, nodes: Node[]): KebabOption[] => {
case TYPE_SERVICE_BINDING:
return false;
case TYPE_EVENT_SOURCE_LINK:
return n.getType() === TYPE_KNATIVE_SERVICE;
return n.getType() === TYPE_KNATIVE_SERVICE || n.getType() === TYPE_EVENT_PUB_SUB;
case TYPE_REVISION_TRAFFIC:
return false;
case TYPE_TRAFFIC_CONNECTOR:
Expand Down
4 changes: 3 additions & 1 deletion frontend/packages/knative-plugin/src/actions/sink-source.ts
@@ -1,10 +1,12 @@
import { KebabOption } from '@console/internal/components/utils';
import { K8sKind, K8sResourceKind } from '@console/internal/module/k8s';
import { setSinkSourceModal } from '../components/modals';
import { EventingSubscriptionModel } from '../models';

export const setSinkSource = (model: K8sKind, source: K8sResourceKind): KebabOption => {
const label = model.kind === EventingSubscriptionModel.kind ? `Move ${model.kind}` : 'Move Sink';
return {
label: 'Move Sink',
label,
callback: () =>
setSinkSourceModal({
source,
Expand Down
@@ -0,0 +1,68 @@
import * as React from 'react';
import * as _ from 'lodash';
import { OverviewItem } from '@console/shared';
import { referenceFor, K8sResourceKind } from '@console/internal/module/k8s';
import { ResourceLink, SidebarSectionHeading } from '@console/internal/components/utils';
import { EventingSubscriptionModel } from '../../models';

type PubSubResourceOverviewListProps = {
items: K8sResourceKind[];
title: string;
};

type EventPubSubResourcesProps = {
item: OverviewItem & {
eventSources?: K8sResourceKind[];
eventingsubscription?: K8sResourceKind[];
connections?: K8sResourceKind[];
};
};

export const PubSubResourceOverviewList: React.FC<PubSubResourceOverviewListProps> = ({
items,
title,
}) => (
<>
<SidebarSectionHeading text={title} />
{items?.length > 0 ? (
<ul className="list-group">
{_.map(items, (itemData) => (
<li className="list-group-item" key={itemData.metadata.uid}>
<ResourceLink
kind={referenceFor(itemData)}
name={itemData.metadata?.name}
namespace={itemData.metadata?.namespace}
/>
</li>
))}
</ul>
) : (
<span className="text-muted">No {title} found for this resource.</span>
)}
</>
);

const EventPubSubResources: React.FC<EventPubSubResourcesProps> = ({ item }) => {
const {
obj,
ksservices = [],
eventSources = [],
eventingsubscription = [],
connections = [],
} = item;

switch (obj.kind) {
case EventingSubscriptionModel.kind:
return <PubSubResourceOverviewList items={connections} title="Connections" />;
default:
return (
<>
<PubSubResourceOverviewList items={ksservices} title="Knative Service" />
<PubSubResourceOverviewList items={eventSources} title="Event Source" />
<PubSubResourceOverviewList items={eventingsubscription} title="Subscription" />
</>
);
}
};

export default EventPubSubResources;
@@ -1,6 +1,11 @@
import * as React from 'react';
import * as _ from 'lodash';
import { K8sResourceKind, referenceForModel, PodKind } from '@console/internal/module/k8s';
import {
K8sResourceKind,
referenceForGroupVersionKind,
groupVersionFor,
PodKind,
} from '@console/internal/module/k8s';
import {
ResourceLink,
ExternalLink,
Expand All @@ -9,7 +14,6 @@ import {
import { PodControllerOverviewItem } from '@console/shared';
import { PodModel } from '@console/internal/models';
import { PodsOverview } from '@console/internal/components/overview/pods-overview';
import { ServiceModel } from '../../models';

export type EventSinkServicesOverviewListProps = {
obj: K8sResourceKind;
Expand All @@ -23,26 +27,30 @@ const EventSinkServicesOverviewList: React.FC<EventSinkServicesOverviewListProps
current,
}) => {
const {
kind: resKind,
kind,
apiVersion,
metadata: { name, namespace },
spec,
} = obj;
const sink = _.get(obj, 'spec.sink.ref') || _.get(obj, 'spec.sink');
const { name: sinkName, kind: sinkKind, apiVersion: sinkApiversion } =
spec?.sink?.ref || spec?.sink || {};
const sinkUri = obj?.status?.sinkUri;
const deploymentData = current?.obj?.metadata?.ownerReferences?.[0];
const apiGroup = apiVersion.split('/')[0];
const linkUrl = `/search/ns/${namespace}?kind=${PodModel.kind}&q=${encodeURIComponent(
`${apiGroup}/${_.lowerFirst(resKind)}=${name}`,
`${apiGroup}/${_.lowerFirst(kind)}=${name}`,
)}`;
const { group, version } = (sinkApiversion && groupVersionFor(sinkApiversion)) || {};
const isSinkReference = !!(sinkKind && sinkName && group && version);
return (
<>
<SidebarSectionHeading text="Knative Services" />
{sink && sink.kind === ServiceModel.kind ? (
<SidebarSectionHeading text="Sink" />
{isSinkReference ? (
<ul className="list-group">
<li className="list-group-item">
<ResourceLink
kind={referenceForModel(ServiceModel)}
name={sink.name}
kind={referenceForGroupVersionKind(group)(version)(sinkKind)}
name={sinkName}
namespace={namespace}
/>
{sinkUri && (
Expand All @@ -58,7 +66,7 @@ const EventSinkServicesOverviewList: React.FC<EventSinkServicesOverviewListProps
</li>
</ul>
) : (
<span className="text-muted">No services found for this resource.</span>
<span className="text-muted">No sink found for this resource.</span>
)}
{pods?.length > 0 && <PodsOverview pods={pods} obj={obj} allPodsLink={linkUrl} />}
{deploymentData?.name && (
Expand Down
Expand Up @@ -6,10 +6,13 @@ import { groupVersionFor, K8sKind, referenceForModel } from '@console/internal/m
import { RootState } from '@console/internal/redux';
import { OverviewItem } from '@console/shared';
import { ModifyApplication } from '@console/dev-console/src/actions/modify-application';
import { KNATIVE_SERVING_APIGROUP } from '../../const';
import { RevisionModel } from '../../models';
import { KNATIVE_SERVING_APIGROUP, KNATIVE_EVENT_MESSAGE_APIGROUP } from '../../const';
import { RevisionModel, EventingSubscriptionModel } from '../../models';
import { getRevisionActions } from '../../actions/getRevisionActions';
import { isDynamicEventResourceKind } from '../../utils/fetch-dynamic-eventsources-utils';
import {
isDynamicEventResourceKind,
isEventingChannelResourceKind,
} from '../../utils/fetch-dynamic-eventsources-utils';
import OverviewDetailsKnativeResourcesTab from './OverviewDetailsKnativeResourcesTab';
import KnativeOverview from './KnativeOverview';

Expand Down Expand Up @@ -52,6 +55,8 @@ export const KnativeResourceOverviewPage: React.ComponentType<KnativeResourceOve
const actions = [];
if (resourceModel.kind === RevisionModel.kind) {
actions.push(...getRevisionActions());
} else if (resourceModel.kind === EventingSubscriptionModel.kind) {
actions.push(...Kebab.getExtensionsActionsForKind(resourceModel), ...Kebab.factory.common);
} else {
actions.push(
ModifyApplication,
Expand All @@ -78,7 +83,9 @@ const mapStateToProps = (state: RootState): StateProps => {
.filter(
(model: K8sKind) =>
model.apiGroup === KNATIVE_SERVING_APIGROUP ||
isDynamicEventResourceKind(referenceForModel(model)),
model.apiGroup === KNATIVE_EVENT_MESSAGE_APIGROUP ||
isDynamicEventResourceKind(referenceForModel(model)) ||
isEventingChannelResourceKind(referenceForModel(model)),
),
};
};
Expand Down
Expand Up @@ -2,14 +2,18 @@ import * as React from 'react';
import { OverviewItem } from '@console/shared';
import OperatorBackedOwnerReferences from '@console/internal/components/utils';
import { referenceFor } from '@console/internal/module/k8s';
import { RevisionModel, ServiceModel } from '../../models';
import { RevisionModel, ServiceModel, EventingSubscriptionModel } from '../../models';
import KnativeServiceResources from './KnativeServiceResources';
import KnativeRevisionResources from './KnativeRevisionResources';
import RevisionsOverviewList from './RevisionsOverviewList';
import KSRoutesOverviewList from './RoutesOverviewList';
import ConfigurationsOverviewList from './ConfigurationsOverviewList';
import EventSinkServicesOverviewList from './EventSinkServicesOverviewList';
import { isDynamicEventResourceKind } from '../../utils/fetch-dynamic-eventsources-utils';
import {
isDynamicEventResourceKind,
isEventingChannelResourceKind,
} from '../../utils/fetch-dynamic-eventsources-utils';
import EventPubSubResources from './EventPubSubResources';

type OverviewDetailsResourcesTabProps = {
item: OverviewItem;
Expand All @@ -20,6 +24,9 @@ const getSidebarResources = (item: OverviewItem) => {
if (isDynamicEventResourceKind(referenceFor(obj))) {
return <EventSinkServicesOverviewList obj={obj} pods={pods} current={current} />;
}
if (isEventingChannelResourceKind(referenceFor(obj))) {
return <EventPubSubResources item={item} />;
}
switch (obj.kind) {
case RevisionModel.kind:
return (
Expand All @@ -33,6 +40,8 @@ const getSidebarResources = (item: OverviewItem) => {
);
case ServiceModel.kind:
return <KnativeServiceResources item={item} />;
case EventingSubscriptionModel.kind:
return <EventPubSubResources item={item} />;
default:
return (
<>
Expand Down
@@ -0,0 +1,75 @@
import * as React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { OverviewItem } from '@console/shared';
import { K8sResourceKind } from '@console/internal/module/k8s';
import { ResourceLink, SidebarSectionHeading } from '@console/internal/components/utils';
import {
EventSubscriptionObj,
EventIMCObj,
knativeServiceObj,
} from '../../../topology/__tests__/topology-knative-test-data';
import EventPubSubResources, { PubSubResourceOverviewList } from '../EventPubSubResources';

type EventPubSubResourcesProps = React.ComponentProps<typeof EventPubSubResources>;
type PubSubResourceOverviewListProps = React.ComponentProps<typeof PubSubResourceOverviewList>;
let itemData: OverviewItem & {
eventSources?: K8sResourceKind[];
eventingsubscription?: K8sResourceKind[];
connections?: K8sResourceKind[];
};

describe('EventPubSubResources', () => {
let wrapper: ShallowWrapper<EventPubSubResourcesProps>;
itemData = {
obj: EventSubscriptionObj,
buildConfigs: [],
eventSources: [],
routes: [],
services: [],
connections: [EventIMCObj, knativeServiceObj],
};

it('should render PubSubResourceOverviewList once if obj of kind Subscription', () => {
wrapper = shallow(<EventPubSubResources item={itemData} />);
const findPubSubList = wrapper.find(PubSubResourceOverviewList);
expect(findPubSubList).toHaveLength(1);
expect(findPubSubList.at(0).props().title).toEqual('Connections');
});

it('should render PubSubResourceOverviewList thrice if obj not of kind Subscription', () => {
const channelItemData = {
...itemData,
obj: EventIMCObj,
ksservices: [knativeServiceObj],
eventingsubscription: [EventSubscriptionObj],
};
wrapper = shallow(<EventPubSubResources item={channelItemData} />);
const findPubSubList = wrapper.find(PubSubResourceOverviewList);
expect(findPubSubList).toHaveLength(3);
expect(findPubSubList.at(0).props().title).toEqual('Knative Service');
expect(findPubSubList.at(1).props().title).toEqual('Event Source');
expect(findPubSubList.at(2).props().title).toEqual('Subscription');
});
});

describe('PubSubResourceOverviewList', () => {
let wrapper: ShallowWrapper<PubSubResourceOverviewListProps>;
const itemsData: K8sResourceKind[] = [EventIMCObj, knativeServiceObj];

it('should render ResourceLink respective for each resources, SidebarSectionHeading and no span', () => {
wrapper = shallow(<PubSubResourceOverviewList items={itemsData} title="Connections" />);
const findPubSubList = wrapper.find(ResourceLink);
expect(findPubSubList).toHaveLength(2);
expect(findPubSubList.at(0).props().name).toEqual('testchannel');
expect(wrapper.find(SidebarSectionHeading)).toHaveLength(1);
expect(wrapper.find('.text-muted').exists()).toBe(false);
});

it('should render SidebarSectionHeading, text-muted and not ResourceLink if no resources exists', () => {
wrapper = shallow(<PubSubResourceOverviewList items={[]} title="Connections" />);
const findTextMutedList = wrapper.find('.text-muted');
expect(wrapper.find(ResourceLink).exists()).toBe(false);
expect(wrapper.find(SidebarSectionHeading)).toHaveLength(1);
expect(findTextMutedList.exists()).toBe(true);
});
});
Expand Up @@ -10,7 +10,12 @@ import {
SidebarSectionHeading,
} from '@console/internal/components/utils';
import { getEventSourceResponse } from '../../../topology/__tests__/topology-knative-test-data';
import { ServiceModel, EventSourceApiServerModel, EventSourceCamelModel } from '../../../models';
import {
ServiceModel,
EventSourceApiServerModel,
EventSourceCamelModel,
EventingIMCModel,
} from '../../../models';
import EventSinkServicesOverviewList from '../EventSinkServicesOverviewList';

describe('EventSinkServicesOverviewList', () => {
Expand Down Expand Up @@ -68,10 +73,10 @@ describe('EventSinkServicesOverviewList', () => {
'spec',
);
const wrapper = shallow(<EventSinkServicesOverviewList obj={mockData} />);
expect(wrapper.find('span').text()).toBe('No services found for this resource.');
expect(wrapper.find('span').text()).toBe('No sink found for this resource.');
});

it('should have ResourceLink with proper kind', () => {
it('should have ResourceLink with proper kind for sink to knSvc', () => {
const wrapper = shallow(
<EventSinkServicesOverviewList
obj={getEventSourceResponse(EventSourceApiServerModel).data[0]}
Expand All @@ -82,6 +87,24 @@ describe('EventSinkServicesOverviewList', () => {
expect(findResourceLink.at(0).props().kind).toEqual(referenceForModel(ServiceModel));
});

it('should have ResourceLink with proper kind for sink to channel', () => {
const sinkData = {
sink: {
apiVersion: `${EventingIMCModel.apiGroup}/${EventingIMCModel.apiVersion}`,
kind: EventingIMCModel.kind,
name: 'testchannel',
},
};
const sinkChannelData = {
...getEventSourceResponse(EventSourceApiServerModel).data[0],
...{ spec: sinkData },
};
const wrapper = shallow(<EventSinkServicesOverviewList obj={sinkChannelData} />);
const findResourceLink = wrapper.find(ResourceLink);
expect(findResourceLink).toHaveLength(1);
expect(findResourceLink.at(0).props().kind).toEqual(referenceForModel(EventingIMCModel));
});

it('should have ExternaLink when sinkUri is present', () => {
const wrapper = shallow(
<EventSinkServicesOverviewList
Expand Down