Skip to content
Merged
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
8 changes: 7 additions & 1 deletion nodes/CoveDataProtection/CoveDataProtection.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,13 @@ export class CoveDataProtection implements INodeType {
}
}

return partners.sort((a, b) => a.name.localeCompare(b.name));
partners.sort((a, b) => a.name.localeCompare(b.name));
partners.unshift({
name: 'All Partners (Top Level)',
value: parentPartnerId,
});

return partners;
},
},
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { INodeProperties } from 'n8n-workflow';

export const getBackupStatisticsDescription: INodeProperties[] = [
{
displayName: 'Partner Name or ID',
name: 'partnerId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getPartners',
},
displayOptions: {
show: {
resource: ['accounts'],
operation: ['getBackupStatistics'],
},
},
default: '',
description: 'The partner to enumerate device backup statistics for. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
required: true,
},
{
displayName: 'Data Sources',
name: 'dataSources',
type: 'multiOptions',
displayOptions: {
show: {
resource: ['accounts'],
operation: ['getBackupStatistics'],
},
},
options: [
{ name: 'Bare Metal Restore (D17)', value: 'D17' },
{ name: 'Files and Folders (D01)', value: 'D01' },
{ name: 'Hyper-V (D14)', value: 'D14' },
{ name: 'Microsoft 365 Exchange (D19)', value: 'D19' },
{ name: 'Microsoft 365 OneDrive (D20)', value: 'D20' },
{ name: 'Microsoft 365 SharePoint (D05)', value: 'D05' },
{ name: 'Microsoft 365 Teams (D23)', value: 'D23' },
{ name: 'MySql (D15)', value: 'D15' },
{ name: 'Network Shares (D06)', value: 'D06' },
{ name: 'Oracle (D12)', value: 'D12' },
{ name: 'System State (D02)', value: 'D02' },
{ name: 'Total (D09)', value: 'D09' },
{ name: 'Virtual Disaster Recovery (D16)', value: 'D16' },
{ name: 'VMware Virtual Machines (D08)', value: 'D08' },
{ name: 'VssExchange (D04)', value: 'D04' },
{ name: 'VssMsSql (D10)', value: 'D10' },
{ name: 'VssSharePoint (D11)', value: 'D11' },
{ name: 'VssSystemState (D07)', value: 'D07' },
],
default: ['D01', 'D02'],
description: 'Which data sources to retrieve backup statistics for',
},
{
displayName: 'Fields',
name: 'fields',
type: 'multiOptions',
displayOptions: {
show: {
resource: ['accounts'],
operation: ['getBackupStatistics'],
},
},
options: [
{ name: 'Last Session Status (F00)', value: 'F00' },
{ name: 'Last Session Timestamp (F15)', value: 'F15' },
{ name: 'Last Successful Session Status (F16)', value: 'F16' },
{ name: 'Last Successful Session Timestamp (F09)', value: 'F09' },
{ name: 'Last Completed Session Status (F17)', value: 'F17' },
{ name: 'Last Completed Session Timestamp (F18)', value: 'F18' },
],
default: ['F00', 'F15'],
description: 'Which backup statistics fields to retrieve for each selected data source',
},
{
displayName: 'Additional Options',
name: 'additionalOptions',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
resource: ['accounts'],
operation: ['getBackupStatistics'],
},
},
options: [
{
displayName: 'Filter',
name: 'filter',
type: 'string',
default: '',
description: 'Filter expression (e.g., "ANY =~ "Device*"")',
},
{
displayName: 'Selection Mode',
name: 'selectionMode',
type: 'options',
options: [
{
name: 'Merged',
value: 'Merged',
},
{
name: 'Detailed (Per Installation)',
value: 'PerInstallation',
},
],
default: 'Merged',
description: 'Selection mode for statistics',
},
],
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['accounts'],
operation: ['getBackupStatistics'],
},
},
default: true,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['accounts'],
operation: ['getBackupStatistics'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
},
default: 50,
description: 'Max number of results to return',
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { IExecuteFunctions, IDataObject } from 'n8n-workflow';
import { jsonRpcRequest } from '../../../transport';

const SESSION_STATUS_LABELS: Record<string, string> = {
'1': 'In process',
'2': 'Failed',
'3': 'Aborted',
'5': 'Completed',
'6': 'Interrupted',
'7': 'NotStarted',
'8': 'CompletedWithErrors',
'9': 'InProgressWithFaults',
'10': 'OverQuota',
'11': 'NoSelection',
'12': 'Restarted',
};

const FIELD_CODE_NAMES: Record<string, string> = {
F00: 'lastSessionStatus',
F09: 'lastSuccessfulSessionTimestamp',
F15: 'lastSessionTimestamp',
F16: 'lastSuccessfulSessionStatus',
F17: 'lastCompletedSessionStatus',
F18: 'lastCompletedSessionTimestamp',
};

