Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4cd5b39
Add beta ACLP alerts to Alerts tab on Linode details page
pmakode-akamai May 16, 2025
99cfdbd
Added changeset: Add beta ACLP contextual alerts to the Alerts tab on…
pmakode-akamai May 16, 2025
e896c4e
Rename legacy alerts heading to `Default Alerts`
pmakode-akamai May 19, 2025
f09962b
Merge branch 'develop' into M3-9989-add-beta-aclp-alerts-on-linode-de…
pmakode-akamai May 19, 2025
4c6d60a
Save progress...
pmakode-akamai May 19, 2025
c61d3ce
Fix aclp alerts validation schema
pmakode-akamai May 19, 2025
4024fe6
Support initial values for legacy alerts in createLinode flow
pmakode-akamai May 19, 2025
238bccb
Merge branch 'develop' into poc/M3-9996
pmakode-akamai May 20, 2025
a2c6464
Some clean up...
pmakode-akamai May 20, 2025
1d398af
Fix alerts validation schema
pmakode-akamai May 20, 2025
0f9dde1
Merge branch 'develop' into poc/M3-9996
pmakode-akamai May 20, 2025
26724f0
Merge branch 'develop' into poc/M3-9996
pmakode-akamai May 20, 2025
6c9ff39
Merge branch 'develop' into poc/M3-9996
pmakode-akamai May 21, 2025
258fc1c
Keep AlertsReusableComponent decoupled
pmakode-akamai May 21, 2025
cda86a8
Merge branch 'develop' into poc/M3-9996
pmakode-akamai Jun 3, 2025
f316d4a
Merge edit & create logic in action table (#1)
ankita-akamai Jun 3, 2025
12d116c
Add Additional Options section to Create Linode flow
pmakode-akamai Jun 3, 2025
656131a
Added changeset: Assigning alert definitions to a Linode during creation
pmakode-akamai Jun 3, 2025
f517604
Added changeset: Beta ACLP alerts property to the `CreateLinodeReques…
pmakode-akamai Jun 3, 2025
b38b95f
Clean up: types
pmakode-akamai Jun 3, 2025
7fd1131
Fix: Use relative type import in apiv4
pmakode-akamai Jun 3, 2025
90123d7
Resolve merge conflict and get latest from develop
pmakode-akamai Jun 10, 2025
43f6381
Update imports after merging the latest from develop
pmakode-akamai Jun 10, 2025
c112f1d
Resolve merge conflicts and get latest from develop
pmakode-akamai Jun 11, 2025
0d80d6d
Resolve merge conficts and document aclp alerts payload type
pmakode-akamai Jun 13, 2025
8950229
Update feature flag references
pmakode-akamai Jun 13, 2025
2b95ba8
Merge branch 'develop' into poc/M3-9996
pmakode-akamai Jun 16, 2025
c5c4196
Destruture linode create payload options
pmakode-akamai Jun 16, 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": Upcoming Features
---

Beta ACLP alerts property to the `CreateLinodeRequest` type ([#12248](https://github.com/linode/manager/pull/12248))
20 changes: 20 additions & 0 deletions packages/api-v4/src/cloudpulse/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,23 @@ export interface DeleteAlertPayload {
alertId: number;
serviceType: string;
}

/**
* Represents the payload for CloudPulse alerts, included only when the ACLP beta mode is enabled.
*
* In Beta mode, the `alerts` object contains enabled system and user alert IDs.
* - Legacy mode: `alerts` is not included (read-only mode).
* - Beta mode: `alerts` is passed and editable.
*/
export interface CloudPulseAlertsPayload {
/**
* Array of enabled system alert IDs in ACLP (Beta) mode.
* Only included in Beta mode.
*/
system: number[];
/**
* Array of enabled user alert IDs in ACLP (Beta) mode.
* Only included in Beta mode.
*/
user: number[];
}
5 changes: 5 additions & 0 deletions packages/api-v4/src/linodes/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { CloudPulseAlertsPayload } from '../cloudpulse/types';
import type { IPAddress, IPRange } from '../networking/types';
import type { LinodePlacementGroupPayload } from '../placement-groups/types';
import type { Region, RegionSite } from '../regions';
Expand Down Expand Up @@ -538,6 +539,10 @@ export interface CreateLinodePlacementGroupPayload {
}

export interface CreateLinodeRequest {
/**
* Beta Aclp alerts
*/
alerts?: CloudPulseAlertsPayload | null;
/**
* A list of public SSH keys that will be automatically appended to the root user’s
* `~/.ssh/authorized_keys`file when deploying from an Image.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Assigning alert definitions to a Linode during creation ([#12248](https://github.com/linode/manager/pull/12248))
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ import { getAPIErrorOrDefault } from 'src/utilities/errorUtils';
import { AlertConfirmationDialog } from '../AlertsLanding/AlertConfirmationDialog';
import { AlertInformationActionRow } from './AlertInformationActionRow';

import type { Alert, APIError, EntityAlertUpdatePayload } from '@linode/api-v4';
import type {
Alert,
APIError,
CloudPulseAlertsPayload,
EntityAlertUpdatePayload,
} from '@linode/api-v4';

export interface AlertInformationActionTableProps {
/**
Expand All @@ -36,19 +41,28 @@ export interface AlertInformationActionTableProps {

/**
* Id of the selected entity
* Only use in edit flow
*/
entityId: string;
entityId?: string;

/**
* Name of the selected entity
* Only use in edit flow
*/
entityName: string;
entityName?: string;

/**
* Error received from API
*/
error?: APIError[] | null;

/**
* Called when an alert is toggled on or off.
* Only use in create flow.
* @param payload enabled alerts ids
*/
onToggleAlert?: (payload: CloudPulseAlertsPayload) => void;

/**
* Column name by which columns will be ordered by default
*/
Expand All @@ -67,10 +81,37 @@ export interface TableColumnHeader {
label: string;
}

export interface AlertRowPropsOptions {
/**
* Enabled alerts payload
*/
enabledAlerts: CloudPulseAlertsPayload;

/**
* Id of the entity
* Only use in edit flow.
*/
entityId?: string;

/**
* Callback function to handle alert toggle
* Only use in create flow.
*/
onToggleAlert?: (payload: CloudPulseAlertsPayload) => void;
}

export const AlertInformationActionTable = (
props: AlertInformationActionTableProps
) => {
const { alerts, columns, entityId, entityName, error, orderByColumn } = props;
const {
alerts,
columns,
entityId,
entityName,
error,
orderByColumn,
onToggleAlert,
} = props;

const _error = error
? getAPIErrorOrDefault(error, 'Error while fetching the alerts')
Expand All @@ -79,16 +120,43 @@ export const AlertInformationActionTable = (
const [selectedAlert, setSelectedAlert] = React.useState<Alert>({} as Alert);
const [isDialogOpen, setIsDialogOpen] = React.useState<boolean>(false);
const [isLoading, setIsLoading] = React.useState<boolean>(false);
const [enabledAlerts, setEnabledAlerts] =
React.useState<CloudPulseAlertsPayload>({
system: [],
user: [],
});

const { mutateAsync: addEntity } = useAddEntityToAlert();

const { mutateAsync: removeEntity } = useRemoveEntityFromAlert();

const getAlertRowProps = (alert: Alert, options: AlertRowPropsOptions) => {
const { entityId, enabledAlerts, onToggleAlert } = options;

// Ensure that at least one of entityId or onToggleAlert is provided
if (!(entityId || onToggleAlert)) {
return null;
}

const isEditMode = !!entityId;

const handleToggle = isEditMode
? handleToggleEditFlow
: handleToggleCreateFlow;
const status = isEditMode
? alert.entity_ids.includes(entityId)
: enabledAlerts[alert.type].includes(alert.id);

return { handleToggle, status };
};

const handleCancel = () => {
setIsDialogOpen(false);
};
const handleConfirm = React.useCallback(
(alert: Alert, currentStatus: boolean) => {
if (entityId === undefined) return;

const payload: EntityAlertUpdatePayload = {
alert,
entityId,
Expand Down Expand Up @@ -117,12 +185,31 @@ export const AlertInformationActionTable = (
},
[addEntity, enqueueSnackbar, entityId, entityName, removeEntity]
);
const handleToggle = (alert: Alert) => {

const handleToggleEditFlow = (alert: Alert) => {
setIsDialogOpen(true);
setSelectedAlert(alert);
};

const isEnabled = selectedAlert.entity_ids?.includes(entityId) ?? false;
const handleToggleCreateFlow = (alert: Alert) => {
if (!onToggleAlert) return;

setEnabledAlerts((prev: CloudPulseAlertsPayload) => {
const newPayload = { ...prev };
const index = newPayload[alert.type].indexOf(alert.id);
// If the alert is already in the payload, remove it, otherwise add it
if (index !== -1) {
newPayload[alert.type].splice(index, 1);
} else {
newPayload[alert.type].push(alert.id);
}

onToggleAlert(newPayload);
return newPayload;
});
};

const isEnabled = selectedAlert.entity_ids?.includes(entityId ?? '') ?? false;

return (
<>
Expand Down Expand Up @@ -170,14 +257,24 @@ export const AlertInformationActionTable = (
length={paginatedAndOrderedAlerts.length}
loading={false}
/>
{paginatedAndOrderedAlerts?.map((alert) => (
<AlertInformationActionRow
alert={alert}
handleToggle={handleToggle}
key={alert.id}
status={alert.entity_ids.includes(entityId)}
/>
))}
{paginatedAndOrderedAlerts?.map((alert) => {
const rowProps = getAlertRowProps(alert, {
enabledAlerts,
entityId,
onToggleAlert,
});

if (!rowProps) return null;

return (
<AlertInformationActionRow
alert={alert}
handleToggle={rowProps.handleToggle}
key={alert.id}
status={rowProps.status}
/>
);
})}
</TableBody>
</Table>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,28 @@ import {
} from '../Utils/utils';
import { AlertInformationActionTable } from './AlertInformationActionTable';

import type { AlertDefinitionType } from '@linode/api-v4';
import type {
AlertDefinitionType,
CloudPulseAlertsPayload,
} from '@linode/api-v4';

interface AlertReusableComponentProps {
/**
* Id for the selected entity
*/
entityId: string;
entityId?: string;

/**
* Name of the selected entity
*/
entityName: string;
entityName?: string;

/**
* Called when an alert is toggled on or off.
* Only use in create flow.
* @param payload enabled alerts ids
*/
onToggleAlert?: (payload: CloudPulseAlertsPayload) => void;

/**
* Service type of selected entity
Expand All @@ -43,7 +53,7 @@ interface AlertReusableComponentProps {
}

export const AlertReusableComponent = (props: AlertReusableComponentProps) => {
const { entityId, entityName, serviceType } = props;
const { entityId, entityName, onToggleAlert, serviceType } = props;
const {
data: alerts,
error,
Expand All @@ -70,6 +80,7 @@ export const AlertReusableComponent = (props: AlertReusableComponentProps) => {
if (isLoading) {
return <CircleProgress />;
}

return (
<Paper>
<Stack gap={3}>
Expand Down Expand Up @@ -125,6 +136,7 @@ export const AlertReusableComponent = (props: AlertReusableComponentProps) => {
entityId={entityId}
entityName={entityName}
error={error}
onToggleAlert={onToggleAlert}
orderByColumn="Alert Name"
/>
</Stack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,9 @@ export const Actions = () => {
<ApiAwarenessModal
isOpen={isAPIAwarenessModalOpen}
onClose={() => setIsAPIAwarenessModalOpen(false)}
payLoad={getLinodeCreatePayload(
structuredClone(getValues()),
isLinodeInterfacesEnabled
)}
payLoad={getLinodeCreatePayload(structuredClone(getValues()), {
isShowingNewNetworkingUI: isLinodeInterfacesEnabled,
})}
/>
</Box>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
import { usePreferences } from '@linode/queries';
import { Accordion, BetaChip, Notice } from '@linode/ui';
import { Accordion, BetaChip } from '@linode/ui';
import * as React from 'react';
import { useController, useFormContext } from 'react-hook-form';

import { AlertReusableComponent } from 'src/features/CloudPulse/Alerts/ContextualView/AlertReusableComponent';
import { AclpPreferenceToggle } from 'src/features/Linodes/AclpPreferenceToggle';
import { LinodeSettingsAlertsPanel } from 'src/features/Linodes/LinodesDetail/LinodeSettings/LinodeSettingsAlertsPanel';
import { useFlags } from 'src/hooks/useFlags';

import type { LinodeCreateFormValues } from '../../utilities';
import type { CloudPulseAlertsPayload } from '@linode/api-v4';

export const Alerts = () => {
const flags = useFlags();
const { data: isAclpAlertsPreferenceBeta } = usePreferences(
(preferences) => preferences?.isAclpAlertsBeta
);

const { control } = useFormContext<LinodeCreateFormValues>();
const { field } = useController({
control,
name: 'alerts',
defaultValue: { system: [], user: [] },
});

const handleToggleAlert = (updatedAlerts: CloudPulseAlertsPayload) => {
field.onChange(updatedAlerts);
};

return (
<Accordion
detailProps={{ sx: { p: 0 } }}
Expand All @@ -26,7 +42,10 @@ export const Alerts = () => {
>
{flags.aclpBetaServices?.alerts && <AclpPreferenceToggle type="alerts" />}
{flags.aclpBetaServices?.alerts && isAclpAlertsPreferenceBeta ? (
<Notice variant="info">ACLP Alerts coming soon...</Notice>
<AlertReusableComponent
onToggleAlert={handleToggleAlert}
serviceType="linode"
/>
) : (
<LinodeSettingsAlertsPanel />
)}
Expand Down
Loading