Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ const BaseNodeComponent: FC<BaseNodeProps> = ({
const cx = width / 2;
const cy = height / 2;
const resourceObj = getTopologyResourceObject(element.getData());
const resourceModel = modelFor(referenceFor(resourceObj));
const resourceModel = resourceObj ? modelFor(referenceFor(resourceObj)) : undefined;
const iconRadius = innerRadius * 0.9;
const editAccess = useAccessReview({
group: resourceModel?.apiGroup,
verb: createConnectorAccessVerb,
resource: resourceModel?.plural,
name: resourceObj.metadata.name,
namespace: resourceObj.metadata.namespace,
name: resourceObj?.metadata?.name,
namespace: resourceObj?.metadata?.namespace,
});
const [filtered] = useSearchFilter(element.getLabel(), resourceObj?.metadata?.labels);
const showLabel = useShowLabel(isHovering);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const BindableNode: FC<BindableNodeProps> = ({
const iconRadius = Math.min(width, height) * 0.25;
const [dndDropProps, dndDropRef] = useDndDrop(spec, { element: nodeElement, ...rest });
const resourceObj = getTopologyResourceObject(element.getData());
const resourceModel = modelFor(referenceFor(resourceObj));
const resourceModel = resourceObj ? modelFor(referenceFor(resourceObj)) : undefined;
const iconData = element.getData()?.data?.icon || openshiftImg;
const kind = resourceModel && referenceForModel(resourceModel);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@ const WorkloadNode: FC<WorkloadNodeProps> = observer(({ element, ...rest }) => {
const resource = getTopologyResourceObject(element.getData());
const { podData, loadError, loaded } = usePodsWatcher(
resource,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resource is of type K8sResourceKind but it's potentially null

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right about the type gap. The GetTopologyResourceObject SDK type declares the return as K8sResourceKind, but the implementation can return null. Since updating the SDK type would be a breaking API change, we handle nullability at each call site with optional chaining (resource?.kind, resource?.metadata?.namespace). The usePodsWatcher hook also uses optional chaining internally, so null is handled gracefully at runtime — it skips watching when kind is undefined.

resource.kind,
resource.metadata.namespace,
resource?.kind,
resource?.metadata?.namespace,
);
return (
<WorkloadPodsNode
Expand Down
2 changes: 1 addition & 1 deletion frontend/packages/topology/src/elements/OdcBaseNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class OdcBaseNode extends BaseNode implements OdcBaseNodeInterface {
}

getResourceKind(): K8sResourceKindReference | undefined {
return this.resourceKind || referenceFor(this.resource);
return this.resourceKind || (this.resource ? referenceFor(this.resource) : undefined);
}

setResourceKind(kind: K8sResourceKindReference | undefined): void {
Expand Down
11 changes: 7 additions & 4 deletions frontend/packages/topology/src/utils/topology-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,13 @@ export const getResource: GetResource = <T = K8sResourceKind>(node: GraphElement
return (resource as T) || (getTopologyResourceObject(node?.getData()) as T);
};

export const getResourceKind = (node: Node): K8sResourceKindReference =>
node instanceof OdcBaseNode
? (node as OdcBaseNode).getResourceKind()
: referenceFor(getTopologyResourceObject(node?.getData()));
export const getResourceKind = (node: Node): K8sResourceKindReference | undefined => {
if (node instanceof OdcBaseNode) {
return (node as OdcBaseNode).getResourceKind();
}
const resource = getTopologyResourceObject(node?.getData());
return resource ? referenceFor(resource) : undefined;
};

export const updateTopologyResourceApplication = (
item: Node,
Expand Down
12 changes: 6 additions & 6 deletions frontend/packages/topology/src/utils/withEditReviewAccess.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import { getResource } from './topology-utils';
export const withEditReviewAccess: WithEditReviewAccess = (verb) => (WrappedComponent) => {
const Component: FC<WithEditReviewAccessComponentProps> = (props) => {
const resourceObj = getResource(props.element);
const resourceModel = modelFor(referenceFor(resourceObj));
const resourceModel = resourceObj ? modelFor(referenceFor(resourceObj)) : undefined;
const editAccess = useAccessReview({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: with resourceModel being undefined, the RBAC request will fail, defaulting to setAllowed(true). So canEdit will be true for null resources. The server still enforces real access, but showing edit controls on unresolvable nodes seems wrong

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great observation — fixed in 7e9ae24. When resourceObj is falsy, canEdit now defaults to false instead of relying on the RBAC check with undefined parameters. This prevents edit controls from appearing on unresolvable nodes.

group: resourceModel.apiGroup,
group: resourceModel?.apiGroup,
verb,
resource: resourceModel.plural,
name: resourceObj.metadata.name,
namespace: resourceObj.metadata.namespace,
resource: resourceModel?.plural,
name: resourceObj?.metadata?.name,
namespace: resourceObj?.metadata?.namespace,
});
return <WrappedComponent {...(props as any)} canEdit={editAccess} />;
return <WrappedComponent {...(props as any)} canEdit={resourceObj ? editAccess : false} />;
};
Component.displayName = `withEditReviewAccess(${
WrappedComponent.displayName || WrappedComponent.name
Expand Down