const STATUS_FIELD_CODES = new Set(['F00', 'F16', 'F17']);

export async function execute(this: IExecuteFunctions, index: number): Promise<IDataObject[]> {
const partnerId = this.getNodeParameter('partnerId', index) as number;
const dataSources = this.getNodeParameter('dataSources', index, []) as string[];
const fields = this.getNodeParameter('fields', index, []) as string[];
const additionalOptions = this.getNodeParameter('additionalOptions', index, {}) as IDataObject;
const returnAll = this.getNodeParameter('returnAll', index, true) as boolean;
const limit = returnAll ? 0 : (this.getNodeParameter('limit', index, 50) as number);

const dataSourceArray = dataSources.length > 0 ? dataSources : ['D01', 'D02'];
const fieldArray = fields.length > 0 ? fields : ['F00', 'F15'];

const backupColumns = dataSourceArray.flatMap((dataSource) =>
fieldArray.map((field) => `${dataSource}${field}`),
);

const columnArray = ['I0', 'I1', 'I18', 'I78', ...backupColumns];

const query: IDataObject = {
PartnerId: partnerId,
Columns: columnArray,
SelectionMode: additionalOptions.selectionMode || 'Merged',
StartRecordNumber: 0,
RecordsCount: returnAll ? 9999999 : limit,
};

if (additionalOptions.filter) {
query.Filter = additionalOptions.filter;
}

const params: IDataObject = {
query,
};

const result = await jsonRpcRequest.call(this, 'EnumerateAccountStatistics', params);

if (result && result.result && Array.isArray(result.result)) {
return result.result.map((item: any) => {
const normalized: IDataObject = {
accountId: item.AccountId,
partnerId: item.PartnerId,
flags: item.Flags,
backupStatistics: {},
};

const backupStatistics = normalized.backupStatistics as IDataObject;

if (item.Settings) {
item.Settings.forEach((setting: any) => {
Object.keys(setting).forEach((columnCode) => {
const value = setting[columnCode];

if (columnCode === 'I0') {
normalized.deviceId = value;
return;
}
if (columnCode === 'I1') {
normalized.deviceName = value;
return;
}
if (columnCode === 'I18') {
normalized.computerName = value;
return;
}
if (columnCode === 'I78') {
normalized.activeDataSources = (value as string).match(/.{1,3}/g) || [];
return;
}

const match = columnCode.match(/^(D\d{2})(F\d{2})$/);
if (match) {
const [, dataSourceCode, fieldCode] = match;
const fieldName = FIELD_CODE_NAMES[fieldCode] || fieldCode;

const dataSourceStats = (backupStatistics[dataSourceCode] ||
(backupStatistics[dataSourceCode] = {})) as IDataObject;

dataSourceStats[fieldName] = value;
if (STATUS_FIELD_CODES.has(fieldCode)) {
dataSourceStats[`${fieldName}Label`] = SESSION_STATUS_LABELS[value as string] || value;
}
}
});
});
}

return normalized;
});
}

return [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { execute } from './execute';
import { getBackupStatisticsDescription as description } from './description';

export { description, execute };
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ export const getStatisticsDescription: INodeProperties[] = [
value: 'Merged',
},
{
name: 'Detailed',
value: 'Detailed',
name: 'Detailed (Per Installation)',
value: 'PerInstallation',
},
],
default: 'Merged',
Expand Down
10 changes: 9 additions & 1 deletion nodes/CoveDataProtection/actions/accounts/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as getAll from './getAll';
import * as getById from './getById';
import * as getStatistics from './getStatistics';
import * as getBackupStatistics from './getBackupStatistics';
import * as create from './create';
import * as update from './update';
import * as deleteAccount from './delete';
import { INodeProperties } from 'n8n-workflow';

export { getAll, getById, getStatistics, create, update, deleteAccount as delete };
export { getAll, getById, getStatistics, getBackupStatistics, create, update, deleteAccount as delete };

export const description: INodeProperties[] = [
{
Expand All @@ -32,6 +33,12 @@ export const description: INodeProperties[] = [
description: 'Delete an account',
action: 'Delete an account',
},
{
name: 'Get Backup Statistics',
value: 'getBackupStatistics',
description: 'Enumerate backup statistics (most recent backup date and status per data source) for devices',
action: 'Get account backup statistics',
},
{
name: 'Get by ID',
value: 'getById',
Expand Down Expand Up @@ -62,6 +69,7 @@ export const description: INodeProperties[] = [
...getAll.description,
...getById.description,
...getStatistics.description,
...getBackupStatistics.description,
...create.description,
...update.description,
...deleteAccount.description,
Expand Down
Loading