Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bef3dd2
[DI-26882] - Add initial changes for object storage integration in me…
ankita-akamai Sep 15, 2025
9fdd7c9
[DI-26882] - Update type for reusable component, queryFn, tests, and …
ankita-akamai Sep 16, 2025
3f3f6b1
[DI-26882] - Integrate new component and temporarily update obj stora…
ankita-akamai Sep 25, 2025
cc1b8b3
[DI-26882] - Make prop otional
ankita-akamai Sep 17, 2025
a3134a4
[DI-26882] - Add tests for endpoints props, update comments
ankita-akamai Sep 25, 2025
b175623
[DI-26882] - Update test case
ankita-akamai Sep 17, 2025
e578219
[DI-26882] - Review suggestions
ankita-akamai Sep 19, 2025
d8258a9
[DI-26882] - Simplify props, add new func
ankita-akamai Sep 25, 2025
d610586
[DI-26882] - Update unit tes
ankita-akamai Sep 19, 2025
217472d
[DI-26882] - Update util
ankita-akamai Sep 19, 2025
2b241ed
[DI-26882] - Fix linting
ankita-akamai Sep 22, 2025
4e5f936
Merge branch 'develop' of github.com:ankitaakamai/manager into featur…
ankita-akamai Sep 25, 2025
efa9170
[DI-26882] - Add changesets
ankita-akamai Sep 25, 2025
0ed7b53
[DI-26882] - Update queryKeys
ankita-akamai Sep 25, 2025
dd53e47
[DI-26882] - Remove type - any
ankita-akamai Sep 25, 2025
57bec96
Merge branch 'develop' into feature/objStPart2
ankita-akamai Sep 29, 2025
1665682
upcoming: [DI-26882] - Remove temporary changes
ankita-akamai Sep 29, 2025
9974294
Merge branch 'feature/objStPart2' of github.com:ankitaakamai/manager …
ankita-akamai Sep 29, 2025
8a483e6
Merge branch 'develop' into feature/objStPart2
nikhagra-akamai Sep 30, 2025
a2e1bc3
Merge branch 'develop' into feature/objStPart2
nikhagra-akamai Sep 30, 2025
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
---

