Skip to content

Commit

Permalink
upcoming: [DI-20204] - Added Interface type setup, query updates and …
Browse files Browse the repository at this point in the history
…enhanced preferences type for upcoming Cloudpulsecustomselect component (linode#10769)

Co-authored-by: vmangalr <vmangalr@akamai.com>
  • Loading branch information
venkymano-akamai and vmangalr authored Aug 20, 2024
1 parent dd19e75 commit 9f0138e
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Upcoming Features
---

Update the type for AclpConfig ([#10769](https://github.com/linode/manager/pull/10769))
20 changes: 14 additions & 6 deletions packages/api-v4/src/cloudpulse/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,21 @@ export interface Filters {
value: string;
}

// Define the type for filter values
type FilterValue =
| number
| string
| string[]
| number[]
| WidgetFilterValue
| undefined;

type WidgetFilterValue = { [key: string]: AclpWidget };

export interface AclpConfig {
dashboardId: number;
interval: string;
region: string;
resources: string[];
timeDuration: string;
widgets: { [label: string]: AclpWidget };
// we maintain only the filters selected in the preferences for latest selected dashboard
[key: string]: FilterValue;
widgets?: WidgetFilterValue;
}

export interface AclpWidget {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Enhance support for passing Xfilters in existing databases fetch all instances query and Add a new query method for supporting custom filter using inbuilt API-V4 query factories for services like linode, dbaas etc., for upcoming CloudpulseCustomSelection component ([#10769](https://github.com/linode/manager/pull/10769))
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const DBAAS_CONFIG: Readonly<CloudPulseServiceTypeFilterMap> = {
filters: [
{
configuration: {
filterKey: 'dbEngine',
filterKey: 'engine',
filterType: 'string',
isFilterable: false, // isFilterable -- this determines whethere you need to pass it metrics api
isMetricsFilter: false, // if it is false, it will go as a part of filter params, else global filter
Expand Down Expand Up @@ -92,7 +92,7 @@ export const DBAAS_CONFIG: Readonly<CloudPulseServiceTypeFilterMap> = {
},
{
configuration: {
dependency: ['region', 'dbEngine'],
dependency: ['region', 'engine'],
filterKey: 'resource_id',
filterType: 'string',
isFilterable: true,
Expand Down
36 changes: 35 additions & 1 deletion packages/manager/src/features/CloudPulse/Utils/models.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import type { DatabaseEngine, DatabaseType } from '@linode/api-v4';
import type { QueryFunction, QueryKey } from '@tanstack/react-query';

/**
* The CloudPulseServiceTypeMap has list of filters to be built for different service types like dbaas, linode etc.,The properties here are readonly as it is only for reading and can't be modified in code
*/
Expand Down Expand Up @@ -30,6 +33,36 @@ export interface CloudPulseServiceTypeFilters {
name: string;
}

/**
* As of now, the list of possible custom filters are engine, database type, this union type will be expanded if we start enhancing our custom select config
*/
export type QueryFunctionType = DatabaseEngine[] | DatabaseType[];

/**
* The non array types of QueryFunctionType like DatabaseEngine|DatabaseType
*/
export type QueryFunctionNonArrayType = SingleType<QueryFunctionType>;

/**
* This infers the type from the QueryFunctionType and makes it a single object type, and by using this we can maintain only QueryFunctionType and NonArray Types are automatically identified
*/
type SingleType<T> = T extends (infer U)[] ? U : never;

/**
* This interface holds the query function and query key from various factories, like databaseQueries, linodeQueries etc.,
*/
export interface QueryFunctionAndKey {
/**
* The query function that contains actual function that calls API like getDatabaseEngines, getDatabaseTypes etc.,
*/
queryFn: QueryFunction<Awaited<QueryFunctionType>>;

/**
* The actual query key defined in the factory
*/
queryKey: QueryKey;
}

/**
* CloudPulseServiceTypeFiltersConfiguration is the actual configuration of the filter component
*/
Expand All @@ -46,8 +79,9 @@ export interface CloudPulseServiceTypeFiltersConfiguration {

/**
* This is an optional field, it is required if the type is dynamic for call the respective API to get the filters
* example, databaseQueries.types, databaseQueries.engines etc., makes use of existing query key and optimises cache
*/
apiUrl?: string;
apiV4QueryKey?: QueryFunctionAndKey;

/**
* This is an optional field, it is used to disable a certain filter, untill of the dependent filters are selected
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ export const CloudPulseResourcesSelect = React.memo(

// Once the data is loaded, set the state variable with value stored in preferences
React.useEffect(() => {
const defaultResources = getUserPreferenceObject()?.resources;
const saveResources = getUserPreferenceObject()?.resources;
const defaultResources = Array.isArray(saveResources)
? Array.of(saveResources).map((resourceId) => String(resourceId))
: undefined;
if (resources) {
if (defaultResources) {
const resource = getResourcesList().filter((resource) =>
Expand Down
93 changes: 93 additions & 0 deletions packages/manager/src/queries/cloudpulse/customfilters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useQuery } from '@tanstack/react-query';

import type { Filter, Params } from '@linode/api-v4';
import type { CloudPulseServiceTypeFiltersOptions } from 'src/features/CloudPulse/Utils/models';
import type { QueryFunctionAndKey } from 'src/features/CloudPulse/Utils/models';
import type {
QueryFunctionNonArrayType,
QueryFunctionType,
} from 'src/features/CloudPulse/Utils/models';

/**
* Interface that has the parameters required for making custom filter query call
*/
interface CustomFilterQueryProps {
/**
* The Built in API-V4 query factory functions like databaseQueries.types, databaseQueries.engines etc., makes use of existing query key and optimises cache
*/
apiV4QueryKey: QueryFunctionAndKey;
/**
* This indicates whether or not to enable the query
*/
enabled: boolean;
/**
* The xFilter that is used to filter the results in API
*/
filter?: Filter;
/**
* The id field to consider from the response of the custom filter call
*/
idField: string;
/**
* The label field to consider from the response of the custom filter call
*/
labelField: string;

/**
* The params like pagination information to be passed to the API
*/
params?: Params;
}

/**
* This functions queries the API-V4 URL and returns the filter value list like db engines, node types, region and resources etc.,
* @param queryProps - The parameters required for making custom filter query call
*/
export const useGetCustomFiltersQuery = (
queryProps: CustomFilterQueryProps
) => {
const { apiV4QueryKey, enabled, idField, labelField } = queryProps;
return useQuery<
QueryFunctionType,
unknown,
CloudPulseServiceTypeFiltersOptions[]
>({
// receive filters and return only id and label
enabled,
...apiV4QueryKey,
select: (
filters: QueryFunctionType
): CloudPulseServiceTypeFiltersOptions[] => {
// whatever field we receive, just return id and label
return filters
.map(
(filter): CloudPulseServiceTypeFiltersOptions => {
return {
id: getStringValue(filter, idField) ?? '',
label: getStringValue(filter, labelField) ?? '',
};
}
)
.filter(({ id, label }) => id.length && label.length);
},
});
};

/**
*
* @param filter The queried filters like DatabaseEngine , DatabaseType
* @param fieldKey The id and label field that needs to be fetched from the filter
* @returns String value of the fieldKey in the filter object
*/
const getStringValue = (
filter: QueryFunctionNonArrayType,
fieldKey: string
): string | undefined => {
if (fieldKey in filter) {
const value = filter[fieldKey as keyof typeof filter]; // since we already checked fieldKey is present in filterObj like (DatabaseEngine, DatabaseType) we can fetch it by considering fieldKey as key of filter
if (value) {
return String(value);
}
}
return undefined;
};
4 changes: 4 additions & 0 deletions packages/manager/src/queries/cloudpulse/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from '@linode/api-v4';
import { createQueryKeys } from '@lukemorales/query-key-factory';

import { databaseQueries } from '../databases/databases';
import { getAllLinodesRequest } from '../linodes/requests';
import { volumeQueries } from '../volumes/volumes';
import { fetchCloudPulseMetrics } from './metrics';
Expand Down Expand Up @@ -64,6 +65,9 @@ export const queryFactory = createQueryKeys(key, {
case 'volumes':
return volumeQueries.lists._ctx.all(params, filters); // in this we don't need to define our own query factory, we will reuse existing implementation in volumes.ts

case 'dbaas':
return databaseQueries.databases._ctx.all(params, filters);

default:
return volumeQueries.lists._ctx.all(params, filters); // default to volumes
}
Expand Down
16 changes: 10 additions & 6 deletions packages/manager/src/queries/databases/databases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ export const databaseQueries = createQueryKeys('databases', {
}),
databases: {
contextQueries: {
all: {
queryFn: getAllDatabases,
queryKey: null,
},
all: (params: Params = {}, filter: Filter = {}) => ({
queryFn: () => getAllDatabases(params, filter),
queryKey: [params, filter],
}),
paginated: (params: Params, filter: Filter) => ({
queryFn: () => getDatabases(params, filter),
queryKey: [params, filter],
Expand Down Expand Up @@ -92,9 +92,13 @@ export const useDatabasesQuery = (params: Params, filter: Filter) =>
refetchInterval: 20000,
});

export const useAllDatabasesQuery = (enabled: boolean = true) =>
export const useAllDatabasesQuery = (
enabled: boolean = true,
params: Params = {},
filter: Filter = {}
) =>
useQuery<DatabaseInstance[], APIError[]>({
...databaseQueries.databases._ctx.all,
...databaseQueries.databases._ctx.all(params, filter),
enabled,
});

Expand Down
20 changes: 15 additions & 5 deletions packages/manager/src/queries/databases/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,24 @@ import {
getDatabaseTypes,
getDatabases,
} from '@linode/api-v4';
import { DatabaseEngine, DatabaseInstance, DatabaseType } from '@linode/api-v4';

import { getAll } from 'src/utilities/getAll';

export const getAllDatabases = () =>
getAll<DatabaseInstance>((params) => getDatabases(params))().then(
(data) => data.data
);
import type {
DatabaseEngine,
DatabaseInstance,
DatabaseType,
Filter,
Params,
} from '@linode/api-v4';

export const getAllDatabases = (
passedParams: Params = {},
passedFilter: Filter = {}
) =>
getAll<DatabaseInstance>((params, filter) =>
getDatabases({ ...params, ...passedParams }, { ...filter, ...passedFilter })
)().then((data) => data.data);

export const getAllDatabaseEngines = () =>
getAll<DatabaseEngine>((params) => getDatabaseEngines(params))().then(
Expand Down

0 comments on commit 9f0138e

Please sign in to comment.