first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
import get from 'lodash/get';
|
||||
import type {
|
||||
DeclarativeRestApiSettings,
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
IExecutePaginationFunctions,
|
||||
IExecuteSingleFunctions,
|
||||
IHttpRequestMethods,
|
||||
IHttpRequestOptions,
|
||||
ILoadOptionsFunctions,
|
||||
IN8nHttpFullResponse,
|
||||
INodeExecutionData,
|
||||
JsonObject,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
export async function gongApiRequest(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
body: IDataObject = {},
|
||||
query: IDataObject = {},
|
||||
) {
|
||||
const authentication = this.getNodeParameter('authentication', 0) as 'accessToken' | 'oAuth2';
|
||||
const credentialsType = authentication === 'oAuth2' ? 'gongOAuth2Api' : 'gongApi';
|
||||
const { baseUrl } = await this.getCredentials<{
|
||||
baseUrl: string;
|
||||
}>(credentialsType);
|
||||
|
||||
const options: IHttpRequestOptions = {
|
||||
method,
|
||||
url: baseUrl.replace(new RegExp('/$'), '') + endpoint,
|
||||
json: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body,
|
||||
qs: query,
|
||||
};
|
||||
|
||||
if (Object.keys(body).length === 0) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
return await this.helpers.requestWithAuthentication.call(this, credentialsType, options);
|
||||
}
|
||||
|
||||
export async function gongApiPaginateRequest(
|
||||
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
body: IDataObject = {},
|
||||
query: IDataObject = {},
|
||||
itemIndex: number = 0,
|
||||
rootProperty: string | undefined = undefined,
|
||||
): Promise<any> {
|
||||
const authentication = this.getNodeParameter('authentication', 0) as 'accessToken' | 'oAuth2';
|
||||
const credentialsType = authentication === 'oAuth2' ? 'gongOAuth2Api' : 'gongApi';
|
||||
const { baseUrl } = await this.getCredentials<{
|
||||
baseUrl: string;
|
||||
}>(credentialsType);
|
||||
|
||||
const options: IHttpRequestOptions = {
|
||||
method,
|
||||
url: baseUrl.replace(new RegExp('/$'), '') + endpoint,
|
||||
json: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body,
|
||||
qs: query,
|
||||
};
|
||||
|
||||
if (Object.keys(body).length === 0) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
const pages = await this.helpers.requestWithAuthenticationPaginated.call(
|
||||
this,
|
||||
options,
|
||||
itemIndex,
|
||||
{
|
||||
requestInterval: 340, // Rate limit 3 calls per second
|
||||
continue: '={{ $response.body.records.cursor }}',
|
||||
request: {
|
||||
[method === 'POST' ? 'body' : 'qs']:
|
||||
'={{ $if($response.body?.records.cursor, { cursor: $response.body.records.cursor }, {}) }}',
|
||||
url: options.url,
|
||||
},
|
||||
},
|
||||
credentialsType,
|
||||
);
|
||||
|
||||
if (rootProperty) {
|
||||
let results: IDataObject[] = [];
|
||||
for (const page of pages) {
|
||||
const items = page.body[rootProperty];
|
||||
if (items) {
|
||||
results = results.concat(items);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
} else {
|
||||
return pages.flat();
|
||||
}
|
||||
}
|
||||
|
||||
const getCursorPaginator = (
|
||||
extractItems: (items: INodeExecutionData[]) => INodeExecutionData[],
|
||||
) => {
|
||||
return async function cursorPagination(
|
||||
this: IExecutePaginationFunctions,
|
||||
requestOptions: DeclarativeRestApiSettings.ResultOptions,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
let executions: INodeExecutionData[] = [];
|
||||
let responseData: INodeExecutionData[];
|
||||
let nextCursor: string | undefined = undefined;
|
||||
const returnAll = this.getNodeParameter('returnAll', true) as boolean;
|
||||
|
||||
do {
|
||||
(requestOptions.options.body as IDataObject).cursor = nextCursor;
|
||||
responseData = await this.makeRoutingRequest(requestOptions);
|
||||
const lastItem = responseData[responseData.length - 1].json;
|
||||
nextCursor = (lastItem.records as IDataObject)?.cursor as string | undefined;
|
||||
executions = executions.concat(extractItems(responseData));
|
||||
} while (returnAll && nextCursor);
|
||||
|
||||
return executions;
|
||||
};
|
||||
};
|
||||
|
||||
export const extractCalls = (items: INodeExecutionData[]): INodeExecutionData[] => {
|
||||
const calls: IDataObject[] = items.flatMap((item) => get(item.json, 'calls') as IDataObject[]);
|
||||
return calls.map((call) => {
|
||||
const { metaData, ...rest } = call ?? {};
|
||||
return { json: { ...(metaData as IDataObject), ...rest } };
|
||||
});
|
||||
};
|
||||
|
||||
export const extractUsers = (items: INodeExecutionData[]): INodeExecutionData[] => {
|
||||
const users: IDataObject[] = items.flatMap((item) => get(item.json, 'users') as IDataObject[]);
|
||||
return users.map((user) => ({ json: user }));
|
||||
};
|
||||
|
||||
export const getCursorPaginatorCalls = () => {
|
||||
return getCursorPaginator(extractCalls);
|
||||
};
|
||||
|
||||
export const getCursorPaginatorUsers = () => {
|
||||
return getCursorPaginator(extractUsers);
|
||||
};
|
||||
|
||||
export async function handleErrorPostReceive(
|
||||
this: IExecuteSingleFunctions,
|
||||
data: INodeExecutionData[],
|
||||
response: IN8nHttpFullResponse,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
if (String(response.statusCode).startsWith('4') || String(response.statusCode).startsWith('5')) {
|
||||
const { resource, operation } = this.getNode().parameters;
|
||||
|
||||
if (resource === 'call') {
|
||||
if (operation === 'get') {
|
||||
if (response.statusCode === 404) {
|
||||
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
|
||||
message: "The required call doesn't match any existing one",
|
||||
description: "Double-check the value in the parameter 'Call to Get' and try again",
|
||||
});
|
||||
}
|
||||
} else if (operation === 'getAll') {
|
||||
if (response.statusCode === 404) {
|
||||
const primaryUserId = this.getNodeParameter('filters.primaryUserIds', {}) as IDataObject;
|
||||
if (Object.keys(primaryUserId).length !== 0) {
|
||||
return [{ json: {} }];
|
||||
}
|
||||
} else if (response.statusCode === 400 || response.statusCode === 500) {
|
||||
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
|
||||
description: 'Double-check the value(s) in the parameter(s)',
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (resource === 'user') {
|
||||
if (operation === 'get') {
|
||||
if (response.statusCode === 404) {
|
||||
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
|
||||
message: "The required user doesn't match any existing one",
|
||||
description: "Double-check the value in the parameter 'User to Get' and try again",
|
||||
});
|
||||
}
|
||||
} else if (operation === 'getAll') {
|
||||
if (response.statusCode === 404) {
|
||||
const userIds = this.getNodeParameter('filters.userIds', '') as string;
|
||||
if (userIds) {
|
||||
throw new NodeApiError(this.getNode(), response as unknown as JsonObject, {
|
||||
message: "The Users IDs don't match any existing user",
|
||||
description: "Double-check the values in the parameter 'Users IDs' and try again",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new NodeApiError(this.getNode(), response as unknown as JsonObject);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export function isValidNumberIds(value: number | number[] | string | string[]): boolean {
|
||||
if (typeof value === 'number') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(value) && value.every((item) => typeof item === 'number')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
const parts = value.split(',');
|
||||
return parts.every((part) => !isNaN(Number(part.trim())));
|
||||
}
|
||||
|
||||
if (Array.isArray(value) && value.every((item) => typeof item === 'string')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.gong",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Development", "Developer Tools"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.gong/"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.gong/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
import {
|
||||
NodeConnectionTypes,
|
||||
type IDataObject,
|
||||
type ILoadOptionsFunctions,
|
||||
type INodeListSearchItems,
|
||||
type INodeListSearchResult,
|
||||
type INodeType,
|
||||
type INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { callFields, callOperations, userFields, userOperations } from './descriptions';
|
||||
import { gongApiRequest } from './GenericFunctions';
|
||||
|
||||
export class Gong implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Gong',
|
||||
name: 'gong',
|
||||
icon: 'file:gong.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Interact with Gong API',
|
||||
defaults: {
|
||||
name: 'Gong',
|
||||
},
|
||||
usableAsTool: true,
|
||||
inputs: [NodeConnectionTypes.Main],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'gongApi',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
authentication: ['accessToken'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'gongOAuth2Api',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
authentication: ['oAuth2'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
requestDefaults: {
|
||||
baseURL: '={{ $credentials.baseUrl.replace(new RegExp("/$"), "") }}',
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Authentication',
|
||||
name: 'authentication',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Access Token',
|
||||
value: 'accessToken',
|
||||
},
|
||||
{
|
||||
name: 'OAuth2',
|
||||
value: 'oAuth2',
|
||||
},
|
||||
],
|
||||
default: 'accessToken',
|
||||
},
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Call',
|
||||
value: 'call',
|
||||
},
|
||||
{
|
||||
name: 'User',
|
||||
value: 'user',
|
||||
},
|
||||
],
|
||||
default: 'call',
|
||||
},
|
||||
...callOperations,
|
||||
...callFields,
|
||||
...userOperations,
|
||||
...userFields,
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
listSearch: {
|
||||
async getCalls(
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
paginationToken?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const query: IDataObject = {};
|
||||
if (paginationToken) {
|
||||
query.cursor = paginationToken;
|
||||
}
|
||||
|
||||
const responseData = await gongApiRequest.call(this, 'GET', '/v2/calls', {}, query);
|
||||
|
||||
const calls: Array<{
|
||||
id: string;
|
||||
title: string;
|
||||
}> = responseData.calls;
|
||||
|
||||
const results: INodeListSearchItems[] = calls
|
||||
.map((c) => ({
|
||||
name: c.title,
|
||||
value: c.id,
|
||||
}))
|
||||
.filter(
|
||||
(c) =>
|
||||
!filter ||
|
||||
c.name.toLowerCase().includes(filter.toLowerCase()) ||
|
||||
c.value?.toString() === filter,
|
||||
)
|
||||
.sort((a, b) => {
|
||||
if (a.name.toLowerCase() < b.name.toLowerCase()) return -1;
|
||||
if (a.name.toLowerCase() > b.name.toLowerCase()) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return { results, paginationToken: responseData.records.cursor };
|
||||
},
|
||||
|
||||
async getUsers(
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
paginationToken?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const query: IDataObject = {};
|
||||
if (paginationToken) {
|
||||
query.cursor = paginationToken;
|
||||
}
|
||||
|
||||
const responseData = await gongApiRequest.call(this, 'GET', '/v2/users', {}, query);
|
||||
|
||||
const users: Array<{
|
||||
id: string;
|
||||
emailAddress: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
}> = responseData.users;
|
||||
|
||||
const results: INodeListSearchItems[] = users
|
||||
.map((u) => ({
|
||||
name: `${u.firstName} ${u.lastName} (${u.emailAddress})`,
|
||||
value: u.id,
|
||||
}))
|
||||
.filter(
|
||||
(u) =>
|
||||
!filter ||
|
||||
u.name.toLowerCase().includes(filter.toLowerCase()) ||
|
||||
u.value?.toString() === filter,
|
||||
)
|
||||
.sort((a, b) => {
|
||||
if (a.name.toLowerCase() < b.name.toLowerCase()) return -1;
|
||||
if (a.name.toLowerCase() > b.name.toLowerCase()) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return { results, paginationToken: responseData.records.cursor };
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"metaData": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"customData": {
|
||||
"type": "null"
|
||||
},
|
||||
"direction": {
|
||||
"type": "string"
|
||||
},
|
||||
"duration": {
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"isPrivate": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"media": {
|
||||
"type": "string"
|
||||
},
|
||||
"meetingUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"primaryUserId": {
|
||||
"type": "string"
|
||||
},
|
||||
"purpose": {
|
||||
"type": "null"
|
||||
},
|
||||
"scheduled": {
|
||||
"type": "string"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string"
|
||||
},
|
||||
"started": {
|
||||
"type": "string"
|
||||
},
|
||||
"system": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"affiliation": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"methods": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"userId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"transcript": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sentences": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"end": {
|
||||
"type": "integer"
|
||||
},
|
||||
"start": {
|
||||
"type": "integer"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"speakerId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"customData": {
|
||||
"type": "null"
|
||||
},
|
||||
"direction": {
|
||||
"type": "string"
|
||||
},
|
||||
"duration": {
|
||||
"type": "integer"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"isPrivate": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"media": {
|
||||
"type": "string"
|
||||
},
|
||||
"meetingUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"primaryUserId": {
|
||||
"type": "string"
|
||||
},
|
||||
"purpose": {
|
||||
"type": "null"
|
||||
},
|
||||
"scheduled": {
|
||||
"type": "string"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string"
|
||||
},
|
||||
"started": {
|
||||
"type": "string"
|
||||
},
|
||||
"system": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"active": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"created": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailAliases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"extension": {
|
||||
"type": "null"
|
||||
},
|
||||
"firstName": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastName": {
|
||||
"type": "string"
|
||||
},
|
||||
"settings": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"emailsImported": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"gongConnectEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"nonRecordedMeetingsImported": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"preventEmailImport": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"preventWebConferenceRecording": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"telephonyCallsImported": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"webConferencesRecorded": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"spokenLanguages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"primary": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"trustedEmailAddress": {
|
||||
"type": "null"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,603 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteSingleFunctions,
|
||||
IHttpRequestOptions,
|
||||
IN8nHttpFullResponse,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
getCursorPaginatorCalls,
|
||||
gongApiPaginateRequest,
|
||||
isValidNumberIds,
|
||||
handleErrorPostReceive,
|
||||
extractCalls,
|
||||
} from '../GenericFunctions';
|
||||
|
||||
export const callOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['call'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Retrieve data for a specific call',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: '/v2/calls/extensive',
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [handleErrorPostReceive],
|
||||
},
|
||||
},
|
||||
action: 'Get call',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Retrieve a list of calls',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: '/v2/calls/extensive',
|
||||
body: {
|
||||
filter: {},
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [handleErrorPostReceive],
|
||||
},
|
||||
},
|
||||
action: 'Get many calls',
|
||||
},
|
||||
],
|
||||
default: 'getAll',
|
||||
},
|
||||
];
|
||||
|
||||
const getFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Call to Get',
|
||||
name: 'call',
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['call'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'getCalls',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: 'e.g. 7782342274025937895',
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '[0-9]{1,20}',
|
||||
errorMessage: 'Not a valid Gong Call ID',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'By URL',
|
||||
name: 'url',
|
||||
extractValue: {
|
||||
type: 'regex',
|
||||
regex: 'https:\\/\\/[a-zA-Z0-9-]+\\.app\\.gong\\.io\\/call\\?id=([0-9]{1,20})',
|
||||
},
|
||||
placeholder: 'e.g. https://subdomain.app.gong.io/call?id=7782342274025937895',
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: 'https:\\/\\/[a-zA-Z0-9-]+\\.app\\.gong\\.io\\/call\\?id=([0-9]{1,20})',
|
||||
errorMessage: 'Not a valid Gong URL',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'filter.callIds',
|
||||
propertyInDotNotation: true,
|
||||
value: '={{ [$value] }}',
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
{
|
||||
type: 'rootProperty',
|
||||
properties: {
|
||||
property: 'calls',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['call'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Call Data to Include',
|
||||
name: 'properties',
|
||||
type: 'multiOptions',
|
||||
default: [],
|
||||
description:
|
||||
'The Call properties to include in the returned results. Choose from a list, or specify IDs using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
|
||||
options: [
|
||||
{
|
||||
name: 'Action Items',
|
||||
value: 'pointsOfInterest',
|
||||
description: 'Call points of interest',
|
||||
},
|
||||
{
|
||||
name: 'Audio and Video URLs',
|
||||
value: 'media',
|
||||
description: 'Audio and video URL of the call. The URLs will be available for 8 hours.',
|
||||
},
|
||||
{
|
||||
name: 'Brief',
|
||||
value: 'brief',
|
||||
description: 'Spotlight call brief',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'contentSelector.exposedFields.content.brief',
|
||||
propertyInDotNotation: true,
|
||||
value: '={{ $value }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Comments',
|
||||
value: 'publicComments',
|
||||
description: 'Public comments made for this call',
|
||||
},
|
||||
{
|
||||
name: 'Highlights',
|
||||
value: 'highlights',
|
||||
description: 'Call highlights',
|
||||
},
|
||||
{
|
||||
name: 'Keypoints',
|
||||
value: 'keyPoints',
|
||||
description: 'Key points of the call',
|
||||
},
|
||||
{
|
||||
name: 'Outcome',
|
||||
value: 'callOutcome',
|
||||
description: 'Outcome of the call',
|
||||
},
|
||||
{
|
||||
name: 'Outline',
|
||||
value: 'outline',
|
||||
description: 'Call outline',
|
||||
},
|
||||
{
|
||||
name: 'Participants',
|
||||
value: 'parties',
|
||||
description: 'Information about the participants of the call',
|
||||
},
|
||||
{
|
||||
name: 'Structure',
|
||||
value: 'structure',
|
||||
description: 'Call agenda',
|
||||
},
|
||||
{
|
||||
name: 'Topics',
|
||||
value: 'topics',
|
||||
description: 'Duration of call topics',
|
||||
},
|
||||
{
|
||||
name: 'Trackers',
|
||||
value: 'trackers',
|
||||
description: 'Smart tracker and keyword tracker information for the call',
|
||||
},
|
||||
{
|
||||
name: 'Transcript',
|
||||
value: 'transcript',
|
||||
description: 'Information about the participants',
|
||||
},
|
||||
],
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
const contentProperties = [
|
||||
'pointsOfInterest',
|
||||
'brief',
|
||||
'highlights',
|
||||
'keyPoints',
|
||||
'outline',
|
||||
'callOutcome',
|
||||
'structure',
|
||||
'trackers',
|
||||
'topics',
|
||||
];
|
||||
const exposedFieldsProperties = ['media', 'parties'];
|
||||
const collaborationProperties = ['publicComments'];
|
||||
|
||||
const properties = this.getNodeParameter('options.properties') as string[];
|
||||
const contentSelector = { exposedFields: {} } as any;
|
||||
for (const property of properties) {
|
||||
if (exposedFieldsProperties.includes(property)) {
|
||||
contentSelector.exposedFields[property] = true;
|
||||
} else if (contentProperties.includes(property)) {
|
||||
contentSelector.exposedFields.content ??= {};
|
||||
contentSelector.exposedFields.content[property] = true;
|
||||
} else if (collaborationProperties.includes(property)) {
|
||||
contentSelector.exposedFields.collaboration ??= {};
|
||||
contentSelector.exposedFields.collaboration[property] = true;
|
||||
}
|
||||
}
|
||||
|
||||
requestOptions.body ||= {};
|
||||
Object.assign(requestOptions.body, { contentSelector });
|
||||
return requestOptions;
|
||||
},
|
||||
],
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
items: INodeExecutionData[],
|
||||
_responseData: IN8nHttpFullResponse,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const properties = this.getNodeParameter('options.properties') as string[];
|
||||
if (properties.includes('transcript')) {
|
||||
for (const item of items) {
|
||||
const callTranscripts = await gongApiPaginateRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'/v2/calls/transcript',
|
||||
{ filter: { callIds: [(item.json.metaData as IDataObject).id] } },
|
||||
{},
|
||||
item.index ?? 0,
|
||||
'callTranscripts',
|
||||
);
|
||||
item.json.transcript = callTranscripts?.length
|
||||
? callTranscripts[0].transcript
|
||||
: [];
|
||||
}
|
||||
}
|
||||
return items;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'Add Option',
|
||||
type: 'collection',
|
||||
},
|
||||
];
|
||||
|
||||
const getAllFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['call'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
paginate: '={{ $value }}',
|
||||
},
|
||||
operations: {
|
||||
pagination: getCursorPaginatorCalls(),
|
||||
},
|
||||
},
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
default: 50,
|
||||
description: 'Max number of results to return',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['call'],
|
||||
operation: ['getAll'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
output: {
|
||||
postReceive: [
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
items: INodeExecutionData[],
|
||||
_response: IN8nHttpFullResponse,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
return extractCalls(items);
|
||||
},
|
||||
{
|
||||
type: 'limit',
|
||||
properties: {
|
||||
maxResults: '={{ $value }}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
validateType: 'number',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['call'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'After',
|
||||
name: 'fromDateTime',
|
||||
default: '',
|
||||
description:
|
||||
'Returns calls that started on or after the specified date and time. If not provided, list starts with earliest call. For web-conference calls recorded by Gong, the date denotes its scheduled time, otherwise, it denotes its actual start time.',
|
||||
placeholder: 'e.g. 2018-02-18T02:30:00-07:00 or 2018-02-18T08:00:00Z',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'filter.fromDateTime',
|
||||
propertyInDotNotation: true,
|
||||
value: '={{ new Date($value).toISOString() }}',
|
||||
},
|
||||
},
|
||||
type: 'dateTime',
|
||||
validateType: 'dateTime',
|
||||
},
|
||||
{
|
||||
displayName: 'Before',
|
||||
name: 'toDateTime',
|
||||
default: '',
|
||||
description:
|
||||
'Returns calls that started up to but excluding specified date and time. If not provided, list ends with most recent call. For web-conference calls recorded by Gong, the date denotes its scheduled time, otherwise, it denotes its actual start time.',
|
||||
placeholder: 'e.g. 2018-02-18T02:30:00-07:00 or 2018-02-18T08:00:00Z',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'filter.toDateTime',
|
||||
propertyInDotNotation: true,
|
||||
value: '={{ new Date($value).toISOString() }}',
|
||||
},
|
||||
},
|
||||
type: 'dateTime',
|
||||
validateType: 'dateTime',
|
||||
},
|
||||
{
|
||||
displayName: 'Workspace ID',
|
||||
name: 'workspaceId',
|
||||
default: '',
|
||||
description: 'Return only the calls belonging to this workspace',
|
||||
placeholder: 'e.g. 623457276584334',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'filter.workspaceId',
|
||||
propertyInDotNotation: true,
|
||||
value: '={{ $value }}',
|
||||
},
|
||||
},
|
||||
type: 'string',
|
||||
validateType: 'number',
|
||||
},
|
||||
{
|
||||
displayName: 'Call IDs',
|
||||
name: 'callIds',
|
||||
default: '',
|
||||
description: 'List of calls IDs to be filtered',
|
||||
hint: 'Comma separated list of IDs, array of strings can be set in expression',
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
const callIdsParam = this.getNodeParameter('filters.callIds') as
|
||||
| number
|
||||
| number[]
|
||||
| string
|
||||
| string[];
|
||||
if (callIdsParam && !isValidNumberIds(callIdsParam)) {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
message: 'Call IDs must be numeric',
|
||||
description: "Double-check the value in the parameter 'Call IDs' and try again",
|
||||
});
|
||||
}
|
||||
|
||||
const callIds = Array.isArray(callIdsParam)
|
||||
? callIdsParam.map((x) => x.toString())
|
||||
: callIdsParam
|
||||
.toString()
|
||||
.split(',')
|
||||
.map((x) => x.trim());
|
||||
|
||||
requestOptions.body ||= {};
|
||||
(requestOptions.body as IDataObject).filter ||= {};
|
||||
Object.assign((requestOptions.body as IDataObject).filter as IDataObject, {
|
||||
callIds,
|
||||
});
|
||||
|
||||
return requestOptions;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'e.g. 7782342274025937895',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
displayName: 'Organizer',
|
||||
name: 'primaryUserIds',
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
description: 'Return only the calls hosted by the specified user',
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'getUsers',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: 'e.g. 7782342274025937895',
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '[0-9]{1,20}',
|
||||
errorMessage: 'Not a valid Gong User ID',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'filter.primaryUserIds',
|
||||
propertyInDotNotation: true,
|
||||
value: '={{ [$value] }}',
|
||||
},
|
||||
},
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
],
|
||||
placeholder: 'Add Filter',
|
||||
type: 'collection',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['call'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Call Data to Include',
|
||||
name: 'properties',
|
||||
type: 'multiOptions',
|
||||
default: [],
|
||||
description:
|
||||
'The Call properties to include in the returned results. Choose from a list, or specify IDs using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
|
||||
options: [
|
||||
{
|
||||
name: 'Participants',
|
||||
value: 'parties',
|
||||
description: 'Information about the participants of the call',
|
||||
},
|
||||
{
|
||||
name: 'Topics',
|
||||
value: 'topics',
|
||||
description: 'Information about the topics of the call',
|
||||
},
|
||||
],
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
const contentProperties = ['topics'];
|
||||
const exposedFieldsProperties = ['parties'];
|
||||
|
||||
const properties = this.getNodeParameter('options.properties') as string[];
|
||||
const contentSelector = { exposedFields: {} } as any;
|
||||
for (const property of properties) {
|
||||
if (exposedFieldsProperties.includes(property)) {
|
||||
contentSelector.exposedFields[property] = true;
|
||||
} else if (contentProperties.includes(property)) {
|
||||
contentSelector.exposedFields.content ??= {};
|
||||
contentSelector.exposedFields.content[property] = true;
|
||||
}
|
||||
}
|
||||
|
||||
requestOptions.body ||= {};
|
||||
Object.assign(requestOptions.body, { contentSelector });
|
||||
return requestOptions;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'Add Option',
|
||||
type: 'collection',
|
||||
},
|
||||
];
|
||||
|
||||
export const callFields: INodeProperties[] = [...getFields, ...getAllFields];
|
||||
@@ -0,0 +1,288 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteSingleFunctions,
|
||||
IHttpRequestOptions,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
getCursorPaginatorUsers,
|
||||
isValidNumberIds,
|
||||
handleErrorPostReceive,
|
||||
} from '../GenericFunctions';
|
||||
|
||||
export const userOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Retrieve data for a specific user',
|
||||
action: 'Get user',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: '/v2/users/extensive',
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [handleErrorPostReceive],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Retrieve a list of users',
|
||||
action: 'Get many users',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: '/v2/users/extensive',
|
||||
body: {
|
||||
filter: {},
|
||||
},
|
||||
ignoreHttpStatusErrors: true,
|
||||
},
|
||||
output: {
|
||||
postReceive: [handleErrorPostReceive],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
},
|
||||
];
|
||||
|
||||
const getOperation: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'User to Get',
|
||||
name: 'user',
|
||||
default: {
|
||||
mode: 'list',
|
||||
value: '',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'getUsers',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
placeholder: 'e.g. 7782342274025937895',
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '[0-9]{1,20}',
|
||||
errorMessage: 'Not a valid Gong User ID',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'filter.userIds',
|
||||
propertyInDotNotation: true,
|
||||
value: '={{ [$value] }}',
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
{
|
||||
type: 'rootProperty',
|
||||
properties: {
|
||||
property: 'users',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
type: 'resourceLocator',
|
||||
},
|
||||
];
|
||||
|
||||
const getAllOperation: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
paginate: '={{ $value }}',
|
||||
},
|
||||
operations: {
|
||||
pagination: getCursorPaginatorUsers(),
|
||||
},
|
||||
},
|
||||
type: 'boolean',
|
||||
validateType: 'boolean',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
default: 50,
|
||||
description: 'Max number of results to return',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['getAll'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
output: {
|
||||
postReceive: [
|
||||
{
|
||||
type: 'rootProperty',
|
||||
properties: {
|
||||
property: 'users',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'limit',
|
||||
properties: {
|
||||
maxResults: '={{ $value }}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
validateType: 'number',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Created After',
|
||||
name: 'createdFromDateTime',
|
||||
default: '',
|
||||
description:
|
||||
'An optional user creation time lower limit, if supplied the API will return only the users created at or after this time',
|
||||
placeholder: 'e.g. 2018-02-18T02:30:00-07:00 or 2018-02-18T08:00:00Z',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'filter.createdFromDateTime',
|
||||
propertyInDotNotation: true,
|
||||
value: '={{ new Date($value).toISOString() }}',
|
||||
},
|
||||
},
|
||||
type: 'dateTime',
|
||||
validateType: 'dateTime',
|
||||
},
|
||||
{
|
||||
displayName: 'Created Before',
|
||||
name: 'createdToDateTime',
|
||||
default: '',
|
||||
description:
|
||||
'An optional user creation time upper limit, if supplied the API will return only the users created before this time',
|
||||
placeholder: 'e.g. 2018-02-18T02:30:00-07:00 or 2018-02-18T08:00:00Z',
|
||||
routing: {
|
||||
send: {
|
||||
type: 'body',
|
||||
property: 'filter.createdToDateTime',
|
||||
propertyInDotNotation: true,
|
||||
value: '={{ new Date($value).toISOString() }}',
|
||||
},
|
||||
},
|
||||
type: 'dateTime',
|
||||
validateType: 'dateTime',
|
||||
},
|
||||
{
|
||||
displayName: 'User IDs',
|
||||
name: 'userIds',
|
||||
default: '',
|
||||
description: "Set of Gong's unique numeric identifiers for the users (up to 20 digits)",
|
||||
hint: 'Comma separated list of IDs, array of strings can be set in expression',
|
||||
routing: {
|
||||
send: {
|
||||
preSend: [
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
const userIdsParam = this.getNodeParameter('filters.userIds') as
|
||||
| number
|
||||
| number[]
|
||||
| string
|
||||
| string[];
|
||||
if (userIdsParam && !isValidNumberIds(userIdsParam)) {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
message: 'User IDs must be numeric',
|
||||
description: "Double-check the value in the parameter 'User IDs' and try again",
|
||||
});
|
||||
}
|
||||
|
||||
const userIds = Array.isArray(userIdsParam)
|
||||
? userIdsParam.map((x) => x.toString())
|
||||
: userIdsParam
|
||||
.toString()
|
||||
.split(',')
|
||||
.map((x) => x.trim());
|
||||
|
||||
requestOptions.body ||= {};
|
||||
(requestOptions.body as IDataObject).filter ||= {};
|
||||
Object.assign((requestOptions.body as IDataObject).filter as IDataObject, {
|
||||
userIds,
|
||||
});
|
||||
|
||||
return requestOptions;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'e.g. 7782342274025937895',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
placeholder: 'Add Filter',
|
||||
type: 'collection',
|
||||
},
|
||||
];
|
||||
|
||||
export const userFields: INodeProperties[] = [...getOperation, ...getAllOperation];
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './CallDescription';
|
||||
export * from './UserDescription';
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg viewBox="30.0354 21.6399 25.8853 28.023" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 55.285 33.621 L 47.685 33.621 C 47.251 33.621 46.92 34.065 47.075 34.479 L 48.905 39.215 C 48.985 39.426 48.821 39.65 48.595 39.639 L 46.258 39.494 C 46.151 39.486 46.048 39.537 45.99 39.628 L 44.17 42.203 C 44.074 42.354 43.873 42.396 43.725 42.296 L 41.037 40.466 C 40.931 40.393 40.791 40.393 40.685 40.466 L 36.963 42.989 C 36.725 43.154 36.404 42.927 36.487 42.647 L 37.521 38.935 C 37.566 38.781 37.485 38.619 37.335 38.563 L 35.36 37.757 C 35.164 37.679 35.102 37.433 35.236 37.271 L 36.983 35.12 C 37.069 35.012 37.073 34.86 36.993 34.748 L 35.525 32.628 C 35.389 32.432 35.517 32.162 35.755 32.142 C 35.757 32.142 35.76 32.142 35.763 32.142 L 38.038 31.966 C 38.204 31.956 38.338 31.811 38.328 31.636 L 38.152 28.451 C 38.143 28.221 38.372 28.058 38.586 28.141 L 41.409 29.309 C 41.533 29.361 41.678 29.329 41.76 29.226 L 43.704 27.055 C 43.858 26.882 44.138 26.926 44.232 27.137 L 45.4 30.147 C 45.555 30.519 46.02 30.653 46.352 30.405 L 50.922 27.003 C 51.439 26.62 51.108 25.793 50.467 25.886 L 47.457 26.29 C 47.315 26.31 47.177 26.228 47.127 26.093 L 45.545 22.03 C 45.375 21.605 44.819 21.504 44.511 21.843 L 41.068 25.576 C 40.977 25.67 40.837 25.699 40.716 25.649 L 36.187 23.736 C 35.782 23.568 35.335 23.856 35.319 24.294 L 35.153 28.968 C 35.147 29.127 35.022 29.256 34.863 29.268 L 30.728 29.536 C 30.249 29.566 29.983 30.103 30.248 30.502 C 30.25 30.504 30.251 30.506 30.252 30.508 L 33.002 34.562 C 33.083 34.679 33.075 34.836 32.982 34.944 L 30.19 38.15 C 29.91 38.468 30.025 38.968 30.417 39.132 L 33.623 40.518 C 33.765 40.576 33.84 40.731 33.799 40.879 L 31.751 48.883 C 31.632 49.349 32.062 49.769 32.525 49.639 C 32.596 49.619 32.663 49.587 32.723 49.544 L 40.416 44.023 C 40.524 43.945 40.67 43.945 40.778 44.023 L 44.284 46.483 C 44.573 46.69 44.966 46.608 45.162 46.318 L 47.355 42.958 C 47.418 42.856 47.537 42.802 47.655 42.823 L 52.897 43.454 C 53.321 43.516 53.745 43.164 53.59 42.772 L 51.408 37.126 C 51.346 36.971 51.408 36.806 51.594 36.712 L 55.574 34.83 C 56.164 34.53 55.957 33.62 55.285 33.62 L 55.285 33.621 Z" fill="#9069E7"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,983 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import type { WorkflowTestData } from 'n8n-workflow';
|
||||
import { NodeConnectionTypes } from 'n8n-workflow';
|
||||
|
||||
import { gongApiResponse, gongNodeResponse } from './mocks';
|
||||
|
||||
describe('Gong Node', () => {
|
||||
const testHarness = new NodeTestHarness();
|
||||
const baseUrl = 'https://api.gong.io';
|
||||
const credentials = {
|
||||
gongApi: { baseUrl },
|
||||
gongOAuth2Api: { baseUrl },
|
||||
};
|
||||
|
||||
describe('Credentials', () => {
|
||||
const tests: WorkflowTestData[] = [
|
||||
{
|
||||
description: 'should use correct credentials',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
parameters: {},
|
||||
id: '416e4fc1-5055-4e61-854e-a6265256ac26',
|
||||
name: 'When clicking ‘Execute workflow’',
|
||||
type: 'n8n-nodes-base.manualTrigger',
|
||||
position: [820, 380],
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
operation: 'get',
|
||||
call: {
|
||||
__rl: true,
|
||||
value: '7782342274025937895',
|
||||
mode: 'id',
|
||||
},
|
||||
options: {},
|
||||
requestOptions: {},
|
||||
},
|
||||
id: 'c87d72ec-0683-4e32-9829-5e6ea1d1ee7d',
|
||||
name: 'Gong gongApi',
|
||||
type: 'n8n-nodes-base.gong',
|
||||
typeVersion: 1,
|
||||
position: [1040, 380],
|
||||
credentials: {
|
||||
gongApi: {
|
||||
id: '1',
|
||||
name: 'Gong account',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
authentication: 'oAuth2',
|
||||
operation: 'get',
|
||||
call: {
|
||||
__rl: true,
|
||||
value: '7782342274025937896',
|
||||
mode: 'id',
|
||||
},
|
||||
options: {},
|
||||
requestOptions: {},
|
||||
},
|
||||
id: 'c87d72ec-0683-4e32-9829-5e6ea1d1ee7d',
|
||||
name: 'Gong gongOAuth2Api',
|
||||
type: 'n8n-nodes-base.gong',
|
||||
typeVersion: 1,
|
||||
position: [1040, 380],
|
||||
credentials: {
|
||||
gongOAuth2Api: {
|
||||
id: '2',
|
||||
name: 'Gong account2',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'When clicking ‘Execute workflow’': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Gong gongApi',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
'Gong gongApi': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Gong gongOAuth2Api',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
'Gong gongApi': [[{ json: { metaData: gongNodeResponse.getCall[0].json.metaData } }]],
|
||||
'Gong gongOAuth2Api': [
|
||||
[{ json: { metaData: gongNodeResponse.getCall[0].json.metaData } }],
|
||||
],
|
||||
},
|
||||
},
|
||||
nock: {
|
||||
baseUrl,
|
||||
mocks: [
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/calls/extensive',
|
||||
requestBody: { filter: { callIds: ['7782342274025937895'] } },
|
||||
statusCode: 200,
|
||||
responseBody: {
|
||||
...gongApiResponse.postCallsExtensive,
|
||||
records: {},
|
||||
calls: [{ metaData: gongApiResponse.postCallsExtensive.calls[0].metaData }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/calls/extensive',
|
||||
requestBody: { filter: { callIds: ['7782342274025937896'] } },
|
||||
statusCode: 200,
|
||||
responseBody: {
|
||||
...gongApiResponse.postCallsExtensive,
|
||||
records: {},
|
||||
calls: [{ metaData: gongApiResponse.postCallsExtensive.calls[0].metaData }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const testData of tests) {
|
||||
testHarness.setupTest(testData, { credentials });
|
||||
}
|
||||
});
|
||||
|
||||
describe('Call description', () => {
|
||||
const tests: WorkflowTestData[] = [
|
||||
{
|
||||
description: 'should get call with no options true',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
parameters: {},
|
||||
id: '416e4fc1-5055-4e61-854e-a6265256ac26',
|
||||
name: 'When clicking ‘Execute workflow’',
|
||||
type: 'n8n-nodes-base.manualTrigger',
|
||||
position: [820, 380],
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
operation: 'get',
|
||||
call: {
|
||||
__rl: true,
|
||||
value: '7782342274025937895',
|
||||
mode: 'id',
|
||||
},
|
||||
options: {},
|
||||
requestOptions: {},
|
||||
},
|
||||
id: 'c87d72ec-0683-4e32-9829-5e6ea1d1ee7d',
|
||||
name: 'Gong',
|
||||
type: 'n8n-nodes-base.gong',
|
||||
typeVersion: 1,
|
||||
position: [1040, 380],
|
||||
credentials: {
|
||||
gongApi: {
|
||||
id: '1',
|
||||
name: 'Gong account',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'When clicking ‘Execute workflow’': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Gong',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
Gong: [[{ json: { metaData: gongNodeResponse.getCall[0].json.metaData } }]],
|
||||
},
|
||||
},
|
||||
nock: {
|
||||
baseUrl,
|
||||
mocks: [
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/calls/extensive',
|
||||
statusCode: 200,
|
||||
requestBody: { filter: { callIds: ['7782342274025937895'] } },
|
||||
responseBody: {
|
||||
...gongApiResponse.postCallsExtensive,
|
||||
records: {},
|
||||
calls: [{ metaData: gongApiResponse.postCallsExtensive.calls[0].metaData }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'should get call with all options true',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
parameters: {},
|
||||
id: '416e4fc1-5055-4e61-854e-a6265256ac26',
|
||||
name: 'When clicking ‘Execute workflow’',
|
||||
type: 'n8n-nodes-base.manualTrigger',
|
||||
position: [820, 380],
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
operation: 'get',
|
||||
call: {
|
||||
__rl: true,
|
||||
value: '7782342274025937895',
|
||||
mode: 'id',
|
||||
},
|
||||
options: {
|
||||
properties: [
|
||||
'pointsOfInterest',
|
||||
'transcript',
|
||||
'media',
|
||||
'brief',
|
||||
'publicComments',
|
||||
'highlights',
|
||||
'trackers',
|
||||
'topics',
|
||||
'structure',
|
||||
'parties',
|
||||
'callOutcome',
|
||||
'outline',
|
||||
'keyPoints',
|
||||
],
|
||||
},
|
||||
requestOptions: {},
|
||||
},
|
||||
id: 'c87d72ec-0683-4e32-9829-5e6ea1d1ee7d',
|
||||
name: 'Gong',
|
||||
type: 'n8n-nodes-base.gong',
|
||||
typeVersion: 1,
|
||||
position: [1040, 380],
|
||||
credentials: {
|
||||
gongApi: {
|
||||
id: '1',
|
||||
name: 'Gong account',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'When clicking ‘Execute workflow’': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Gong',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
Gong: [gongNodeResponse.getCall],
|
||||
},
|
||||
},
|
||||
nock: {
|
||||
baseUrl,
|
||||
mocks: [
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/calls/extensive',
|
||||
statusCode: 200,
|
||||
requestBody: {
|
||||
filter: {
|
||||
callIds: ['7782342274025937895'],
|
||||
},
|
||||
contentSelector: {
|
||||
exposedFields: {
|
||||
content: {
|
||||
pointsOfInterest: true,
|
||||
brief: true,
|
||||
highlights: true,
|
||||
keyPoints: true,
|
||||
outline: true,
|
||||
callOutcome: true,
|
||||
structure: true,
|
||||
trackers: true,
|
||||
topics: true,
|
||||
},
|
||||
media: true,
|
||||
collaboration: {
|
||||
publicComments: true,
|
||||
},
|
||||
parties: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
responseBody: {
|
||||
...gongApiResponse.postCallsExtensive,
|
||||
records: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/calls/transcript',
|
||||
statusCode: 200,
|
||||
requestBody: {
|
||||
filter: {
|
||||
callIds: ['7782342274025937895'],
|
||||
},
|
||||
},
|
||||
responseBody: gongApiResponse.postCallsTranscript,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'should get all calls with filters',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
parameters: {},
|
||||
id: '416e4fc1-5055-4e61-854e-a6265256ac26',
|
||||
name: 'When clicking ‘Execute workflow’',
|
||||
type: 'n8n-nodes-base.manualTrigger',
|
||||
position: [820, 380],
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
returnAll: true,
|
||||
filters: {
|
||||
fromDateTime: '2024-01-01T00:00:00Z',
|
||||
toDateTime: '2024-12-31T00:00:00Z',
|
||||
workspaceId: '3662366901393371750',
|
||||
callIds: "={{ ['3662366901393371750', '3662366901393371751'] }}",
|
||||
primaryUserIds: {
|
||||
__rl: true,
|
||||
value: '234599484848423',
|
||||
mode: 'id',
|
||||
},
|
||||
},
|
||||
options: {
|
||||
properties: ['parties', 'topics'],
|
||||
},
|
||||
requestOptions: {},
|
||||
},
|
||||
id: 'c87d72ec-0683-4e32-9829-5e6ea1d1ee7d',
|
||||
name: 'Gong',
|
||||
type: 'n8n-nodes-base.gong',
|
||||
typeVersion: 1,
|
||||
position: [1040, 380],
|
||||
credentials: {
|
||||
gongApi: {
|
||||
id: '1',
|
||||
name: 'Gong account',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'When clicking ‘Execute workflow’': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Gong',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
Gong: [gongNodeResponse.getAllCall],
|
||||
},
|
||||
},
|
||||
nock: {
|
||||
baseUrl,
|
||||
mocks: [
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/calls/extensive',
|
||||
statusCode: 200,
|
||||
requestBody: {
|
||||
filter: {
|
||||
fromDateTime: '2024-01-01T00:00:00.000Z',
|
||||
toDateTime: '2024-12-31T00:00:00.000Z',
|
||||
workspaceId: '3662366901393371750',
|
||||
callIds: ['3662366901393371750', '3662366901393371751'],
|
||||
primaryUserIds: ['234599484848423'],
|
||||
},
|
||||
contentSelector: {
|
||||
exposedFields: {
|
||||
parties: true,
|
||||
content: {
|
||||
topics: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
cursor: undefined,
|
||||
},
|
||||
responseBody: {
|
||||
...gongApiResponse.postCallsExtensive,
|
||||
calls: [
|
||||
{
|
||||
metaData: {
|
||||
...gongApiResponse.postCallsExtensive.calls[0].metaData,
|
||||
parties: [...gongApiResponse.postCallsExtensive.calls[0].parties],
|
||||
content: {
|
||||
topics: [...gongApiResponse.postCallsExtensive.calls[0].content.topics],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/calls/extensive',
|
||||
statusCode: 200,
|
||||
requestBody: {
|
||||
filter: {
|
||||
fromDateTime: '2024-01-01T00:00:00.000Z',
|
||||
toDateTime: '2024-12-31T00:00:00.000Z',
|
||||
workspaceId: '3662366901393371750',
|
||||
callIds: ['3662366901393371750', '3662366901393371751'],
|
||||
primaryUserIds: ['234599484848423'],
|
||||
},
|
||||
contentSelector: {
|
||||
exposedFields: {
|
||||
parties: true,
|
||||
content: {
|
||||
topics: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
cursor:
|
||||
'eyJhbGciOiJIUzI1NiJ9.eyJjYWxsSWQiM1M30.6qKwpOcvnuweTZmFRzYdtjs_YwJphJU4QIwWFM',
|
||||
},
|
||||
responseBody: {
|
||||
...gongApiResponse.postCallsExtensive,
|
||||
records: {},
|
||||
calls: [
|
||||
{
|
||||
metaData: {
|
||||
...gongApiResponse.postCallsExtensive.calls[0].metaData,
|
||||
id: '7782342274025937896',
|
||||
url: 'https://app.gong.io/call?id=7782342274025937896',
|
||||
},
|
||||
parties: [...gongApiResponse.postCallsExtensive.calls[0].parties],
|
||||
content: {
|
||||
topics: [...gongApiResponse.postCallsExtensive.calls[0].content.topics],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'should get limit 50 calls with no options and filters',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
parameters: {},
|
||||
id: '416e4fc1-5055-4e61-854e-a6265256ac26',
|
||||
name: 'When clicking ‘Execute workflow’',
|
||||
type: 'n8n-nodes-base.manualTrigger',
|
||||
position: [820, 380],
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
filters: {},
|
||||
options: {},
|
||||
requestOptions: {},
|
||||
},
|
||||
id: 'c87d72ec-0683-4e32-9829-5e6ea1d1ee7d',
|
||||
name: 'Gong',
|
||||
type: 'n8n-nodes-base.gong',
|
||||
typeVersion: 1,
|
||||
position: [1040, 380],
|
||||
credentials: {
|
||||
gongApi: {
|
||||
id: '1',
|
||||
name: 'Gong account',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'When clicking ‘Execute workflow’': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Gong',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
Gong: [
|
||||
Array.from({ length: 50 }, () => ({ ...gongNodeResponse.getAllCallNoOptions[0] })),
|
||||
],
|
||||
},
|
||||
},
|
||||
nock: {
|
||||
baseUrl,
|
||||
mocks: [
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/calls/extensive',
|
||||
statusCode: 200,
|
||||
requestBody: {
|
||||
filter: {},
|
||||
},
|
||||
responseBody: {
|
||||
...gongApiResponse.postCallsExtensive,
|
||||
calls: Array.from({ length: 100 }, () => ({
|
||||
metaData: { ...gongApiResponse.postCallsExtensive.calls[0].metaData },
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'should return empty result if no calls found for user',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
parameters: {},
|
||||
id: '416e4fc1-5055-4e61-854e-a6265256ac26',
|
||||
name: 'When clicking ‘Execute workflow’',
|
||||
type: 'n8n-nodes-base.manualTrigger',
|
||||
position: [820, 380],
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
filters: {
|
||||
primaryUserIds: {
|
||||
__rl: true,
|
||||
value: '234599484848423',
|
||||
mode: 'id',
|
||||
},
|
||||
},
|
||||
options: {},
|
||||
requestOptions: {},
|
||||
},
|
||||
id: 'c87d72ec-0683-4e32-9829-5e6ea1d1ee7d',
|
||||
name: 'Gong',
|
||||
type: 'n8n-nodes-base.gong',
|
||||
typeVersion: 1,
|
||||
position: [1040, 380],
|
||||
credentials: {
|
||||
gongApi: {
|
||||
id: '1',
|
||||
name: 'Gong account',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'When clicking ‘Execute workflow’': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Gong',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
Gong: [[{ json: {} }]],
|
||||
},
|
||||
},
|
||||
nock: {
|
||||
baseUrl,
|
||||
mocks: [
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/calls/extensive',
|
||||
statusCode: 404,
|
||||
requestBody: {
|
||||
filter: {
|
||||
primaryUserIds: ['234599484848423'],
|
||||
},
|
||||
cursor: undefined,
|
||||
},
|
||||
responseBody: {
|
||||
requestId: 'thrhbxbkqiw41ma1cl',
|
||||
errors: ['No calls found corresponding to the provided filters'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'should handle error response',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
parameters: {},
|
||||
id: '416e4fc1-5055-4e61-854e-a6265256ac26',
|
||||
name: 'When clicking ‘Execute workflow’',
|
||||
type: 'n8n-nodes-base.manualTrigger',
|
||||
position: [820, 380],
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
filters: {
|
||||
workspaceId: '623457276584335',
|
||||
},
|
||||
options: {},
|
||||
requestOptions: {},
|
||||
},
|
||||
id: 'c87d72ec-0683-4e32-9829-5e6ea1d1ee7d',
|
||||
name: 'Gong',
|
||||
type: 'n8n-nodes-base.gong',
|
||||
typeVersion: 1,
|
||||
position: [1040, 380],
|
||||
credentials: {
|
||||
gongApi: {
|
||||
id: '1',
|
||||
name: 'Gong account',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'When clicking ‘Execute workflow’': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Gong',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
Gong: [],
|
||||
},
|
||||
error: 'The resource you are requesting could not be found',
|
||||
},
|
||||
nock: {
|
||||
baseUrl,
|
||||
mocks: [
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/calls/extensive',
|
||||
statusCode: 404,
|
||||
requestBody: {
|
||||
filter: {
|
||||
workspaceId: '623457276584335',
|
||||
},
|
||||
},
|
||||
responseBody: {
|
||||
requestId: 'thrhbxbkqiw41ma1cl',
|
||||
errors: ['No calls found corresponding to the provided filters'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const testData of tests) {
|
||||
testHarness.setupTest(testData, { credentials });
|
||||
}
|
||||
});
|
||||
|
||||
describe('User description', () => {
|
||||
const tests: WorkflowTestData[] = [
|
||||
{
|
||||
description: 'should get user',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
parameters: {},
|
||||
id: '416e4fc1-5055-4e61-854e-a6265256ac26',
|
||||
name: 'When clicking ‘Execute workflow’',
|
||||
type: 'n8n-nodes-base.manualTrigger',
|
||||
position: [820, 380],
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
resource: 'user',
|
||||
user: {
|
||||
__rl: true,
|
||||
value: '234599484848423',
|
||||
mode: 'id',
|
||||
},
|
||||
requestOptions: {},
|
||||
},
|
||||
id: 'c87d72ec-0683-4e32-9829-5e6ea1d1ee7d',
|
||||
name: 'Gong',
|
||||
type: 'n8n-nodes-base.gong',
|
||||
typeVersion: 1,
|
||||
position: [1040, 380],
|
||||
credentials: {
|
||||
gongApi: {
|
||||
id: '1',
|
||||
name: 'Gong account',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'When clicking ‘Execute workflow’': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Gong',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
Gong: [gongNodeResponse.getUser],
|
||||
},
|
||||
},
|
||||
nock: {
|
||||
baseUrl,
|
||||
mocks: [
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/users/extensive',
|
||||
statusCode: 200,
|
||||
requestBody: { filter: { userIds: ['234599484848423'] } },
|
||||
responseBody: {
|
||||
...gongApiResponse.postUsersExtensive,
|
||||
records: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'should get all users',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
parameters: {},
|
||||
id: '416e4fc1-5055-4e61-854e-a6265256ac26',
|
||||
name: 'When clicking ‘Execute workflow’',
|
||||
type: 'n8n-nodes-base.manualTrigger',
|
||||
position: [820, 380],
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
resource: 'user',
|
||||
operation: 'getAll',
|
||||
returnAll: true,
|
||||
filters: {
|
||||
createdFromDateTime: '2024-01-01T00:00:00Z',
|
||||
createdToDateTime: '2024-12-31T00:00:00Z',
|
||||
userIds: '234599484848423, 234599484848424',
|
||||
},
|
||||
requestOptions: {},
|
||||
},
|
||||
id: 'c87d72ec-0683-4e32-9829-5e6ea1d1ee7d',
|
||||
name: 'Gong',
|
||||
type: 'n8n-nodes-base.gong',
|
||||
typeVersion: 1,
|
||||
position: [1040, 380],
|
||||
credentials: {
|
||||
gongApi: {
|
||||
id: '1',
|
||||
name: 'Gong account',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'When clicking ‘Execute workflow’': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Gong',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
Gong: [gongNodeResponse.getAllUser],
|
||||
},
|
||||
},
|
||||
nock: {
|
||||
baseUrl,
|
||||
mocks: [
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/users/extensive',
|
||||
statusCode: 200,
|
||||
requestBody: {
|
||||
filter: {
|
||||
createdFromDateTime: '2024-01-01T00:00:00.000Z',
|
||||
createdToDateTime: '2024-12-31T00:00:00.000Z',
|
||||
userIds: ['234599484848423', '234599484848424'],
|
||||
},
|
||||
},
|
||||
responseBody: gongApiResponse.postUsersExtensive,
|
||||
},
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/users/extensive',
|
||||
statusCode: 200,
|
||||
requestBody: {
|
||||
filter: {
|
||||
createdFromDateTime: '2024-01-01T00:00:00.000Z',
|
||||
createdToDateTime: '2024-12-31T00:00:00.000Z',
|
||||
userIds: ['234599484848423', '234599484848424'],
|
||||
},
|
||||
cursor:
|
||||
'eyJhbGciOiJIUzI1NiJ9.eyJjYWxsSWQiM1M30.6qKwpOcvnuweTZmFRzYdtjs_YwJphJU4QIwWFM',
|
||||
},
|
||||
responseBody: {
|
||||
...gongApiResponse.postUsersExtensive,
|
||||
records: {},
|
||||
users: [{ ...gongApiResponse.postUsersExtensive.users[0], id: '234599484848424' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'should handle error response',
|
||||
input: {
|
||||
workflowData: {
|
||||
nodes: [
|
||||
{
|
||||
parameters: {},
|
||||
id: '416e4fc1-5055-4e61-854e-a6265256ac26',
|
||||
name: 'When clicking ‘Execute workflow’',
|
||||
type: 'n8n-nodes-base.manualTrigger',
|
||||
position: [820, 380],
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
resource: 'user',
|
||||
operation: 'getAll',
|
||||
filters: {
|
||||
userIds: '234599484848423',
|
||||
},
|
||||
requestOptions: {},
|
||||
},
|
||||
id: 'c87d72ec-0683-4e32-9829-5e6ea1d1ee7d',
|
||||
name: 'Gong',
|
||||
type: 'n8n-nodes-base.gong',
|
||||
typeVersion: 1,
|
||||
position: [1040, 380],
|
||||
credentials: {
|
||||
gongApi: {
|
||||
id: '1',
|
||||
name: 'Gong account',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'When clicking ‘Execute workflow’': {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Gong',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
nodeData: {
|
||||
Gong: [],
|
||||
},
|
||||
error: "The Users IDs don't match any existing user",
|
||||
},
|
||||
nock: {
|
||||
baseUrl,
|
||||
mocks: [
|
||||
{
|
||||
method: 'post',
|
||||
path: '/v2/users/extensive',
|
||||
statusCode: 404,
|
||||
requestBody: {
|
||||
filter: {
|
||||
userIds: ['234599484848423'],
|
||||
},
|
||||
},
|
||||
responseBody: {
|
||||
requestId: '26r8maav84ehguoddd7',
|
||||
errors: ['The following userIds were not found: 234599484848423'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const testData of tests) {
|
||||
testHarness.setupTest(testData, { credentials });
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,781 @@
|
||||
/* eslint-disable n8n-nodes-base/node-param-display-name-miscased */
|
||||
export const gongApiResponse = {
|
||||
// https://gong.app.gong.io/settings/api/documentation#post-/v2/calls
|
||||
postCalls: {
|
||||
requestId: '4al018gzaztcr8nbukw',
|
||||
callId: '7782342274025937895',
|
||||
},
|
||||
// https://gong.app.gong.io/settings/api/documentation#put-/v2/calls/-id-/media
|
||||
postCallsMedia: {
|
||||
requestId: '4al018gzaztcr8nbukw',
|
||||
callId: '7782342274025937895',
|
||||
url: 'https://app.gong.io/call?id=7782342274025937895',
|
||||
},
|
||||
// https://gong.app.gong.io/settings/api/documentation#post-/v2/calls/extensive
|
||||
postCallsExtensive: {
|
||||
requestId: '4al018gzaztcr8nbukw',
|
||||
records: {
|
||||
totalRecords: 263,
|
||||
currentPageSize: 100,
|
||||
currentPageNumber: 0,
|
||||
cursor: 'eyJhbGciOiJIUzI1NiJ9.eyJjYWxsSWQiM1M30.6qKwpOcvnuweTZmFRzYdtjs_YwJphJU4QIwWFM',
|
||||
},
|
||||
calls: [
|
||||
{
|
||||
metaData: {
|
||||
id: '7782342274025937895',
|
||||
url: 'https://app.gong.io/call?id=7782342274025937895',
|
||||
title: 'Example call',
|
||||
scheduled: 1518863400,
|
||||
started: 1518863400,
|
||||
duration: 460,
|
||||
primaryUserId: '234599484848423',
|
||||
direction: 'Inbound',
|
||||
system: 'Outreach',
|
||||
scope: 'Internal',
|
||||
media: 'Video',
|
||||
language: 'eng',
|
||||
workspaceId: '623457276584334',
|
||||
sdrDisposition: 'Got the gatekeeper',
|
||||
clientUniqueId: '7JEHFRGXDDZFEW2FC4U',
|
||||
customData: 'Conference Call',
|
||||
purpose: 'Demo Call',
|
||||
meetingUrl: 'https://zoom.us/j/123',
|
||||
isPrivate: false,
|
||||
calendarEventId: 'abcde@google.com',
|
||||
},
|
||||
context: [
|
||||
{
|
||||
system: 'Salesforce',
|
||||
objects: [
|
||||
{
|
||||
objectType: 'Opportunity',
|
||||
objectId: '0013601230sV7grAAC',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
value: 'Gong Inc.',
|
||||
},
|
||||
],
|
||||
timing: 'Now',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
parties: [
|
||||
{
|
||||
id: '56825452554556',
|
||||
emailAddress: 'test@test.com',
|
||||
name: 'Test User',
|
||||
title: 'Enterprise Account Executive',
|
||||
userId: '234599484848423',
|
||||
speakerId: '6432345678555530067',
|
||||
context: [
|
||||
{
|
||||
system: 'Salesforce',
|
||||
objects: [
|
||||
{
|
||||
objectType: 'Contact',
|
||||
objectId: '0013601230sV7grAAC',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
value: 'Gong Inc.',
|
||||
},
|
||||
],
|
||||
timing: 'Now',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
affiliation: 'Internal',
|
||||
phoneNumber: '+1 123-567-8989',
|
||||
methods: ['Invitee'],
|
||||
},
|
||||
],
|
||||
content: {
|
||||
structure: [
|
||||
{
|
||||
name: 'Meeting Setup',
|
||||
duration: 67,
|
||||
},
|
||||
],
|
||||
trackers: [
|
||||
{
|
||||
id: '56825452554556',
|
||||
name: 'Competitors',
|
||||
count: 7,
|
||||
type: 'KEYWORD',
|
||||
occurrences: [
|
||||
{
|
||||
startTime: 32.56,
|
||||
speakerId: '234599484848423',
|
||||
},
|
||||
],
|
||||
phrases: [
|
||||
{
|
||||
count: 5,
|
||||
occurrences: [
|
||||
{
|
||||
startTime: 32.56,
|
||||
speakerId: '234599484848423',
|
||||
},
|
||||
],
|
||||
phrase: 'Walmart',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
topics: [
|
||||
{
|
||||
name: 'Objections',
|
||||
duration: 86,
|
||||
},
|
||||
],
|
||||
pointsOfInterest: {
|
||||
actionItems: [
|
||||
{
|
||||
snippetStartTime: 26,
|
||||
snippetEndTime: 26,
|
||||
speakerID: '56825452554556',
|
||||
snippet:
|
||||
"And I'll send you an invite with a link that you can use at that time as well.",
|
||||
},
|
||||
],
|
||||
},
|
||||
brief: 'string',
|
||||
outline: [
|
||||
{
|
||||
section: 'string',
|
||||
startTime: 0.5,
|
||||
duration: 0.5,
|
||||
items: [
|
||||
{
|
||||
text: 'string',
|
||||
startTime: 0.5,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
highlights: [
|
||||
{
|
||||
title: 'string',
|
||||
items: [
|
||||
{
|
||||
text: 'string',
|
||||
startTimes: [0.5],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
callOutcome: {
|
||||
id: 'MEETING_BOOKED',
|
||||
category: 'Answered',
|
||||
name: 'Meeting booked',
|
||||
},
|
||||
keyPoints: [
|
||||
{
|
||||
text: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
interaction: {
|
||||
speakers: [
|
||||
{
|
||||
id: '56825452554556',
|
||||
userId: '234599484848423',
|
||||
talkTime: 145,
|
||||
},
|
||||
],
|
||||
interactionStats: [
|
||||
{
|
||||
name: 'Interactivity',
|
||||
value: 56,
|
||||
},
|
||||
],
|
||||
video: [
|
||||
{
|
||||
name: 'Browser',
|
||||
duration: 218,
|
||||
},
|
||||
],
|
||||
questions: {
|
||||
companyCount: 0,
|
||||
nonCompanyCount: 0,
|
||||
},
|
||||
},
|
||||
collaboration: {
|
||||
publicComments: [
|
||||
{
|
||||
id: '6843152929075440037',
|
||||
audioStartTime: 26,
|
||||
audioEndTime: 26,
|
||||
commenterUserId: '234599484848423',
|
||||
comment: 'new comment',
|
||||
posted: 1518863400,
|
||||
inReplyTo: '792390015966656336',
|
||||
duringCall: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
media: {
|
||||
audioUrl: 'http://example.com',
|
||||
videoUrl: 'http://example.com',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// https://gong.app.gong.io/settings/api/documentation#post-/v2/calls/transcript
|
||||
postCallsTranscript: {
|
||||
requestId: '4al018gzaztcr8nbukw',
|
||||
records: {
|
||||
totalRecords: 1,
|
||||
currentPageSize: 1,
|
||||
currentPageNumber: 0,
|
||||
},
|
||||
callTranscripts: [
|
||||
{
|
||||
callId: '7782342274025937895',
|
||||
transcript: [
|
||||
{
|
||||
speakerId: '6432345678555530067',
|
||||
topic: 'Objections',
|
||||
sentences: [
|
||||
{
|
||||
start: 460230,
|
||||
end: 462343,
|
||||
text: 'No wait, I think we should check that out first.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// https://gong.app.gong.io/settings/api/documentation#post-/v2/users/extensive
|
||||
postUsersExtensive: {
|
||||
requestId: '4al018gzaztcr8nbukw',
|
||||
records: {
|
||||
totalRecords: 263,
|
||||
currentPageSize: 100,
|
||||
currentPageNumber: 0,
|
||||
cursor: 'eyJhbGciOiJIUzI1NiJ9.eyJjYWxsSWQiM1M30.6qKwpOcvnuweTZmFRzYdtjs_YwJphJU4QIwWFM',
|
||||
},
|
||||
users: [
|
||||
{
|
||||
id: '234599484848423',
|
||||
emailAddress: 'test@test.com',
|
||||
created: '2018-02-17T02:30:00-08:00',
|
||||
active: true,
|
||||
emailAliases: ['testAlias@test.com'],
|
||||
trustedEmailAddress: 'test@test.com',
|
||||
firstName: 'Jon',
|
||||
lastName: 'Snow',
|
||||
title: 'Enterprise Account Executive',
|
||||
phoneNumber: '+1 123-567-8989',
|
||||
extension: '123',
|
||||
personalMeetingUrls: ['https://zoom.us/j/123'],
|
||||
settings: {
|
||||
webConferencesRecorded: true,
|
||||
preventWebConferenceRecording: false,
|
||||
telephonyCallsImported: false,
|
||||
emailsImported: true,
|
||||
preventEmailImport: false,
|
||||
nonRecordedMeetingsImported: true,
|
||||
gongConnectEnabled: true,
|
||||
},
|
||||
managerId: '563515258458745',
|
||||
meetingConsentPageUrl:
|
||||
'https://join.gong.io/my-company/jon.snow?tkn=MoNpS9tMNt8BK7EZxQpSJl',
|
||||
spokenLanguages: [
|
||||
{
|
||||
language: 'es-ES',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const gongNodeResponse = {
|
||||
getCall: [
|
||||
{
|
||||
json: {
|
||||
metaData: {
|
||||
id: '7782342274025937895',
|
||||
url: 'https://app.gong.io/call?id=7782342274025937895',
|
||||
title: 'Example call',
|
||||
scheduled: 1518863400,
|
||||
started: 1518863400,
|
||||
duration: 460,
|
||||
primaryUserId: '234599484848423',
|
||||
direction: 'Inbound',
|
||||
system: 'Outreach',
|
||||
scope: 'Internal',
|
||||
media: 'Video',
|
||||
language: 'eng',
|
||||
workspaceId: '623457276584334',
|
||||
sdrDisposition: 'Got the gatekeeper',
|
||||
clientUniqueId: '7JEHFRGXDDZFEW2FC4U',
|
||||
customData: 'Conference Call',
|
||||
purpose: 'Demo Call',
|
||||
meetingUrl: 'https://zoom.us/j/123',
|
||||
isPrivate: false,
|
||||
calendarEventId: 'abcde@google.com',
|
||||
},
|
||||
context: [
|
||||
{
|
||||
system: 'Salesforce',
|
||||
objects: [
|
||||
{
|
||||
objectType: 'Opportunity',
|
||||
objectId: '0013601230sV7grAAC',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
value: 'Gong Inc.',
|
||||
},
|
||||
],
|
||||
timing: 'Now',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
parties: [
|
||||
{
|
||||
id: '56825452554556',
|
||||
emailAddress: 'test@test.com',
|
||||
name: 'Test User',
|
||||
title: 'Enterprise Account Executive',
|
||||
userId: '234599484848423',
|
||||
speakerId: '6432345678555530067',
|
||||
context: [
|
||||
{
|
||||
system: 'Salesforce',
|
||||
objects: [
|
||||
{
|
||||
objectType: 'Contact',
|
||||
objectId: '0013601230sV7grAAC',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
value: 'Gong Inc.',
|
||||
},
|
||||
],
|
||||
timing: 'Now',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
affiliation: 'Internal',
|
||||
phoneNumber: '+1 123-567-8989',
|
||||
methods: ['Invitee'],
|
||||
},
|
||||
],
|
||||
content: {
|
||||
structure: [
|
||||
{
|
||||
name: 'Meeting Setup',
|
||||
duration: 67,
|
||||
},
|
||||
],
|
||||
trackers: [
|
||||
{
|
||||
id: '56825452554556',
|
||||
name: 'Competitors',
|
||||
count: 7,
|
||||
type: 'KEYWORD',
|
||||
occurrences: [
|
||||
{
|
||||
startTime: 32.56,
|
||||
speakerId: '234599484848423',
|
||||
},
|
||||
],
|
||||
phrases: [
|
||||
{
|
||||
count: 5,
|
||||
occurrences: [
|
||||
{
|
||||
startTime: 32.56,
|
||||
speakerId: '234599484848423',
|
||||
},
|
||||
],
|
||||
phrase: 'Walmart',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
topics: [
|
||||
{
|
||||
name: 'Objections',
|
||||
duration: 86,
|
||||
},
|
||||
],
|
||||
pointsOfInterest: {
|
||||
actionItems: [
|
||||
{
|
||||
snippetStartTime: 26,
|
||||
snippetEndTime: 26,
|
||||
speakerID: '56825452554556',
|
||||
snippet:
|
||||
"And I'll send you an invite with a link that you can use at that time as well.",
|
||||
},
|
||||
],
|
||||
},
|
||||
brief: 'string',
|
||||
outline: [
|
||||
{
|
||||
section: 'string',
|
||||
startTime: 0.5,
|
||||
duration: 0.5,
|
||||
items: [
|
||||
{
|
||||
text: 'string',
|
||||
startTime: 0.5,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
highlights: [
|
||||
{
|
||||
title: 'string',
|
||||
items: [
|
||||
{
|
||||
text: 'string',
|
||||
startTimes: [0.5],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
callOutcome: {
|
||||
id: 'MEETING_BOOKED',
|
||||
category: 'Answered',
|
||||
name: 'Meeting booked',
|
||||
},
|
||||
keyPoints: [
|
||||
{
|
||||
text: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
interaction: {
|
||||
speakers: [
|
||||
{
|
||||
id: '56825452554556',
|
||||
userId: '234599484848423',
|
||||
talkTime: 145,
|
||||
},
|
||||
],
|
||||
interactionStats: [
|
||||
{
|
||||
name: 'Interactivity',
|
||||
value: 56,
|
||||
},
|
||||
],
|
||||
video: [
|
||||
{
|
||||
name: 'Browser',
|
||||
duration: 218,
|
||||
},
|
||||
],
|
||||
questions: {
|
||||
companyCount: 0,
|
||||
nonCompanyCount: 0,
|
||||
},
|
||||
},
|
||||
collaboration: {
|
||||
publicComments: [
|
||||
{
|
||||
id: '6843152929075440037',
|
||||
audioStartTime: 26,
|
||||
audioEndTime: 26,
|
||||
commenterUserId: '234599484848423',
|
||||
comment: 'new comment',
|
||||
posted: 1518863400,
|
||||
inReplyTo: '792390015966656336',
|
||||
duringCall: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
media: {
|
||||
audioUrl: 'http://example.com',
|
||||
videoUrl: 'http://example.com',
|
||||
},
|
||||
transcript: [
|
||||
{
|
||||
speakerId: '6432345678555530067',
|
||||
topic: 'Objections',
|
||||
sentences: [
|
||||
{
|
||||
start: 460230,
|
||||
end: 462343,
|
||||
text: 'No wait, I think we should check that out first.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
getAllCall: [
|
||||
{
|
||||
json: {
|
||||
id: '7782342274025937895',
|
||||
url: 'https://app.gong.io/call?id=7782342274025937895',
|
||||
title: 'Example call',
|
||||
scheduled: 1518863400,
|
||||
started: 1518863400,
|
||||
duration: 460,
|
||||
primaryUserId: '234599484848423',
|
||||
direction: 'Inbound',
|
||||
system: 'Outreach',
|
||||
scope: 'Internal',
|
||||
media: 'Video',
|
||||
language: 'eng',
|
||||
workspaceId: '623457276584334',
|
||||
sdrDisposition: 'Got the gatekeeper',
|
||||
clientUniqueId: '7JEHFRGXDDZFEW2FC4U',
|
||||
customData: 'Conference Call',
|
||||
purpose: 'Demo Call',
|
||||
meetingUrl: 'https://zoom.us/j/123',
|
||||
isPrivate: false,
|
||||
calendarEventId: 'abcde@google.com',
|
||||
content: {
|
||||
topics: [
|
||||
{
|
||||
name: 'Objections',
|
||||
duration: 86,
|
||||
},
|
||||
],
|
||||
},
|
||||
parties: [
|
||||
{
|
||||
id: '56825452554556',
|
||||
emailAddress: 'test@test.com',
|
||||
name: 'Test User',
|
||||
title: 'Enterprise Account Executive',
|
||||
userId: '234599484848423',
|
||||
speakerId: '6432345678555530067',
|
||||
context: [
|
||||
{
|
||||
system: 'Salesforce',
|
||||
objects: [
|
||||
{
|
||||
objectType: 'Contact',
|
||||
objectId: '0013601230sV7grAAC',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
value: 'Gong Inc.',
|
||||
},
|
||||
],
|
||||
timing: 'Now',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
affiliation: 'Internal',
|
||||
phoneNumber: '+1 123-567-8989',
|
||||
methods: ['Invitee'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
json: {
|
||||
id: '7782342274025937896',
|
||||
url: 'https://app.gong.io/call?id=7782342274025937896',
|
||||
title: 'Example call',
|
||||
scheduled: 1518863400,
|
||||
started: 1518863400,
|
||||
duration: 460,
|
||||
primaryUserId: '234599484848423',
|
||||
direction: 'Inbound',
|
||||
system: 'Outreach',
|
||||
scope: 'Internal',
|
||||
media: 'Video',
|
||||
language: 'eng',
|
||||
workspaceId: '623457276584334',
|
||||
sdrDisposition: 'Got the gatekeeper',
|
||||
clientUniqueId: '7JEHFRGXDDZFEW2FC4U',
|
||||
customData: 'Conference Call',
|
||||
purpose: 'Demo Call',
|
||||
meetingUrl: 'https://zoom.us/j/123',
|
||||
isPrivate: false,
|
||||
calendarEventId: 'abcde@google.com',
|
||||
content: {
|
||||
topics: [
|
||||
{
|
||||
name: 'Objections',
|
||||
duration: 86,
|
||||
},
|
||||
],
|
||||
},
|
||||
parties: [
|
||||
{
|
||||
id: '56825452554556',
|
||||
emailAddress: 'test@test.com',
|
||||
name: 'Test User',
|
||||
title: 'Enterprise Account Executive',
|
||||
userId: '234599484848423',
|
||||
speakerId: '6432345678555530067',
|
||||
context: [
|
||||
{
|
||||
system: 'Salesforce',
|
||||
objects: [
|
||||
{
|
||||
objectType: 'Contact',
|
||||
objectId: '0013601230sV7grAAC',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
value: 'Gong Inc.',
|
||||
},
|
||||
],
|
||||
timing: 'Now',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
affiliation: 'Internal',
|
||||
phoneNumber: '+1 123-567-8989',
|
||||
methods: ['Invitee'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
getAllCallNoOptions: [
|
||||
{
|
||||
json: {
|
||||
id: '7782342274025937895',
|
||||
url: 'https://app.gong.io/call?id=7782342274025937895',
|
||||
title: 'Example call',
|
||||
scheduled: 1518863400,
|
||||
started: 1518863400,
|
||||
duration: 460,
|
||||
primaryUserId: '234599484848423',
|
||||
direction: 'Inbound',
|
||||
system: 'Outreach',
|
||||
scope: 'Internal',
|
||||
media: 'Video',
|
||||
language: 'eng',
|
||||
workspaceId: '623457276584334',
|
||||
sdrDisposition: 'Got the gatekeeper',
|
||||
clientUniqueId: '7JEHFRGXDDZFEW2FC4U',
|
||||
customData: 'Conference Call',
|
||||
purpose: 'Demo Call',
|
||||
meetingUrl: 'https://zoom.us/j/123',
|
||||
isPrivate: false,
|
||||
calendarEventId: 'abcde@google.com',
|
||||
},
|
||||
},
|
||||
],
|
||||
getUser: [
|
||||
{
|
||||
json: {
|
||||
id: '234599484848423',
|
||||
emailAddress: 'test@test.com',
|
||||
created: '2018-02-17T02:30:00-08:00',
|
||||
active: true,
|
||||
emailAliases: ['testAlias@test.com'],
|
||||
trustedEmailAddress: 'test@test.com',
|
||||
firstName: 'Jon',
|
||||
lastName: 'Snow',
|
||||
title: 'Enterprise Account Executive',
|
||||
phoneNumber: '+1 123-567-8989',
|
||||
extension: '123',
|
||||
personalMeetingUrls: ['https://zoom.us/j/123'],
|
||||
settings: {
|
||||
webConferencesRecorded: true,
|
||||
preventWebConferenceRecording: false,
|
||||
telephonyCallsImported: false,
|
||||
emailsImported: true,
|
||||
preventEmailImport: false,
|
||||
nonRecordedMeetingsImported: true,
|
||||
gongConnectEnabled: true,
|
||||
},
|
||||
managerId: '563515258458745',
|
||||
meetingConsentPageUrl:
|
||||
'https://join.gong.io/my-company/jon.snow?tkn=MoNpS9tMNt8BK7EZxQpSJl',
|
||||
spokenLanguages: [
|
||||
{
|
||||
language: 'es-ES',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
getAllUser: [
|
||||
{
|
||||
json: {
|
||||
id: '234599484848423',
|
||||
emailAddress: 'test@test.com',
|
||||
created: '2018-02-17T02:30:00-08:00',
|
||||
active: true,
|
||||
emailAliases: ['testAlias@test.com'],
|
||||
trustedEmailAddress: 'test@test.com',
|
||||
firstName: 'Jon',
|
||||
lastName: 'Snow',
|
||||
title: 'Enterprise Account Executive',
|
||||
phoneNumber: '+1 123-567-8989',
|
||||
extension: '123',
|
||||
personalMeetingUrls: ['https://zoom.us/j/123'],
|
||||
settings: {
|
||||
webConferencesRecorded: true,
|
||||
preventWebConferenceRecording: false,
|
||||
telephonyCallsImported: false,
|
||||
emailsImported: true,
|
||||
preventEmailImport: false,
|
||||
nonRecordedMeetingsImported: true,
|
||||
gongConnectEnabled: true,
|
||||
},
|
||||
managerId: '563515258458745',
|
||||
meetingConsentPageUrl:
|
||||
'https://join.gong.io/my-company/jon.snow?tkn=MoNpS9tMNt8BK7EZxQpSJl',
|
||||
spokenLanguages: [
|
||||
{
|
||||
language: 'es-ES',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
json: {
|
||||
id: '234599484848424',
|
||||
emailAddress: 'test@test.com',
|
||||
created: '2018-02-17T02:30:00-08:00',
|
||||
active: true,
|
||||
emailAliases: ['testAlias@test.com'],
|
||||
trustedEmailAddress: 'test@test.com',
|
||||
firstName: 'Jon',
|
||||
lastName: 'Snow',
|
||||
title: 'Enterprise Account Executive',
|
||||
phoneNumber: '+1 123-567-8989',
|
||||
extension: '123',
|
||||
personalMeetingUrls: ['https://zoom.us/j/123'],
|
||||
settings: {
|
||||
webConferencesRecorded: true,
|
||||
preventWebConferenceRecording: false,
|
||||
telephonyCallsImported: false,
|
||||
emailsImported: true,
|
||||
preventEmailImport: false,
|
||||
nonRecordedMeetingsImported: true,
|
||||
gongConnectEnabled: true,
|
||||
},
|
||||
managerId: '563515258458745',
|
||||
meetingConsentPageUrl:
|
||||
'https://join.gong.io/my-company/jon.snow?tkn=MoNpS9tMNt8BK7EZxQpSJl',
|
||||
spokenLanguages: [
|
||||
{
|
||||
language: 'es-ES',
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user