Skip to content

Commit

Permalink
feat: [M3-7011] - Add firewalls to search result queries (linode#11023)
Browse files Browse the repository at this point in the history
* Add firewalls to search result queries

* Added changeset: Firewalls to search result queries

* Update tests

* Change search bar text and update e2e tests

* Added changeset: Firewall attributes 'created_dt' to 'created' and 'updated_dt' to 'updated'

* Add customized Firewall description

* Move getFirewallDescription to Firewalls/shared.ts
  • Loading branch information
pmakode-akamai authored Oct 7, 2024
1 parent 34d5d1f commit d1edee6
Show file tree
Hide file tree
Showing 15 changed files with 106 additions and 40 deletions.
5 changes: 5 additions & 0 deletions packages/api-v4/.changeset/pr-11023-changed-1727944517923.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Changed
---

Firewall attributes 'created_dt' to 'created' and 'updated_dt' to 'updated' ([#11023](https://github.com/linode/manager/pull/11023))
4 changes: 2 additions & 2 deletions packages/api-v4/src/firewalls/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export interface Firewall {
label: string;
tags: string[];
rules: FirewallRules;
created_dt: string;
updated_dt: string;
created: string;
updated: string;
entities: {
id: number;
type: FirewallDeviceEntityType;
Expand Down
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-11023-added-1727785979860.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Added
---

Firewalls to search result queries ([#11023](https://github.com/linode/manager/pull/11023))
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ const confirmDeletion = (linodeLabel: string) => {
cy.findByText(linodeLabel).should('not.exist');

// Confirm the linode instance is removed
cy.findByText(
'Search for Linodes, Volumes, NodeBalancers, Domains, Buckets, Tags...'
)
cy.findByText('Search Products, IP Addresses, Tags...')
.click()
.type(`${linodeLabel}{enter}`);
cy.findByText('You searched for ...').should('be.visible');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,7 @@ describe('linode landing checks', () => {
getVisible('[aria-label="open menu"]');
getVisible('[data-qa-add-new-menu-button="true"]');
getVisible('[data-qa-search-icon="true"]');
fbtVisible(
'Search for Linodes, Volumes, NodeBalancers, Domains, Buckets, Tags...'
);
fbtVisible('Search Products, IP Addresses, Tags...');

cy.findByLabelText('Help & Support')
.should('be.visible')
Expand Down
12 changes: 6 additions & 6 deletions packages/manager/src/__data__/firewalls.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Firewall } from '@linode/api-v4/lib/firewalls';
import { FirewallDeviceEntityType } from '@linode/api-v4/lib/firewalls';
import type { Firewall } from '@linode/api-v4/lib/firewalls';
import type { FirewallDeviceEntityType } from '@linode/api-v4/lib/firewalls';

export const firewall: Firewall = {
created_dt: '2019-09-11T19:44:38.526Z',
created: '2019-09-11T19:44:38.526Z',
entities: [
{
id: 1,
Expand Down Expand Up @@ -37,11 +37,11 @@ export const firewall: Firewall = {
},
status: 'enabled',
tags: [],
updated_dt: '2019-09-11T19:44:38.526Z',
updated: '2019-09-11T19:44:38.526Z',
};

export const firewall2: Firewall = {
created_dt: '2019-12-11T19:44:38.526Z',
created: '2019-12-11T19:44:38.526Z',
entities: [
{
id: 1,
Expand Down Expand Up @@ -71,7 +71,7 @@ export const firewall2: Firewall = {
},
status: 'disabled',
tags: [],
updated_dt: '2019-12-11T19:44:38.526Z',
updated: '2019-12-11T19:44:38.526Z',
};

export const firewalls = [firewall, firewall2];
4 changes: 2 additions & 2 deletions packages/manager/src/factories/firewalls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const firewallRulesFactory = Factory.Sync.makeFactory<FirewallRules>({
});

export const firewallFactory = Factory.Sync.makeFactory<Firewall>({
created_dt: '2020-01-01 00:00:00',
created: '2020-01-01 00:00:00',
entities: [
{
id: 1,
Expand All @@ -43,7 +43,7 @@ export const firewallFactory = Factory.Sync.makeFactory<Firewall>({
rules: firewallRulesFactory.build(),
status: 'enabled',
tags: [],
updated_dt: '2020-01-01 00:00:00',
updated: '2020-01-01 00:00:00',
});

export const firewallDeviceFactory = Factory.Sync.makeFactory<FirewallDevice>({
Expand Down
13 changes: 12 additions & 1 deletion packages/manager/src/features/Firewalls/shared.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { capitalize } from 'src/utilities/capitalize';
import { truncateAndJoinList } from 'src/utilities/stringUtils';

import { PORT_PRESETS } from './FirewallDetail/Rules/shared';

import type { Grants, Profile } from '@linode/api-v4';
import type {
Firewall,
FirewallRuleProtocol,
FirewallRuleType,
} from '@linode/api-v4/lib/firewalls/types';
import { PORT_PRESETS } from './FirewallDetail/Rules/shared';

export type FirewallPreset = 'dns' | 'http' | 'https' | 'mysql' | 'ssh';

Expand Down Expand Up @@ -258,3 +261,11 @@ export const checkIfUserCanModifyFirewall = (
?.permissions === 'read_write'
);
};

export const getFirewallDescription = (firewall: Firewall) => {
const description = [
`Status: ${capitalize(firewall.status)}`,
`Services Assigned: ${firewall.entities.length}`,
];
return description.join(', ');
};
16 changes: 14 additions & 2 deletions packages/manager/src/features/Search/SearchLanding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Typography } from 'src/components/Typography';
import { useAPISearch } from 'src/features/Search/useAPISearch';
import { useIsLargeAccount } from 'src/hooks/useIsLargeAccount';
import { useAllDomainsQuery } from 'src/queries/domains';
import { useAllFirewallsQuery } from 'src/queries/firewalls';
import { useAllImagesQuery } from 'src/queries/images';
import { useAllKubernetesClustersQuery } from 'src/queries/kubernetes';
import { useAllLinodesQuery } from 'src/queries/linodes/linodes';
Expand Down Expand Up @@ -45,6 +46,7 @@ import type { RouteComponentProps } from 'react-router-dom';
const displayMap = {
buckets: 'Buckets',
domains: 'Domains',
firewalls: 'Firewalls',
images: 'Images',
kubernetesClusters: 'Kubernetes',
linodes: 'Linodes',
Expand Down Expand Up @@ -85,6 +87,12 @@ export const SearchLanding = (props: SearchLandingProps) => {
isLoading: areDomainsLoading,
} = useAllDomainsQuery(shouldFetchAllEntities);

const {
data: firewalls,
error: firewallsError,
isLoading: areFirewallsLoading,
} = useAllFirewallsQuery(shouldFetchAllEntities);

const {
data: kubernetesClusters,
error: kubernetesClustersError,
Expand Down Expand Up @@ -177,7 +185,8 @@ export const SearchLanding = (props: SearchLandingProps) => {
_privateImages ?? [],
regions ?? [],
searchableLinodes ?? [],
nodebalancers ?? []
nodebalancers ?? [],
firewalls ?? []
);
}
}, [
Expand All @@ -194,6 +203,7 @@ export const SearchLanding = (props: SearchLandingProps) => {
regions,
nodebalancers,
linodes,
firewalls,
]);

const getErrorMessage = () => {
Expand All @@ -205,6 +215,7 @@ export const SearchLanding = (props: SearchLandingProps) => {
[imagesError, 'Images'],
[nodebalancersError, 'NodeBalancers'],
[kubernetesClustersError, 'Kubernetes'],
[firewallsError, 'Firewalls'],
[
objectStorageBuckets && objectStorageBuckets.errors.length > 0,
`Object Storage in ${objectStorageBuckets?.errors
Expand Down Expand Up @@ -238,7 +249,8 @@ export const SearchLanding = (props: SearchLandingProps) => {
areVolumesLoading ||
areKubernetesClustersLoading ||
areImagesLoading ||
areNodeBalancersLoading;
areNodeBalancersLoading ||
areFirewallsLoading;

const errorMessage = getErrorMessage();

Expand Down
2 changes: 2 additions & 0 deletions packages/manager/src/features/Search/search.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface SearchableItem<T = number | string> {
export type SearchableEntityType =
| 'bucket'
| 'domain'
| 'firewall'
| 'image'
| 'kubernetesCluster'
| 'linode'
Expand All @@ -25,6 +26,7 @@ export type SearchField = 'ips' | 'label' | 'tags' | 'type';
export interface SearchResultsByEntity {
buckets: SearchableItem[];
domains: SearchableItem[];
firewalls: SearchableItem[];
images: SearchableItem[];
kubernetesClusters: SearchableItem[];
linodes: SearchableItem[];
Expand Down
6 changes: 5 additions & 1 deletion packages/manager/src/features/Search/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { searchableItems } from 'src/__data__/searchableItems';
import { SearchableItem } from 'src/features/Search/search.interfaces';

import { separateResultsByEntity } from './utils';

import type { SearchableItem } from 'src/features/Search/search.interfaces';

const data = searchableItems as SearchableItem[];

describe('separate results by entity', () => {
Expand All @@ -15,6 +16,7 @@ describe('separate results by entity', () => {
expect(results).toHaveProperty('nodebalancers');
expect(results).toHaveProperty('kubernetesClusters');
expect(results).toHaveProperty('buckets');
expect(results).toHaveProperty('firewalls');
});

it('the value of each entity type is an array', () => {
Expand All @@ -25,13 +27,15 @@ describe('separate results by entity', () => {
expect(results.nodebalancers).toBeInstanceOf(Array);
expect(results.kubernetesClusters).toBeInstanceOf(Array);
expect(results.buckets).toBeInstanceOf(Array);
expect(results.firewalls).toBeInstanceOf(Array);
});

it('returns empty results if there is no data', () => {
const newResults = separateResultsByEntity([]);
expect(newResults).toEqual({
buckets: [],
domains: [],
firewalls: [],
images: [],
kubernetesClusters: [],
linodes: [],
Expand Down
7 changes: 6 additions & 1 deletion packages/manager/src/features/Search/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { SearchResultsByEntity, SearchableItem } from './search.interfaces';
import type {
SearchResultsByEntity,
SearchableItem,
} from './search.interfaces';

export const emptyResults: SearchResultsByEntity = {
buckets: [],
domains: [],
firewalls: [],
images: [],
kubernetesClusters: [],
linodes: [],
Expand All @@ -16,6 +20,7 @@ export const separateResultsByEntity = (
const separatedResults: SearchResultsByEntity = {
buckets: [],
domains: [],
firewalls: [],
images: [],
kubernetesClusters: [],
linodes: [],
Expand Down
37 changes: 22 additions & 15 deletions packages/manager/src/features/Search/withStoreSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
import {
Image,
KubernetesCluster,
NodeBalancer,
Region,
Volume,
} from '@linode/api-v4';
import { Domain } from '@linode/api-v4/lib/domains';
import { ObjectStorageBucket } from '@linode/api-v4/lib/object-storage';
import * as React from 'react';
import { compose, withStateHandlers } from 'recompose';

import {
bucketToSearchableItem,
domainToSearchableItem,
firewallToSearchableItem,
imageToSearchableItem,
kubernetesClusterToSearchableItem,
nodeBalToSearchableItem,
volumeToSearchableItem,
} from 'src/store/selectors/getSearchEntities';

import { refinedSearch } from './refinedSearch';
import {
import { emptyResults, separateResultsByEntity } from './utils';

import type {
SearchResults,
SearchResultsByEntity,
SearchableItem,
} from './search.interfaces';
import { emptyResults, separateResultsByEntity } from './utils';
import type {
Firewall,
Image,
KubernetesCluster,
NodeBalancer,
Region,
Volume,
} from '@linode/api-v4';
import type { Domain } from '@linode/api-v4/lib/domains';
import type { ObjectStorageBucket } from '@linode/api-v4/lib/object-storage';

interface HandlerProps {
search: (
Expand All @@ -37,7 +40,8 @@ interface HandlerProps {
images: Image[],
regions: Region[],
searchableLinodes: SearchableItem<number | string>[],
nodebalancers: NodeBalancer[]
nodebalancers: NodeBalancer[],
firewalls: Firewall[]
) => SearchResults;
}
export interface SearchProps extends HandlerProps {
Expand Down Expand Up @@ -83,7 +87,8 @@ export default () => (Component: React.ComponentType<any>) => {
images: Image[],
regions: Region[],
searchableLinodes: SearchableItem<number | string>[],
nodebalancers: NodeBalancer[]
nodebalancers: NodeBalancer[],
firewalls: Firewall[]
) => {
const searchableBuckets = objectStorageBuckets.map((bucket) =>
bucketToSearchableItem(bucket)
Expand All @@ -97,14 +102,15 @@ export default () => (Component: React.ComponentType<any>) => {
const searchableImages = images.map((image) =>
imageToSearchableItem(image)
);

const searchableClusters = clusters.map((cluster) =>
kubernetesClusterToSearchableItem(cluster, regions)
);

const searchableNodebalancers = nodebalancers.map((nodebalancer) =>
nodeBalToSearchableItem(nodebalancer)
);
const searchableFirewalls = firewalls.map((firewall) =>
firewallToSearchableItem(firewall)
);
const results = search(
[
...searchableLinodes,
Expand All @@ -114,6 +120,7 @@ export default () => (Component: React.ComponentType<any>) => {
...searchableVolumes,
...searchableClusters,
...searchableNodebalancers,
...searchableFirewalls,
],
query
);
Expand Down
Loading

0 comments on commit d1edee6

Please sign in to comment.