2023-01-27 03:22:44 -08:00
|
|
|
import type { IExecuteFunctions, ILoadOptionsFunctions } from 'n8n-core';
|
|
|
|
import type { IDataObject, INodeListSearchItems, INodePropertyOptions } from 'n8n-workflow';
|
|
|
|
import { NodeOperationError } from 'n8n-workflow';
|
2023-01-20 07:00:47 -08:00
|
|
|
import { DateTime } from 'luxon';
|
|
|
|
|
|
|
|
// tslint:disable-next-line:no-any
|
|
|
|
export function simplify(responseData: any | [any]) {
|
|
|
|
const returnData = [];
|
|
|
|
for (const {
|
|
|
|
columnHeader: { dimensions, metricHeader },
|
|
|
|
data: { rows },
|
|
|
|
} of responseData) {
|
|
|
|
if (rows === undefined) {
|
|
|
|
// Do not error if there is no data
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const metrics = metricHeader.metricHeaderEntries.map((entry: { name: string }) => entry.name);
|
|
|
|
for (const row of rows) {
|
|
|
|
const rowDimensions: IDataObject = {};
|
|
|
|
const rowMetrics: IDataObject = {};
|
|
|
|
if (dimensions) {
|
|
|
|
for (let i = 0; i < dimensions.length; i++) {
|
|
|
|
rowDimensions[dimensions[i]] = row.dimensions[i];
|
|
|
|
for (const [index, metric] of metrics.entries()) {
|
|
|
|
rowMetrics[metric] = row.metrics[0].values[index];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (const [index, metric] of metrics.entries()) {
|
|
|
|
rowMetrics[metric] = row.metrics[0].values[index];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
returnData.push({ ...rowDimensions, ...rowMetrics });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return returnData;
|
|
|
|
}
|
|
|
|
|
|
|
|
// tslint:disable-next-line:no-any
|
|
|
|
export function merge(responseData: [any]) {
|
|
|
|
const response: { columnHeader: IDataObject; data: { rows: [] } } = {
|
|
|
|
columnHeader: responseData[0].columnHeader,
|
|
|
|
data: responseData[0].data,
|
|
|
|
};
|
|
|
|
const allRows = [];
|
|
|
|
for (const {
|
|
|
|
data: { rows },
|
|
|
|
} of responseData) {
|
|
|
|
allRows.push(...rows);
|
|
|
|
}
|
|
|
|
response.data.rows = allRows as [];
|
|
|
|
return [response];
|
|
|
|
}
|
|
|
|
|
|
|
|
export function simplifyGA4(response: IDataObject) {
|
|
|
|
if (!response.rows) return [];
|
|
|
|
const dimensionHeaders = ((response.dimensionHeaders as IDataObject[]) || []).map(
|
|
|
|
(header) => header.name as string,
|
|
|
|
);
|
|
|
|
const metricHeaders = ((response.metricHeaders as IDataObject[]) || []).map(
|
|
|
|
(header) => header.name as string,
|
|
|
|
);
|
|
|
|
const returnData: IDataObject[] = [];
|
|
|
|
|
|
|
|
(response.rows as IDataObject[]).forEach((row) => {
|
|
|
|
if (!row) return;
|
|
|
|
const rowDimensions: IDataObject = {};
|
|
|
|
const rowMetrics: IDataObject = {};
|
|
|
|
dimensionHeaders.forEach((dimension, index) => {
|
|
|
|
rowDimensions[dimension] = (row.dimensionValues as IDataObject[])[index].value;
|
|
|
|
});
|
|
|
|
metricHeaders.forEach((metric, index) => {
|
|
|
|
rowMetrics[metric] = (row.metricValues as IDataObject[])[index].value;
|
|
|
|
});
|
|
|
|
returnData.push({ ...rowDimensions, ...rowMetrics });
|
|
|
|
});
|
|
|
|
|
|
|
|
return returnData;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function processFilters(expression: IDataObject): IDataObject[] {
|
|
|
|
const processedFilters: IDataObject[] = [];
|
|
|
|
|
|
|
|
Object.entries(expression).forEach((entry) => {
|
|
|
|
const [filterType, filters] = entry;
|
|
|
|
|
|
|
|
(filters as IDataObject[]).forEach((filter) => {
|
|
|
|
let fieldName = '';
|
|
|
|
switch (filter.listName) {
|
|
|
|
case 'other':
|
|
|
|
fieldName = filter.name as string;
|
|
|
|
delete filter.name;
|
|
|
|
break;
|
|
|
|
case 'custom':
|
|
|
|
fieldName = filter.name as string;
|
|
|
|
delete filter.name;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fieldName = filter.listName as string;
|
|
|
|
}
|
|
|
|
delete filter.listName;
|
|
|
|
|
|
|
|
if (filterType === 'inListFilter') {
|
|
|
|
filter.values = (filter.values as string).split(',');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filterType === 'numericFilter') {
|
|
|
|
filter.value = {
|
|
|
|
[filter.valueType as string]: filter.value,
|
|
|
|
};
|
|
|
|
delete filter.valueType;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filterType === 'betweenFilter') {
|
|
|
|
filter.fromValue = {
|
|
|
|
[filter.valueType as string]: filter.fromValue,
|
|
|
|
};
|
|
|
|
filter.toValue = {
|
|
|
|
[filter.valueType as string]: filter.toValue,
|
|
|
|
};
|
|
|
|
delete filter.valueType;
|
|
|
|
}
|
|
|
|
|
|
|
|
processedFilters.push({
|
|
|
|
filter: {
|
|
|
|
fieldName,
|
|
|
|
[filterType]: filter,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return processedFilters;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function prepareDateRange(
|
|
|
|
this: IExecuteFunctions | ILoadOptionsFunctions,
|
|
|
|
period: string,
|
|
|
|
itemIndex: number,
|
|
|
|
) {
|
|
|
|
const dateRanges: IDataObject[] = [];
|
|
|
|
|
|
|
|
switch (period) {
|
|
|
|
case 'today':
|
|
|
|
dateRanges.push({
|
|
|
|
startDate: DateTime.local().startOf('day').toISODate(),
|
|
|
|
endDate: DateTime.now().toISODate(),
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'yesterday':
|
|
|
|
dateRanges.push({
|
|
|
|
startDate: DateTime.local().startOf('day').minus({ days: 1 }).toISODate(),
|
|
|
|
endDate: DateTime.local().endOf('day').minus({ days: 1 }).toISODate(),
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'lastCalendarWeek':
|
|
|
|
const begginingOfLastWeek = DateTime.local().startOf('week').minus({ weeks: 1 }).toISODate();
|
|
|
|
const endOfLastWeek = DateTime.local().endOf('week').minus({ weeks: 1 }).toISODate();
|
|
|
|
dateRanges.push({
|
|
|
|
startDate: begginingOfLastWeek,
|
|
|
|
endDate: endOfLastWeek,
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'lastCalendarMonth':
|
|
|
|
const begginingOfLastMonth = DateTime.local()
|
|
|
|
.startOf('month')
|
|
|
|
.minus({ months: 1 })
|
|
|
|
.toISODate();
|
|
|
|
const endOfLastMonth = DateTime.local().endOf('month').minus({ months: 1 }).toISODate();
|
|
|
|
dateRanges.push({
|
|
|
|
startDate: begginingOfLastMonth,
|
|
|
|
endDate: endOfLastMonth,
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'last7days':
|
|
|
|
dateRanges.push({
|
|
|
|
startDate: DateTime.now().minus({ days: 7 }).toISODate(),
|
|
|
|
endDate: DateTime.now().toISODate(),
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'last30days':
|
|
|
|
dateRanges.push({
|
|
|
|
startDate: DateTime.now().minus({ days: 30 }).toISODate(),
|
|
|
|
endDate: DateTime.now().toISODate(),
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'custom':
|
|
|
|
const start = DateTime.fromISO(this.getNodeParameter('startDate', itemIndex, '') as string);
|
|
|
|
const end = DateTime.fromISO(this.getNodeParameter('endDate', itemIndex, '') as string);
|
|
|
|
|
|
|
|
if (start > end) {
|
|
|
|
throw new NodeOperationError(
|
|
|
|
this.getNode(),
|
|
|
|
`Parameter Start: ${start.toISO()} cannot be after End: ${end.toISO()}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
dateRanges.push({
|
|
|
|
startDate: start.toISODate(),
|
|
|
|
endDate: end.toISODate(),
|
|
|
|
});
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new NodeOperationError(
|
|
|
|
this.getNode(),
|
|
|
|
`The period '${period}' is not supported, to specify own period use 'custom' option`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dateRanges;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const defaultStartDate = () => DateTime.now().startOf('day').minus({ days: 8 }).toISO();
|
|
|
|
|
|
|
|
export const defaultEndDate = () => DateTime.now().startOf('day').minus({ days: 1 }).toISO();
|
|
|
|
|
|
|
|
export function checkDuplicates(
|
|
|
|
this: IExecuteFunctions,
|
|
|
|
data: IDataObject[],
|
|
|
|
key: string,
|
|
|
|
type: string,
|
|
|
|
) {
|
|
|
|
const fields = data.map((item) => item[key] as string);
|
|
|
|
const duplicates = fields.filter((field, i) => fields.indexOf(field) !== i);
|
|
|
|
const unique = Array.from(new Set(duplicates));
|
|
|
|
if (unique.length) {
|
|
|
|
throw new NodeOperationError(
|
|
|
|
this.getNode(),
|
|
|
|
`A ${type} is specified more than once (${unique.join(', ')})`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function sortLoadOptions(data: INodePropertyOptions[] | INodeListSearchItems[]) {
|
|
|
|
const returnData = [...data];
|
|
|
|
returnData.sort((a, b) => {
|
|
|
|
const aName = a.name.toLowerCase();
|
|
|
|
const bName = b.name.toLowerCase();
|
|
|
|
if (aName < bName) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (aName > bName) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
return returnData;
|
|
|
|
}
|