Skip to content
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.
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
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Changed
---

New fields in the NodeBalancer details object and NodeBalancerVPC object to align with recent API updates ([#13394](https://github.com/linode/manager/pull/13394))
5 changes: 4 additions & 1 deletion packages/api-v4/src/nodebalancers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type UDPStickiness = 'none' | 'session' | 'source_ip';

export type Stickiness = TCPStickiness | UDPStickiness;

type NodeBalancerType = 'common' | 'premium';
type NodeBalancerType = 'common' | 'premium' | 'premium_40GB';

export interface LKEClusterInfo {
id: number;
Expand All @@ -33,6 +33,8 @@ export interface NodeBalancer {
*/
client_udp_sess_throttle?: number;
created: string;
frontend_address_type: 'public' | 'vpc';
frontend_vpc_subnet_id: null | number;
hostname: string;
id: number;
ipv4: string;
Expand Down Expand Up @@ -145,6 +147,7 @@ export interface NodeBalancerVpcConfig {
ipv4_range: null | string;
ipv6_range: null | string;
nodebalancer_id: number;
purpose: 'backend' | 'frontend';
subnet_id: number;
vpc_id: number;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Changed
---

Display front end IP and backend VPCs for Nodebalancer ([#13394](https://github.com/linode/manager/pull/13394))
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
nodeBalancerConfigFactory,
nodeBalancerConfigVPCFactory,
nodeBalancerFactory,
nodeBalancerVPCFactory,
} from '@linode/utilities';
import { waitFor } from '@testing-library/react';
import * as React from 'react';
Expand All @@ -13,12 +13,10 @@
import { SummaryPanel } from './SummaryPanel';

const queryMocks = vi.hoisted(() => ({
useAllNodeBalancerConfigsQuery: vi.fn().mockReturnValue({ data: undefined }),
useNodeBalancerQuery: vi.fn().mockReturnValue({ data: undefined }),
useNodeBalancersFirewallsQuery: vi.fn().mockReturnValue({ data: undefined }),
useNodeBalancerVPCConfigsBetaQuery: vi
.fn()
.mockReturnValue({ data: undefined }),
useAllNodeBalancerConfigsQuery: vi.fn().mockReturnValue({ data: null }),
useNodeBalancerQuery: vi.fn().mockReturnValue({ data: null }),
useNodeBalancersFirewallsQuery: vi.fn().mockReturnValue({ data: null }),
useNodeBalancerVPCConfigsBetaQuery: vi.fn().mockReturnValue({ data: null }),
useParams: vi.fn().mockReturnValue({}),
userPermissions: vi.fn(() => ({
data: {
Expand Down Expand Up @@ -51,8 +49,8 @@
};
});

const nodeBalancerDetails = 'NodeBalancer Details';
const nbVpcConfig = nodeBalancerConfigVPCFactory.build();
const nodeBalancerDetails = 'Details';
const nbVpcConfig = nodeBalancerVPCFactory.build();

describe('SummaryPanel', () => {
beforeEach(() => {
Expand All @@ -79,7 +77,7 @@

it('does not render anything if there is no nodebalancer', () => {
queryMocks.useAllNodeBalancerConfigsQuery.mockReturnValue({
data: undefined,
data: null,
});
const { queryByText } = renderWithTheme(<SummaryPanel />);

Expand All @@ -88,7 +86,7 @@

it('does not render anything if there are no configs', () => {
queryMocks.useNodeBalancerQuery.mockReturnValue({
data: undefined,
data: null,
});
const { queryByText } = renderWithTheme(<SummaryPanel />);

Expand All @@ -104,14 +102,14 @@
expect(getByText(nodeBalancerDetails)).toBeVisible();
expect(getByText('Ports:')).toBeVisible();
expect(getByText('Backend Status:')).toBeVisible();
expect(getByText('0 up, 2 down'));
expect(getByText('0 up, 2 down')).toBeVisible();
expect(getByText('Transferred:')).toBeVisible();
expect(getByText('0 bytes')).toBeVisible();
expect(getByText('Host Name:')).toBeVisible();
expect(getByText('example.com')).toBeVisible();
expect(getByText('Region:')).toBeVisible();
// Type should not display for non-premium NBs
expect(queryByText('Type:')).not.toBeInTheDocument();
// Type should be visible and default to Basic since the NB is not premium
expect(getByText('Basic')).toBeVisible();
// Cluster should not display for if the NB is not associated with LKE or LKE-E
expect(queryByText('Cluster:')).not.toBeInTheDocument();

Expand All @@ -120,11 +118,11 @@
expect(getByText('mock-firewall-1')).toBeVisible();

// IP Address panel
expect(getByText('IP Addresses')).toBeVisible();
expect(getByText('Frontend Configuration')).toBeVisible();
expect(getByText('0.0.0.0')).toBeVisible();

// VPC Details Panel
expect(getByText('VPC')).toBeVisible();
expect(getByText('Backend Configuration - VPC')).toBeVisible();
expect(getByText('Subnets:')).toBeVisible();
expect(getByText(`${nbVpcConfig.ipv4_range}`)).toBeVisible();

Expand All @@ -133,19 +131,54 @@
expect(getByText('Add a tag')).toBeVisible();
});

it('displays type: Basic if the nodebalancer is non premium', () => {
queryMocks.useNodeBalancerQuery.mockReturnValue({
data: nodeBalancerFactory.build({ type: 'common' }),
});

const { getByText } = renderWithTheme(<SummaryPanel />);
const typeElement = getByText((_, element) => {
return (
!!element?.hasAttribute('data-qa-type') &&

Check warning on line 142 in packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 3 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":142,"column":33,"nodeType":"Literal","endLine":142,"endColumn":47}
element?.textContent === 'Type: Basic'
);
});
expect(typeElement).toBeVisible();
});

it('displays type: premium if the nodebalancer is premium', () => {
queryMocks.useNodeBalancerQuery.mockReturnValue({
data: nodeBalancerFactory.build({ type: 'premium' }),
});

const { container } = renderWithTheme(<SummaryPanel />);
const { getByText } = renderWithTheme(<SummaryPanel />);

expect(container.querySelector('[data-qa-type]')).toHaveTextContent(
'Type: Premium'
);
const typeElement = getByText((_, element) => {
return (
!!element?.hasAttribute('data-qa-type') &&
element?.textContent === 'Type: Premium'
);
});
expect(typeElement).toBeVisible();
});

it('displays link to cluster if it exists', () => {
it('displays type: Enterprise if the nodebalancer is premium_40GB', () => {
queryMocks.useNodeBalancerQuery.mockReturnValue({
data: nodeBalancerFactory.build({ type: 'premium_40GB' }),
});

const { getByText } = renderWithTheme(<SummaryPanel />);

const typeElement = getByText((_, element) => {
return (
!!element?.hasAttribute('data-qa-type') &&
element?.textContent === 'Type: Enterprise'
);
});
expect(typeElement).toBeVisible();
});

it('displays link to cluster if it exists', async () => {
queryMocks.useNodeBalancerQuery.mockReturnValue({
data: nodeBalancerFactory.build({
lke_cluster: {
Expand All @@ -157,11 +190,19 @@
}),
});

const { container, getByText } = renderWithTheme(<SummaryPanel />);
server.use(
http.get('*/lke/clusters/:clusterId', () => {
return HttpResponse.json({ id: 1, label: 'lke-123' });
})
);

const { getByText } = renderWithTheme(<SummaryPanel />);

expect(getByText('Cluster:')).toBeVisible();
const clusterLink = container.querySelector('[data-qa-cluster] a');
expect(clusterLink).toHaveTextContent('lke-123');
const clusterLink = await waitFor(() => {
return getByText('lke-123');
});
expect(clusterLink).toBeVisible();
expect(clusterLink).toHaveAttribute(
'href',
'/kubernetes/clusters/1/summary'
Expand All @@ -186,16 +227,18 @@
})
);

const { container } = renderWithTheme(<SummaryPanel />);
const { getByText } = renderWithTheme(<SummaryPanel />);

await waitFor(() => {
const clusterLink = container.querySelector('[data-qa-cluster]');
expect(clusterLink).toHaveTextContent('Cluster: lke-123 (deleted)');
expect(clusterLink).not.toHaveAttribute(
'href',
'/kubernetes/clusters/1/summary'
);
const clusterElement = await waitFor(() => {
return getByText((_, element) => {
return (
!!element?.hasAttribute('data-qa-cluster') &&
element?.textContent === 'Cluster: lke-123 (deleted)'
);
});
});
expect(clusterElement).toBeVisible();
expect(clusterElement).not.toHaveAttribute('href');
});

it('should disable "Add a tag" if user does not have permission', () => {
Expand Down
Loading