CloudPulse-Metrics: Update `CloudPulseMetricsRequest` and `JWETokenPayLoad` type at `types.ts` ([#12912](https://github.com/linode/manager/pull/12912))
6 changes: 3 additions & 3 deletions packages/api-v4/src/cloudpulse/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export type CloudPulseServiceType =
| 'linode'
| 'nodebalancer'
| 'objectstorage';

export type AlertClass = 'dedicated' | 'shared';
export type DimensionFilterOperatorType =
| 'endswith'
Expand Down Expand Up @@ -134,7 +133,7 @@ export interface Dimension {
}

export interface JWETokenPayLoad {
entity_ids: number[];
entity_ids?: number[];
}

export interface JWEToken {
Expand All @@ -149,7 +148,8 @@ export interface Metric {
export interface CloudPulseMetricsRequest {
absolute_time_duration: DateTimeWithPreset | undefined;
associated_entity_region?: string;
entity_ids: number[];
entity_ids: number[] | string[];
entity_region?: string;
filters?: Filters[];
group_by?: string[];
metrics: Metric[];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

CloudPulse-Metrics: Handle special conditions for `objectstorage` service addition, add related filters at `FilterConfig.ts`, integrate related component `CloudPulseEndpointsSelect.tsx` ([#12912](https://github.com/linode/manager/pull/12912))
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import {
} from '../Widget/CloudPulseWidgetRenderer';

import type { CloudPulseMetricsAdditionalFilters } from '../Widget/CloudPulseWidget';
import type { DateTimeWithPreset, JWETokenPayLoad } from '@linode/api-v4';
import type {
CloudPulseServiceType,
DateTimeWithPreset,
JWETokenPayLoad,
} from '@linode/api-v4';

export interface DashboardProperties {
/**
Expand Down Expand Up @@ -65,6 +69,11 @@ export interface DashboardProperties {
*/
savePref?: boolean;

/**
* Selected service type for the dashboard
*/
serviceType: CloudPulseServiceType;

/**
* Selected tags for the dashboard
*/
Expand All @@ -79,12 +88,18 @@ export const CloudPulseDashboard = (props: DashboardProperties) => {
manualRefreshTimeStamp,
resources,
savePref,
serviceType,
groupBy,
linodeRegion,
region,
} = props;

const { preferences } = useAclpPreference();

const getJweTokenPayload = (): JWETokenPayLoad => {
if (serviceType === 'objectstorage') {
return {};
}
return {
entity_ids: resources?.map((resource) => Number(resource)) ?? [],
};
Expand Down Expand Up @@ -169,6 +184,7 @@ export const CloudPulseDashboard = (props: DashboardProperties) => {
manualRefreshTimeStamp={manualRefreshTimeStamp}
metricDefinitions={metricDefinitions}
preferences={preferences}
region={region}
resourceList={resourceList}
resources={resources}
savePref={savePref}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export const CloudPulseDashboardRenderer = React.memo(
: []
}
savePref={true}
serviceType={dashboard.service_type}
tags={
filterValue[TAGS] && Array.isArray(filterValue[TAGS])
? (filterValue[TAGS] as string[])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,19 @@ export interface CloudPulseDashboardWithFiltersProp {
* The id of the dashboard that needs to be rendered
*/
dashboardId: number;
/**
* The region for which the metrics will be listed
*/
region?: string;
/**
* The resource id for which the metrics will be listed
*/
resource: number;
resource: number | string;
}

export const CloudPulseDashboardWithFilters = React.memo(
(props: CloudPulseDashboardWithFiltersProp) => {
const { dashboardId, resource } = props;
const { dashboardId, resource, region } = props;
const { data: dashboard, isError } =
useCloudPulseDashboardByIdQuery(dashboardId);
const [filterData, setFilterData] = React.useState<FilterData>({
Expand Down Expand Up @@ -122,6 +126,7 @@ export const CloudPulseDashboardWithFilters = React.memo(
dashboardObj: dashboard,
filterValue: filterData.id,
resource,
region,
timeDuration,
groupBy,
});
Expand Down Expand Up @@ -180,7 +185,13 @@ export const CloudPulseDashboardWithFilters = React.memo(
emitFilterChange={onFilterChange}
handleToggleAppliedFilter={toggleAppliedFilter}
isServiceAnalyticsIntegration
resource_ids={[resource]}
resource_ids={
dashboard.service_type !== 'objectstorage'
? typeof resource === 'number'
? [resource]
: undefined
: undefined
}
/>
)}
<GridLegacy
Expand All @@ -206,6 +217,7 @@ export const CloudPulseDashboardWithFilters = React.memo(
dashboardObj: dashboard,
filterValue: filterData.id,
resource,
region,
timeDuration,
groupBy,
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { formatPercentage } from '@linode/utilities';

import { widgetFactory } from 'src/factories';

import {
generateGraphData,
generateMaxUnit,
getDimensionName,
getEntityIds,
getLabelName,
getTimeDurationFromPreset,
mapResourceIdToName,
Expand Down Expand Up @@ -261,4 +264,26 @@ describe('getTimeDurationFromPreset method', () => {
const result = getTimeDurationFromPreset('15min');
expect(result).toBe(undefined);
});

describe('getEntityIds method', () => {
it('should return entity ids for linode service type', () => {
const result = getEntityIds(
[{ id: '123', label: 'linode-1' }],
['123'],
widgetFactory.build(),
'linode'
);
expect(result).toEqual([123]);
});

it('should return entity ids for objectstorage service type', () => {
const result = getEntityIds(
[{ id: 'bucket-1', label: 'bucket-name-1' }],
['bucket-1'],
widgetFactory.build(),
'objectstorage'
);
expect(result).toEqual(['bucket-1']);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,21 @@ interface MetricRequestProps {
*/
linodeRegion?: string;

/**
* selected region for the widget
*/
region?: string;

/**
* list of CloudPulse resources available
*/
resources: CloudPulseResources[];

/**
* service type of the widget
*/
serviceType: CloudPulseServiceType;

/**
* widget filters for metrics data
*/
Expand Down Expand Up @@ -326,18 +336,23 @@ export const generateMaxUnit = (
export const getCloudPulseMetricRequest = (
props: MetricRequestProps
): CloudPulseMetricsRequest => {
const { duration, entityIds, resources, widget, groupBy, linodeRegion } =
props;
const {
duration,
entityIds,
resources,
widget,
groupBy,
linodeRegion,
region,
serviceType,
} = props;
const preset = duration.preset;

return {
absolute_time_duration:
preset !== 'reset' && preset !== 'this month' && preset !== 'last month'
? undefined
: { end: duration.end, start: duration.start },
entity_ids: resources
? entityIds.map((id) => parseInt(id, 10))
: widget.entity_ids.map((id) => parseInt(id, 10)),
entity_ids: getEntityIds(resources, entityIds, widget, serviceType),
filters: undefined,
group_by: !groupBy?.length ? undefined : groupBy,
relative_time_duration: getTimeDurationFromPreset(preset),
Expand All @@ -355,9 +370,31 @@ export const getCloudPulseMetricRequest = (
value: widget.time_granularity.value,
},
associated_entity_region: linodeRegion,
entity_region: serviceType === 'objectstorage' ? region : undefined,
};
};

/**
*
* @param resources list of CloudPulse resources
* @param entityIds list of entity ids
* @param widget widget
* @returns transformed entity ids
*/
export const getEntityIds = (
resources: CloudPulseResources[],
entityIds: string[],
widget: Widgets,
serviceType: CloudPulseServiceType
) => {
if (serviceType === 'objectstorage') {
return entityIds;
}
return resources
? entityIds.map((id) => parseInt(id, 10))
: widget.entity_ids.map((id) => parseInt(id, 10));
};

/**
*
* @returns generated label name for graph dimension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
filterBasedOnConfig,
filterEndpointsUsingRegion,
filterUsingDependentFilters,
getEndpointsProperties,
getFilters,
getTextFilterProperties,
} from './FilterBuilder';
Expand Down Expand Up @@ -46,6 +47,13 @@ const firewallConfig = FILTER_CONFIG.get(4);

const dbaasDashboard = dashboardFactory.build({ service_type: 'dbaas', id: 1 });

const objectStorageBucketDashboard = dashboardFactory.build({
service_type: 'objectstorage',
id: 6,
});

const objectStorageBucketConfig = FILTER_CONFIG.get(6);

it('test getRegionProperties method', () => {
const regionConfig = linodeConfig?.filters.find(
(filterObj) => filterObj.name === 'Region'
Expand Down Expand Up @@ -408,6 +416,46 @@ it('test getTextFilterProperties method for interface_id', () => {
}
});

it('test getEndpointsProperties method', () => {
const endpointsConfig = objectStorageBucketConfig?.filters.find(
(filterObj) => filterObj.name === 'Endpoints'
);

expect(endpointsConfig).toBeDefined();

if (endpointsConfig) {
const endpointsProperties = getEndpointsProperties(
{
config: endpointsConfig,
dashboard: objectStorageBucketDashboard,
dependentFilters: { region: 'us-east' },
isServiceAnalyticsIntegration: false,
},
vi.fn()
);
const {
label,
serviceType,
disabled,
savePreferences,
handleEndpointsSelection,
defaultValue,
region,
xFilter,
} = endpointsProperties;

expect(endpointsProperties).toBeDefined();
expect(label).toEqual(endpointsConfig.configuration.name);
expect(serviceType).toEqual('objectstorage');
expect(savePreferences).toEqual(true);
expect(disabled).toEqual(false);
expect(handleEndpointsSelection).toBeDefined();
expect(defaultValue).toEqual(undefined);
expect(region).toEqual('us-east');
expect(xFilter).toEqual({ region: 'us-east' });
}
});

it('test getFiltersForMetricsCallFromCustomSelect method', () => {
const result = getMetricsCallCustomFilters(
{
Expand Down
Loading