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
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-13492-added-1773404986416.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Added
---

Stream Metrics tab with embedded metrics dashboard ([#13492](https://github.com/linode/manager/pull/13492))
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ function editDestinationViaActionMenu(
mockGetDestination(destination);
// Edit destination redirect
ui.actionMenuItem.findByTitle('Edit').click();
cy.url().should('endWith', `/destinations/${destination.id}/edit`);
cy.url().should('endWith', `/destinations/${destination.id}/summary`);
});
}

Expand Down Expand Up @@ -181,15 +181,15 @@ describe('destinations landing checks for non-empty state', () => {
});
});

it('navigates to edit page when clicking destination label', () => {
it('navigates to summary page when clicking destination label', () => {
cy.visitWithLogin('/logs/delivery/destinations');
cy.wait('@getDestinations');

const destination = mockAkamaiObjectStorageDestinations[0];
mockGetDestination(destination).as('getDestination');

cy.findByText(destination.label).click();
cy.url().should('endWith', `/destinations/${destination.id}/edit`);
cy.url().should('endWith', `/destinations/${destination.id}/summary`);
cy.wait('@getDestination');
});

Expand Down Expand Up @@ -255,7 +255,7 @@ describe('destinations landing checks for non-empty state', () => {
mockGetDestination(destination).as('getDestination');

cy.findByText(destination.label).click();
cy.url().should('endWith', `/destinations/${destination.id}/edit`);
cy.url().should('endWith', `/destinations/${destination.id}/summary`);
cy.wait('@getDestination');
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('Edit Destination', () => {
describe('given Akamai Object Storage type destination', () => {
beforeEach(() => {
cy.visitWithLogin(
`/logs/delivery/destinations/${mockAkamaiObjectStorageDestination.id}/edit`
`/logs/delivery/destinations/${mockAkamaiObjectStorageDestination.id}/summary`
);
mockGetDestination(mockAkamaiObjectStorageDestination);
});
Expand Down Expand Up @@ -199,7 +199,7 @@ describe('Edit Destination', () => {
describe('given Custom HTTPS type destination', () => {
beforeEach(() => {
cy.visitWithLogin(
`/logs/delivery/destinations/${mockCustomHttpsDestination.id}/edit`
`/logs/delivery/destinations/${mockCustomHttpsDestination.id}/summary`
);
mockGetDestination(mockCustomHttpsDestination);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ describe('Edit Stream', () => {
mockGetStream(mockAuditLogsStream);

// Visit the Edit Stream page
cy.visitWithLogin(
`/logs/delivery/streams/${mockAuditLogsStream.id}/edit/`
);
cy.visitWithLogin(`/logs/delivery/streams/${mockAuditLogsStream.id}`);

const updatedLabel = randomLabel();

Expand Down Expand Up @@ -206,9 +204,7 @@ describe('Edit Stream', () => {
mockGetClusters([cluster1, cluster2, cluster3, cluster4]);

// Visit the Edit Stream page
cy.visitWithLogin(
`/logs/delivery/streams/${mockLKEAuditLogsStream.id}/edit/`
);
cy.visitWithLogin(`/logs/delivery/streams/${mockLKEAuditLogsStream.id}`);

const updatedLabel = randomLabel();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ function editStreamViaActionMenu(tableAlias: string, stream: Stream) {
mockGetStream(stream);
// Edit stream redirect
ui.actionMenuItem.findByTitle('Edit').click();
cy.url().should('endWith', `/streams/${stream.id}/edit`);
cy.url().should('endWith', `/streams/${stream.id}/summary`);
});
}

Expand Down Expand Up @@ -180,7 +180,7 @@ describe('Streams non-empty landing page', () => {

// Redirect to stream edit page via name
cy.findByText(exampleStream.label).click();
cy.url().should('endWith', `/streams/${exampleStream.id}/edit`);
cy.url().should('endWith', `/streams/${exampleStream.id}/summary`);
cy.wait(['@getStream', '@getDestinations']);

// Redirect to stream edit page via menu item
Expand Down
4 changes: 4 additions & 0 deletions packages/manager/src/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ interface AclpLogsFlag extends BetaFeatureFlag {
* This property indicates whether to show Custom HTTPS destination type
*/
customHttpsEnabled?: boolean;
/**
* This property indicates whether to show the "Metrics" tab on Logs Stream details page or not
*/
metricsEnabled?: boolean;
/**
* This property indicates whether the feature is new or not
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,14 +214,14 @@ describe('DestinationEdit', () => {
});
});

describe('given Test Connection and Edit Destination buttons', () => {
describe('given Test Connection and Save Changes buttons', () => {
const testConnectionButtonText = 'Test Connection';
const saveDestinationButtonText = 'Save Changes';
const editDestinationSpy = vi.fn();
const verifyDestinationSpy = vi.fn();

describe('when Test Connection button clicked and connection verified positively', () => {
it("should enable Edit Destination button and perform proper call when it's clicked", async () => {
it("should enable Save Changes button and perform proper call when it's clicked", async () => {
server.use(
http.get(`*/monitor/streams/destinations/${destinationId}`, () => {
return HttpResponse.json(mockDestination);
Expand Down Expand Up @@ -267,7 +267,7 @@ describe('DestinationEdit', () => {
});

describe('when Test Connection button clicked and connection verified negatively', () => {
it('should not enable Edit Destination button', async () => {
it('should not enable Save Changes button', async () => {
server.use(
http.get(`*/monitor/streams/destinations/${destinationId}`, () => {
return HttpResponse.json(mockDestination);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import type { DestinationFormType } from 'src/features/Delivery/Shared/types';
export const DestinationEdit = () => {
const navigate = useNavigate();
const { destinationId } = useParams({
from: '/logs/delivery/destinations/$destinationId/edit',
from: '/logs/delivery/destinations/$destinationId/summary',
});
const { mutateAsync: updateDestination, isPending: isUpdatingDestination } =
useUpdateDestinationMutation();
Expand All @@ -37,7 +37,7 @@ export const DestinationEdit = () => {

const landingHeaderProps: LandingHeaderProps = {
breadcrumbProps: {
pathname: '/logs/delivery/destinations/edit',
pathname: '/logs/delivery/destinations/summary',
crumbOverrides: [
{
label: 'Delivery',
Expand All @@ -48,7 +48,7 @@ export const DestinationEdit = () => {
},
docsLink: 'https://techdocs.akamai.com/cloud-computing/docs/log-delivery',
removeCrumbX: [1, 2],
title: `Edit Destination ${destinationId}`,
title: `Destination ${destinationId}`,
};

const form = useForm<DestinationFormType>({
Expand Down Expand Up @@ -114,7 +114,7 @@ export const DestinationEdit = () => {

return (
<>
<DocumentTitleSegment segment="Edit Destination" />
<DocumentTitleSegment segment="Destination" />
<LandingHeader {...landingHeaderProps} />
{isLoading && (
<Box display="flex" justifyContent="center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createLazyRoute } from '@tanstack/react-router';
import { DestinationEdit } from 'src/features/Delivery/Destinations/DestinationForm/DestinationEdit';

export const destinationEditLazyRoute = createLazyRoute(
'/logs/delivery/destinations/$destinationId/edit'
'/logs/delivery/destinations/$destinationId/summary'
)({
component: DestinationEdit,
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const DestinationTableRow = React.memo(
<TableCell>
<LinkWithTooltipAndEllipsis
pendoId="Logs Delivery Destinations-Name"
to={`/logs/delivery/destinations/${id}/edit`}
to={`/logs/delivery/destinations/${id}/summary`}
>
{destination.label}
</LinkWithTooltipAndEllipsis>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ describe('Destinations Landing Table', () => {
await clickOnActionMenuItem('Edit');

expect(mockNavigate).toHaveBeenCalledWith({
to: '/logs/delivery/destinations/1/edit',
to: '/logs/delivery/destinations/1/summary',
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export const DestinationsLanding = () => {
}

const handleEdit = ({ id }: Destination) => {
navigate({ to: `/logs/delivery/destinations/${id}/edit` });
navigate({ to: `/logs/delivery/destinations/${id}/summary` });
};

const openDeleteDialog = (destination: Destination) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { screen } from '@testing-library/react';
import * as React from 'react';
import { beforeEach, describe, it } from 'vitest';

import {
akamaiObjectStorageDestinationFactory,
streamFactory,
} from 'src/factories';
import { StreamLanding } from 'src/features/Delivery/Streams/Stream/StreamLanding';
import { renderWithTheme } from 'src/utilities/testHelpers';

import type { Flags } from 'src/featureFlags';

const queryMocks = vi.hoisted(() => ({
useStreamQuery: vi.fn().mockReturnValue({}),
}));

vi.mock('@linode/queries', async () => {
const actual = await vi.importActual('@linode/queries');
return {
...actual,
useStreamQuery: queryMocks.useStreamQuery,
};
});

const streamId = 123;
const mockDestinations = [
akamaiObjectStorageDestinationFactory.build({ id: 1 }),
];
const mockStream = streamFactory.build({
id: streamId,
label: `Stream ${streamId}`,
destinations: mockDestinations,
});

describe('StreamLanding', () => {
const renderComponent = (flags: Partial<Flags>) => {
renderWithTheme(<StreamLanding />, {
flags,
initialRoute: '/logs/delivery/streams/$streamId/summary',
});
};

describe('and stream has loaded successfully', () => {
beforeEach(async () => {
queryMocks.useStreamQuery.mockReturnValue({
data: mockStream,
isLoading: false,
});
});

describe('and metrics are not enabled', () => {
const flags = {
aclpLogs: {
enabled: true,
beta: false,
metricsEnabled: false,
},
};

it('should render the summary and not the metrics tab', async () => {
renderComponent(flags);

screen.getByText('Summary');
expect(screen.queryByText('Metrics')).not.toBeInTheDocument();
});
});

describe('and metrics are enabled', () => {
const flags = {
aclpLogs: {
enabled: true,
beta: false,
metricsEnabled: true,
},
};

it('should render the summary tab and metrics tab', async () => {
renderComponent(flags);

screen.getByText('Summary');
expect(screen.queryByText('Metrics')).toBeInTheDocument();

Check warning on line 82 in packages/manager/src/features/Delivery/Streams/Stream/StreamLanding.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Use `getBy*` queries rather than `queryBy*` for checking element is present Raw Output: {"ruleId":"testing-library/prefer-presence-queries","severity":1,"message":"Use `getBy*` queries rather than `queryBy*` for checking element is present","line":82,"column":23,"nodeType":"Identifier","messageId":"wrongPresenceQuery","endLine":82,"endColumn":34}
});
});
});

describe('and stream is loading', () => {
beforeEach(async () => {
queryMocks.useStreamQuery.mockReturnValue({
isLoading: true,
});
});

it('should render loading spinner', async () => {
renderComponent({});

expect(screen.queryByText('Summary')).not.toBeInTheDocument();
expect(screen.queryByText('Metrics')).not.toBeInTheDocument();

const loadingElement = screen.queryByTestId('circle-progress');
expect(loadingElement).toBeInTheDocument();
});
});

describe('and stream request threw error', () => {
const streamErrorMessage = 'Stream not found';
beforeEach(async () => {
queryMocks.useStreamQuery.mockReturnValue({
isLoading: false,
error: [{ reason: streamErrorMessage }],
});
});

it('should render error state with message', async () => {
renderComponent({});

expect(screen.queryByText('Summary')).not.toBeInTheDocument();
expect(screen.queryByText('Metrics')).not.toBeInTheDocument();

expect(screen.queryByText(streamErrorMessage)).toBeInTheDocument();

Check warning on line 120 in packages/manager/src/features/Delivery/Streams/Stream/StreamLanding.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Use `getBy*` queries rather than `queryBy*` for checking element is present Raw Output: {"ruleId":"testing-library/prefer-presence-queries","severity":1,"message":"Use `getBy*` queries rather than `queryBy*` for checking element is present","line":120,"column":21,"nodeType":"Identifier","messageId":"wrongPresenceQuery","endLine":120,"endColumn":32}
});
});
});
Loading
Loading