mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-15 00:54:06 -08:00
193 lines
4.9 KiB
TypeScript
193 lines
4.9 KiB
TypeScript
|
import {
|
||
|
NodeApiError,
|
||
|
NodeConnectionType,
|
||
|
type IPollFunctions,
|
||
|
type IDataObject,
|
||
|
type INodeExecutionData,
|
||
|
type INodeType,
|
||
|
type INodeTypeDescription,
|
||
|
} from 'n8n-workflow';
|
||
|
|
||
|
import { googleApiRequest, searchAccounts, searchLocations } from './GenericFunctions';
|
||
|
|
||
|
export class GoogleMyBusinessTrigger implements INodeType {
|
||
|
description: INodeTypeDescription = {
|
||
|
displayName: 'Google My Business Trigger',
|
||
|
name: 'googleMyBusinessTrigger',
|
||
|
icon: 'file:googleMyBusines.svg',
|
||
|
group: ['trigger'],
|
||
|
version: 1,
|
||
|
description:
|
||
|
'Fetches reviews from Google My Business and starts the workflow on specified polling intervals.',
|
||
|
subtitle: '={{"Google My Business Trigger"}}',
|
||
|
defaults: {
|
||
|
name: 'Google My Business Trigger',
|
||
|
},
|
||
|
credentials: [
|
||
|
{
|
||
|
name: 'googleMyBusinessOAuth2Api',
|
||
|
required: true,
|
||
|
},
|
||
|
],
|
||
|
polling: true,
|
||
|
inputs: [],
|
||
|
outputs: [NodeConnectionType.Main],
|
||
|
properties: [
|
||
|
{
|
||
|
displayName: 'Event',
|
||
|
name: 'event',
|
||
|
required: true,
|
||
|
type: 'options',
|
||
|
noDataExpression: true,
|
||
|
default: 'reviewAdded',
|
||
|
options: [
|
||
|
{
|
||
|
name: 'Review Added',
|
||
|
value: 'reviewAdded',
|
||
|
},
|
||
|
],
|
||
|
},
|
||
|
{
|
||
|
displayName: 'Account',
|
||
|
name: 'account',
|
||
|
required: true,
|
||
|
type: 'resourceLocator',
|
||
|
default: { mode: 'list', value: '' },
|
||
|
description: 'The Google My Business account',
|
||
|
displayOptions: { show: { event: ['reviewAdded'] } },
|
||
|
modes: [
|
||
|
{
|
||
|
displayName: 'From list',
|
||
|
name: 'list',
|
||
|
type: 'list',
|
||
|
typeOptions: {
|
||
|
searchListMethod: 'searchAccounts',
|
||
|
searchable: true,
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
displayName: 'By name',
|
||
|
name: 'name',
|
||
|
type: 'string',
|
||
|
hint: 'Enter the account name',
|
||
|
validation: [
|
||
|
{
|
||
|
type: 'regex',
|
||
|
properties: {
|
||
|
regex: 'accounts/[0-9]+',
|
||
|
errorMessage: 'The name must start with "accounts/"',
|
||
|
},
|
||
|
},
|
||
|
],
|
||
|
placeholder: 'e.g. accounts/0123456789',
|
||
|
},
|
||
|
],
|
||
|
},
|
||
|
{
|
||
|
displayName: 'Location',
|
||
|
name: 'location',
|
||
|
required: true,
|
||
|
type: 'resourceLocator',
|
||
|
default: { mode: 'list', value: '' },
|
||
|
description: 'The specific location or business associated with the account',
|
||
|
displayOptions: { show: { event: ['reviewAdded'] } },
|
||
|
modes: [
|
||
|
{
|
||
|
displayName: 'From list',
|
||
|
name: 'list',
|
||
|
type: 'list',
|
||
|
typeOptions: {
|
||
|
searchListMethod: 'searchLocations',
|
||
|
searchable: true,
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
displayName: 'By name',
|
||
|
name: 'name',
|
||
|
type: 'string',
|
||
|
hint: 'Enter the location name',
|
||
|
validation: [
|
||
|
{
|
||
|
type: 'regex',
|
||
|
properties: {
|
||
|
regex: 'locations/[0-9]+',
|
||
|
errorMessage: 'The name must start with "locations/"',
|
||
|
},
|
||
|
},
|
||
|
],
|
||
|
placeholder: 'e.g. locations/0123456789',
|
||
|
},
|
||
|
],
|
||
|
},
|
||
|
],
|
||
|
};
|
||
|
|
||
|
methods = {
|
||
|
listSearch: {
|
||
|
searchAccounts,
|
||
|
searchLocations,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
async poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null> {
|
||
|
const nodeStaticData = this.getWorkflowStaticData('node');
|
||
|
let responseData;
|
||
|
const qs: IDataObject = {};
|
||
|
|
||
|
const account = (this.getNodeParameter('account') as { value: string; mode: string }).value;
|
||
|
const location = (this.getNodeParameter('location') as { value: string; mode: string }).value;
|
||
|
|
||
|
const manualMode = this.getMode() === 'manual';
|
||
|
if (manualMode) {
|
||
|
qs.pageSize = 1; // In manual mode we only want to fetch the latest review
|
||
|
} else {
|
||
|
qs.pageSize = 50; // Maximal page size for the get reviews endpoint
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
responseData = (await googleApiRequest.call(
|
||
|
this,
|
||
|
'GET',
|
||
|
`/${account}/${location}/reviews`,
|
||
|
{},
|
||
|
qs,
|
||
|
)) as { reviews: IDataObject[]; totalReviewCount: number; nextPageToken?: string };
|
||
|
|
||
|
if (manualMode) {
|
||
|
responseData = responseData.reviews;
|
||
|
} else {
|
||
|
// During the first execution there is no delta
|
||
|
if (!nodeStaticData.totalReviewCountLastTimeChecked) {
|
||
|
nodeStaticData.totalReviewCountLastTimeChecked = responseData.totalReviewCount;
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
// When count did't change the node shouldn't trigger
|
||
|
if (
|
||
|
!responseData?.reviews?.length ||
|
||
|
nodeStaticData?.totalReviewCountLastTimeChecked === responseData?.totalReviewCount
|
||
|
) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
const numNewReviews =
|
||
|
// @ts-ignore
|
||
|
responseData.totalReviewCount - nodeStaticData.totalReviewCountLastTimeChecked;
|
||
|
nodeStaticData.totalReviewCountLastTimeChecked = responseData.totalReviewCount;
|
||
|
|
||
|
// By default the reviews will be sorted by updateTime in descending order
|
||
|
// Return only the delta reviews since last pooling
|
||
|
responseData = responseData.reviews.slice(0, numNewReviews);
|
||
|
}
|
||
|
|
||
|
if (Array.isArray(responseData) && responseData.length) {
|
||
|
return [this.helpers.returnJsonArray(responseData)];
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
} catch (error) {
|
||
|
throw new NodeApiError(this.getNode(), error);
|
||
|
}
|
||
|
}
|
||
|
}
|